From 26e212ed8a45e9c372cbea6dd858259c16729637 Mon Sep 17 00:00:00 2001 From: "lichenyang.anarkh" Date: Mon, 20 Jul 2026 10:44:02 +0800 Subject: [PATCH 1/2] perf: slim map feed setData payload and exclude local tool artifacts from package Moved raw map posts from Page.data to controller-only this.posts, removed unrendered NearbyPreview/open-count work, and skipped no-selection region-end rebuilds. A 100-post synthetic benchmark reduced average refresh setData payload from 183,539 to 119,848 bytes (-34.7%). project.config.json now excludes .gstack/ and style-comparison.html from the mini program package, avoiding 106,313 B (18.1%) of local non-app input. style-comparison.html is added to .gitignore as a local tool artifact. Static checks and readiness all pass; DevTools/real-device performance remains unverified (service port 9420 blocked). --- .gitignore | 1 + LAUNCH_TODO.md | 320 +++++++++++++++++++++++++++++++++++++ harness/check-map-feed.mjs | 54 ++++--- harness/claude-progress.md | 16 ++ harness/feature_list.json | 5 +- pages/map/map.js | 35 ++-- project.config.json | 10 +- utils/post-presenter.js | 42 ----- 8 files changed, 393 insertions(+), 90 deletions(-) create mode 100644 LAUNCH_TODO.md diff --git a/.gitignore b/.gitignore index ab2d499..64d35b1 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ project.private.config.json .gstack/ .understand-anything/ skills-lock.json +style-comparison.html # Runtime logs log/ diff --git a/LAUNCH_TODO.md b/LAUNCH_TODO.md new file mode 100644 index 0000000..55f7ced --- /dev/null +++ b/LAUNCH_TODO.md @@ -0,0 +1,320 @@ +# Street Tasks 上线 TODO LIST + +> 目标:将"街区任务"微信小程序从当前开发状态推进到正式上线 +> 生成日期:2026-07-15 +> 预计周期:2-4 周(取决于审核和资质进度) + +--- + +## 一、微信平台资质与配置(优先级:P0,阻塞项) + +### 1.1 小程序注册与认证 +- [ ] 注册微信小程序账号(如尚未注册):https://mp.weixin.qq.com/ +- [ ] 完成小程序主体认证(个人/企业,企业认证可使用更多接口) +- [ ] 获取真实 AppID(替换 `project.config.json` 中的 `touristappid`) +- [ ] 将真实 AppID 写入 `project.private.config.json`(已在 .gitignore 中,不会泄露到 GitHub) +- [ ] 设置小程序名称、头像、简介、服务类目 + - 名称建议:街区任务 / 附近任务 + - 简介:确认、更新、关闭身边短时信息 + - 服务类目需选择包含"工具"或"生活服务"相关类目 +- [ ] 设置小程序隐私协议(位置信息属于敏感个人信息,需要单独同意) + +### 1.2 域名与接口配置 +- [ ] 在微信公众平台配置服务器域名(如使用云端外的接口) +- [ ] 确认 CloudBase 环境已绑定当前小程序 AppID +- [ ] 配置业务域名(如未来需要 H5 跳转) + +### 1.3 类目与权限申请 +- [ ] 确认 `scope.userLocation` 权限声明已在 `app.json` 中配置(已配置 ✓) +- [ ] 确认 `requiredPrivateInfos` 包含 `getLocation`(已配置 ✓) +- [ ] 如使用订阅消息,申请并配置消息模板 +- [ ] 如使用激励视频广告,开通流量主(需累计 UV ≥ 1,000) + +--- + +## 二、CloudBase 云端部署(优先级:P0,阻塞项) + +### 2.1 环境与数据库 +- [ ] 确认 CloudBase 环境 `cloud1-d4ga42hl16934fbdf` 已创建且可用 +- [ ] 创建数据库集合(6 个): + - [ ] `posts` — 任务帖子 + - [ ] `post_reactions` — 信任动作(确认/过时/举报) + - [ ] `post_comments` — 评论 + - [ ] `feedback_items` — 用户反馈 + - [ ] `viral_attribution_events` — 传播归因事件 + - [ ] `admins` — 管理员列表 +- [ ] 为每个集合配置安全规则(最小权限原则): + - `posts`:所有用户可读,仅创建者/管理员可写 + - `post_reactions`:登录用户可创建自己的反应,可读 + - `post_comments`:登录用户可创建,所有可读 + - `feedback_items`:登录用户可创建自己的,仅管理员可读 + - `viral_attribution_events`:仅服务端可写 + - `admins`:仅服务端可读 + +### 2.2 云函数部署 +- [ ] 部署 `posts` 云函数(处理帖子 CRUD、评论、反馈、信任动作、传播归因、图片上传) +- [ ] 部署 `getMyRole` 云函数(管理员角色查询) +- [ ] 为两个云函数配置超时时间(建议 10-20 秒) +- [ ] 确认云函数运行环境 Node.js 版本与本地一致 +- [ ] 安装云函数依赖:`cloudfunctions/posts` 下执行 `npm install`(wx-server-sdk) +- [ ] 安装云函数依赖:`cloudfunctions/getMyRole` 下执行 `npm install` + +### 2.3 云存储 +- [ ] 开通 CloudBase 云存储(图片发布需要 `cloud://` fileID) +- [ ] 创建存储目录:`posts/images/` +- [ ] 配置存储安全规则:用户可上传,所有用户可读 +- [ ] 设置存储容量上限和清理策略(过期任务的图片定期清理) + +### 2.4 云端验证 +- [ ] 在 CloudBase 控制台手动创建一条测试 post,确认数据库读写正常 +- [ ] 在小程序端调用 `wx.cloud.callFunction` 测试 `posts` 云函数 listPosts +- [ ] 测试 `getMyRole` 云函数返回正常(非管理员返回 guest) +- [ ] 测试图片上传到云存储并生成 `cloud://` fileID + +--- + +## 三、代码修复与优化(优先级:P0-P1) + +### 3.1 已知 Bug 修复 +- [ ] 修复 `WAServiceMainContext timeout` 偶发问题(当前不阻断渲染但需跟踪) + - 确认地图首屏已改为本地优先(已改 ✓) + - 确认定位、云函数列表和地图 region 读取不阻塞启动(已改 ✓) + - 在真机上复现并确认是否仍出现 +- [ ] 修复本地存储与云端数据同步问题 + - 确认详情页本地 fallback 正常(已改 ✓) + - 确认本地发布的任务在云端也可读 +- [ ] 清理 `utils/post-presenter.js` 中已移除的无用代码(当前 diff 显示删除了 42 行) + +### 3.2 发布前必须修复的问题 +- [ ] 评论治理:补充评论举报、隐藏、删除能力(当前 TODOS.md 已记录,上线前至少需要举报+管理员隐藏) +- [ ] 内容安全:接入微信内容安全 API(`msgSecCheck`、`imgSecCheck`) + - 发布任务时校验标题和正文 + - 上传图片时校验图片 + - 评论内容校验 +- [ ] 隐私合规:位置信息单独同意弹窗(PIPL 要求) + - 首次使用定位前展示单独的隐私说明 + - 用户拒绝后仍可使用默认中心浏览 +- [ ] 用户协议与隐私政策页面 + - 创建用户协议页面 + - 创建隐私政策页面 + - 在登录/发布前展示并获得同意 + +### 3.3 性能优化 +- [ ] 地图高密度场景:当前 100 marker 上限,上线前需确认种子区域不会超过 + - 如预期会超过,需实现聚合标记(TODOS.md 已记录) + - 或实现按视野范围查询+分页加载 +- [ ] 图片压缩与上传:确认大图压缩、上传超时、失败重试逻辑稳定 +- [ ] 云函数冷启动:考虑云函数定时触发或预留实例减少冷启动时间 + +--- + +## 四、测试与验证(优先级:P0) + +### 4.1 WeChat DevTools 全量验证 +- [ ] 地图首页: + - [ ] 首次加载渲染正常,无白屏 + - [ ] 允许定位后地图移动到当前位置 + - [ ] 拒绝定位后使用默认中心(北京) + - [ ] 点击 marker 进入详情页 + - [ ] 点击"列表"打开任务抽屉 + - [ ] 点击"找一找"跳转到附近随机任务 + - [ ] 分类筛选正常 +- [ ] 发布流程: + - [ ] 未登录点击发布显示登录引导 + - [ ] 登录后每个分类(打卡/失物招领/地点动态/求助问答)均可发布 + - [ ] 位置确认、有效期选择、自定义时间正常 + - [ ] 图片选择、压缩、上传正常(最多 4 张,每张 <1.5MB) + - [ ] 发布成功后地图/详情页可见新任务 + - [ ] 必填项校验、字数限制、图片数量限制正常 +- [ ] 详情页: + - [ ] 任务内容、图片、发布者信息、地点、距离、时间展示正常 + - [ ] 评论列表展示正常 + - [ ] 悬浮评论按钮→登录引导→评论弹窗→发布评论 + - [ ] 确认/过时/举报按钮计数正常 + - [ ] 同一用户不能重复同一动作 + - [ ] 发布者可关闭任务 + - [ ] resolved/expired 任务评论只读 +- [ ] 管理后台: + - [ ] 普通用户无管理员能力 + - [ ] 管理员可查看风险筛选、搜索、隐藏/关闭任务 + - [ ] 反馈列表查看 + - [ ] admins 集合缺失时不崩溃 +- [ ] 个人中心: + - [ ] 登录/头像昵称设置 + - [ ] 我的发布列表和统计 + - [ ] 动态记录 + - [ ] 反馈提交 +- [ ] 分享传播: + - [ ] 发布后分享引导 + - [ ] 详情页分享卡片 + - [ ] 分享接收者引导 + - [ ] 朋友圈分享(低风险 active 任务) + +### 4.2 真机测试(至少 2 台设备) +- [ ] iPhone(最新 iOS + 微信最新版)全流程测试 +- [ ] Android(主流机型 + 微信最新版)全流程测试 +- [ ] 弱网/无网环境测试(本地 fallback 是否正常) +- [ ] 定位授权拒绝/允许/仅一次三种场景 +- [ ] 后台/前台切换后状态保持 +- [ ] 小程序冷启动/热启动表现 +- [ ] 不同屏幕尺寸和安全区域适配(iPhone 灵动岛、Android 挖孔屏) +- [ ] 图片拍摄/相册选择权限处理 + +### 4.3 自动化检查 +- [ ] `npm run check:json` — JSON 配置语法检查 +- [ ] `node harness/check-harness.mjs` — Harness 状态检查 +- [ ] `bash harness/init.sh` — 基线验证 +- [ ] `node --check` 所有 JS 文件语法检查 +- [ ] `git diff --check` — 空白和冲突标记检查 + +--- + +## 五、内容准备(优先级:P1) + +### 5.1 种子内容 +- [ ] 准备 20-50 条种子任务数据(覆盖 4 个分类) + - 失物招领:5-10 条(我丢了/我捡到各半) + - 地点动态:5-10 条(施工、占道、临时调整等) + - 求助问答:5-10 条 + - 打卡:5-10 条(适合拍照的地点、活动等) +- [ ] 种子任务坐标设置在种子区域(选择 1-2 个高密度社区/校园/园区) +- [ ] 种子任务使用真实的地点名称和合理的描述 +- [ ] 为部分种子任务添加测试图片 + +### 5.2 种子区域选择 +- [ ] 选择 1-2 个种子区域(建议:大学校园或年轻社区) + - 标准:3-5km 半径内 3-8 万居民 + - 优先:有微信群/公众号等触达渠道的区域 +- [ ] 将种子任务坐标集中在种子区域 +- [ ] 准备种子区域的线下推广素材(海报、二维码、文案) + +### 5.3 运营准备 +- [ ] 准备发布教程(如何发布第一条任务) +- [ ] 准备新用户引导文案 +- [ ] 准备管理员操作手册 +- [ ] 准备常见问题 FAQ + +--- + +## 六、合规与法律(优先级:P0,阻塞项) + +### 6.1 隐私合规 +- [ ] 编写并上线《用户协议》 +- [ ] 编写并上线《隐私政策》 + - 明确说明收集的信息:位置、昵称、头像、发布内容 + - 明确说明位置信息的用途和存储方式 + - 明确说明第三方服务(CloudBase、微信广告) + - 提供用户注销/删除数据的方式 +- [ ] 位置信息单独同意弹窗(PIPL 第 28 条要求) +- [ ] 首次启动展示隐私协议弹窗,用户同意后才初始化 +- [ ] 不收集不必要的敏感信息(不收集精确通讯录、不收集身份证号等) + +### 6.2 内容安全 +- [ ] 接入微信 `msgSecCheck` 文本内容安全 API +- [ ] 接入微信 `imgSecCheck` 图片内容安全 API +- [ ] 设置关键词黑名单(政治、色情、暴力、广告等) +- [ ] 建立用户举报→管理员审核→处理的流程 +- [ ] 准备内容审核 SOP 和应急响应方案 + +### 6.3 广告合规(如上线广告) +- [ ] 广告内容标注"广告"标识 +- [ ] 广告档案留存 3 年(《互联网广告管理办法》要求) +- [ ] 指定广告合规负责人 +- [ ] 不展示医疗、药品、保健品等需审批的广告 + +### 6.4 ICP 备案 +- [ ] 确认小程序是否需要 ICP 备案(如涉及用户发布内容,通常需要) +- [ ] 如需备案,准备材料并提交 +- [ ] 备案期间可使用"开发版/体验版"进行内部测试 + +--- + +## 七、提交审核与发布(优先级:P0) + +### 7.1 提交审核前检查清单 +- [ ] 真实 AppID 已配置(非 `touristappid`) +- [ ] 所有页面无空白页、无 JS 报错 +- [ ] 无测试数据、无 Mock 数据暴露(或明确标注为测试) +- [ ] 无敏感信息泄露(API Key、Secret、Token 等) + - 运行 secret scan:`rg --no-ignore -n -i "(api[_-]?key|secret|token|password|...)" .` +- [ ] 隐私协议和用户协议已上线且可访问 +- [ ] 内容安全 API 已接入 +- [ ] 定位权限有明确的使用说明和拒绝后的降级方案 +- [ ] 小程序名称、头像、简介与实际功能一致 +- [ ] 服务类目与实际功能匹配 +- [ ] 截图准备:4-5 张功能截图(地图、发布、详情、管理、个人中心) + +### 7.2 上传代码 +- [ ] 在 WeChat DevTools 中点击"上传" +- [ ] 填写版本号(如 1.0.0)和项目备注 +- [ ] 确认上传成功,版本出现在微信公众平台"开发管理"中 + +### 7.3 提交审核 +- [ ] 在微信公众平台选择上传的版本,点击"提交审核" +- [ ] 填写功能页面(首页地图、发布、详情等) +- [ ] 填写测试账号(如需登录功能) +- [ ] 填写审核说明(说明小程序核心功能和使用方式) +- [ ] 提交后等待审核(通常 1-7 天) + +### 7.4 审核常见问题与应对 +- [ ] 如被拒:查看拒绝原因,针对性修改后重新提交 +- [ ] 常见拒绝原因: + - 类目不符 → 调整服务类目 + - 功能无法体验 → 提供测试账号或开放体验 + - 隐私问题 → 补充隐私协议和授权流程 + - 内容问题 → 清理测试数据,接入内容安全 + +### 7.5 发布上线 +- [ ] 审核通过后,点击"发布" +- [ ] 选择发布方式:全量发布 / 灰度发布(建议先灰度 10%) +- [ ] 发布后立即在真机上验证核心流程 +- [ ] 监控云函数日志和错误率 + +--- + +## 八、上线后运营(优先级:P2) + +### 8.1 首日监控 +- [ ] 监控 PV/UV、新增用户、留存率 +- [ ] 监控云函数调用量和错误率 +- [ ] 监控数据库读写量 +- [ ] 监控存储使用量 +- [ ] 监控用户反馈和举报 + +### 8.2 种子区域运营 +- [ ] 在种子区域微信群/公众号发布推广 +- [ ] 线下张贴二维码海报 +- [ ] 每日发布 3-5 条种子任务保持活跃度 +- [ ] 回复用户反馈和评论 + +### 8.3 数据指标目标 +- [ ] 首日 UV ≥ 100 +- [ ] 7 日留存 ≥ 20% +- [ ] 日新增任务 ≥ 10 条 +- [ ] 信任动作使用率 ≥ 20% +- [ ] 举报率 < 5% + +### 8.4 迭代计划 +- [ ] 收集用户反馈,整理 Top 10 问题 +- [ ] 规划 v1.1 版本迭代(参考 PRODUCT_STRATEGY.md) +- [ ] 持续优化性能和体验 + +--- + +## 优先级总结 + +| 优先级 | 类别 | 预计耗时 | 阻塞上线? | +|--------|------|----------|-----------| +| **P0** | 微信平台资质 | 1-3 天 | ✅ 是 | +| **P0** | CloudBase 部署 | 1-2 天 | ✅ 是 | +| **P0** | 代码修复(Bug+合规) | 3-5 天 | ✅ 是 | +| **P0** | 测试验证 | 2-3 天 | ✅ 是 | +| **P0** | 合规法律 | 1-2 天 | ✅ 是 | +| **P0** | 提交审核发布 | 1-7 天 | ✅ 是 | +| **P1** | 内容准备 | 1-2 天 | 否(可并行) | +| **P1** | 性能优化 | 2-3 天 | 否(影响体验) | +| **P2** | 上线后运营 | 持续 | 否 | + +**关键路径:** 微信平台资质 → CloudBase 部署 → 代码修复 → 测试验证 → 合规 → 提交审核 → 发布上线 + +**最快路径(如资质已有):** CloudBase 部署(1天) + 代码修复(3天) + 测试(2天) + 合规(1天) + 审核(1-7天) = **8-14 天** diff --git a/harness/check-map-feed.mjs b/harness/check-map-feed.mjs index ecb4ff9..f3032fe 100644 --- a/harness/check-map-feed.mjs +++ b/harness/check-map-feed.mjs @@ -4,12 +4,16 @@ import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { markerFromPost } from '../utils/geo.js'; -import { buildNearbyPreviewPosts } from '../utils/post-presenter.js'; const rootDir = dirname(dirname(fileURLToPath(import.meta.url))); const mapJs = readFileSync(join(rootDir, 'pages/map/map.js'), 'utf8'); const mapWxml = readFileSync(join(rootDir, 'pages/map/map.wxml'), 'utf8'); const mapWxss = readFileSync(join(rootDir, 'pages/map/map.wxss'), 'utf8'); +const mapDataStart = mapJs.indexOf(' data: {'); +const mapOnLoadStart = mapJs.indexOf('\n\n onLoad('); +assert.notEqual(mapDataStart, -1, 'Map page should define initial data.'); +assert.notEqual(mapOnLoadStart, -1, 'Map page should define onLoad after initial data.'); +const mapDataSource = mapJs.slice(mapDataStart, mapOnLoadStart); const discoverNearbyStart = mapJs.indexOf('async discoverNearby()'); const focusPostStart = mapJs.indexOf('\n\n focusPost(', discoverNearbyStart); assert.notEqual(discoverNearbyStart, -1, 'Map page should define discoverNearby.'); @@ -80,20 +84,7 @@ const posts = [ } ]; -const previewPosts = buildNearbyPreviewPosts(posts, 'near_active', 3); - -assert.deepEqual( - previewPosts.map((post) => post.id), - ['near_active', 'stale_middle', 'far_active'] -); -assert.equal(previewPosts[0].isSelected, true); -assert.equal(previewPosts[0].browseRank, 1); -assert.equal(previewPosts[0].browseHint, '最近'); -assert.equal(previewPosts[1].browseHint, '待复核'); -assert.equal(previewPosts[1].previewTitle, '中距离待复核任务标题非常非常...'); -assert(previewPosts[1].previewTitle.length <= 18); - -const selectedMarker = markerFromPost(previewPosts[0]); +const selectedMarker = markerFromPost({ ...posts[2], isSelected: true }); assert.equal(selectedMarker.callout.bgColor, '#1F6658'); assert.equal(selectedMarker.callout.color, '#FFFFFF'); assert.equal(selectedMarker.callout.borderWidth, 2); @@ -158,27 +149,42 @@ assert.match( assert.doesNotMatch( mapJs, /buildPosts\(center\)[\s\S]*?\.map\(\(post\) => decorateMapPost\(post\)\)/, - 'buildPosts should not pre-decorate every post before filtering and preview building.' + 'buildPosts should not pre-decorate every post before filtering.' ); assert.match( mapJs, - /const viewportPosts = \[\];[\s\S]*?const categoryCounts = \{\};[\s\S]*?let openPostCount = 0;[\s\S]*?posts\.forEach\(\(post\) => \{[\s\S]*?if \(isOpenPost\(post\)\) \{[\s\S]*?openPostCount \+= 1;[\s\S]*?\}[\s\S]*?if \(!isPostInRegion\(post, mapRegion\)\) \{[\s\S]*?return;[\s\S]*?\}[\s\S]*?viewportPosts\.push\(post\);[\s\S]*?categoryCounts\[post\.category\]/, - 'Map filtering should collect viewport posts, category counts, and open-post count in a single pass.' + /const viewportPosts = \[\];[\s\S]*?const categoryCounts = \{\};[\s\S]*?posts\.forEach\(\(post\) => \{[\s\S]*?if \(!isPostInRegion\(post, mapRegion\)\) \{[\s\S]*?return;[\s\S]*?\}[\s\S]*?viewportPosts\.push\(post\);[\s\S]*?categoryCounts\[post\.category\]/, + 'Map filtering should collect viewport posts and category counts in a single pass.' +); +assert.doesNotMatch( + mapJs, + /\b(?:nearbyPreviewPosts|openPostCount)\b/, + 'Map filtering should not compute or transfer presentation fields removed from the current UI.' ); assert.doesNotMatch( + mapDataSource, + /^\s*posts:\s*\[\],?$/m, + 'Raw posts are controller state and should not live in Page.data.' +); +assert.match( mapJs, - /openPostCount:\s*posts\.filter\(isOpenPost\)\.length/, - 'Map filtering should not rescan all posts just to compute openPostCount.' + /onLoad\(options = \{\}\) \{[\s\S]*?this\.posts = \[\];[\s\S]*?this\.refresh\(\);/, + 'Map startup should keep raw posts on the Page instance instead of the view-model bridge.' ); assert.match( mapJs, - /nearbyPreviewPosts:\s*buildNearbyPreviewPosts\(baseVisiblePosts,\s*selectedPostId\)/, - 'NearbyPreview should be built from raw filtered posts so map cards are not decorated twice.' + /applyPostFilters\(posts, activeCategory, mapRegion, options = \{\}\) \{[\s\S]*?this\.posts = posts;/, + 'Map refreshes should retain raw posts as controller-only instance state.' ); assert.doesNotMatch( mapJs, - /nearbyPreviewPosts:\s*buildNearbyPreviewPosts\(visiblePosts,\s*selectedPostId\)/, - 'NearbyPreview should not re-decorate visiblePosts that were already prepared for the list.' + /this\.data\.posts|nextData\.posts/, + 'Map interactions should read raw posts from instance state and never send them through setData.' +); +assert.match( + mapJs, + /onRegionChange\(event\) \{[\s\S]*?if \(!this\.data\.selectedPost && !this\.pendingSelectedPost\) \{[\s\S]*?return;[\s\S]*?\}[\s\S]*?this\.applyPostFilters\(this\.posts, this\.data\.activeCategory, null, \{[\s\S]*?clearSelection: true/, + 'Ordinary map pans should not rebuild and resend an unchanged feed just to clear an empty selection.' ); assert.match( mapJs, diff --git a/harness/claude-progress.md b/harness/claude-progress.md index 49992dc..4fba25b 100644 --- a/harness/claude-progress.md +++ b/harness/claude-progress.md @@ -1404,3 +1404,19 @@ - 已记录证据:readiness 输出 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`;WCC/WCSC 退出 0;发布页滚动截图和 accessibility tree 均确认单 tabBar;地图页截图确认普通 tabBar 仍可见 - 已知风险或未解决问题:DevTools 仍有既有 `WAServiceMainContext Error: timeout`,Service Port `9420` 仍 disabled/connect_refused;未在真机做快速连续滑动和安全区复测,因此不标记发布功能整体 passing - 下一步最佳动作:在真机连续快速上下滑发布页,并切换四个 tab,确认没有重复 dock、点击失效或安全区抖动 + +### Session 102MapBridgeAndPackagePerformance + +- 日期:2026-07-14 +- 分支:`main` +- 工作区:`/Users/bytedance/git/x` +- 本轮目标:按用户要求做一轮可量化、低风险的性能优化,优先收敛当前 `map-feed-001` 首屏/刷新链路,并避免 DevTools 打包本地工具工件 +- 根因与修复:地图 `applyPostFilters` 每次刷新都会把 JS 内部原始 `posts` 放入 `Page.data/setData`,同时继续计算和传输已经没有 WXML 消费者的 `nearbyPreviewPosts` 与 `openPostCount`;现将原始帖子保留为 Page 实例字段 `this.posts`,删除已失去 UI 消费者的预览 helper/字段/计算,并让没有选中任务的普通地图拖动直接跳过 region-end 整批重建。`project.config.json` 同时排除本地 `.gstack/` 和未被应用引用的 `style-comparison.html`,只改变打包边界,不删除用户文件 +- TDD 证据:先更新 `harness/check-map-feed.mjs`,旧实现按预期失败并报告 `Map filtering should not compute or transfer presentation fields removed from the current UI.`;完成控制器状态迁移后新增普通地图拖动不得空重建的断言,旧 region-end 实现再次按预期失败并报告 `Ordinary map pans should not rebuild and resend an unchanged feed just to clear an empty selection.`;实现早返回后同一门禁通过 +- 性能证据:使用同一组 100 条合成任务、300 次 `applyPostFilters` 刷新,桌面 Node 微基准中的平均 `setData` JSON 负载从 `183,539 B` 降到 `119,848 B`,减少 `63,691 B / 34.7%`;同一脚本的 JS 处理加 JSON 序列化均值从 `0.541 ms` 降到 `0.380 ms`,减少约 `29.8%`。无选中任务时的普通 region-end 现在为 `0` 次 feed `setData`。这些数字是可重复的桌面合成基准,不代表真机帧率或原生 map bridge 耗时 +- 包体证据:按当前 `packOptions.ignore` 与独立 `cloudfunctionRoot` 估算,补充排除规则前当前树为 `588,892 B / 91 files`,排除 `.gstack/` 与 `style-comparison.html` 后为 `482,579 B / 86 files`,避免 `106,313 B / 18.1%` 本地非应用输入;没有采用收益较小且会延迟常用详情首开的 subpackage 改造,也没有改动视觉图片资产 +- 运行过的验证:`bash harness/init.sh` 基线;`node --no-warnings harness/check-map-feed.mjs` 失败/通过 TDD;`node --check pages/map/map.js`;`node --check utils/post-presenter.js`;`node scripts/check-json.mjs`;`node --no-warnings scripts/check-performance-guards.mjs`;`node --no-warnings scripts/check-map-list-resilience.mjs`;`npm run check`;`git diff --check`;前后 100 条/300 次 map payload 微基准;两条 BSD/macOS-safe `find | stat | awk` 包体边界估算;最终 `bash harness/init.sh` +- 已记录证据:`Map feed checks passed.`、`Performance guard checks passed.`、`Map list resilience checks passed.`、`Checked 11 JSON files.`、`Harness OK: 6 features checked.`、`DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`;`git diff --check` 无输出 +- 严格复审:按 `FP1` 到 `FP5` 将完整 diff、上下文和验证结果并发提交给三个 `super-relay-review` reviewer;第一轮一票批准、两票因 reviewer 输出格式不合法而按 blocked 处理,未把失败票算作通过;随后完整重跑三票,第二轮三位 reviewer 均对五个功能点给出 `APPROVE`,无 actionable finding。共同保留的限制是尚无 DevTools/真机性能轨迹和真实上传包详情 +- 已知风险或未解决问题:本轮没有得到真实 WeChat DevTools 性能面板、上传包详情或真机帧率/手势体感证据;readiness 仍确认 service port `9420` disabled/connect_refused,因此保留 `map-feed-001=in_progress`,不把桌面基准写作 UI/真机通过。未跟踪的 `style-comparison.html` 仍原样保留,只是不再进入小程序包 +- 下一步最佳动作:启用 DevTools Service Port 后,在 100 条附近任务样本下录制地图首屏、连续拖动、选中/清除 marker、打开列表和分类切换的性能轨迹,并用 DevTools 上传详情确认实际包体未包含 `.gstack/` 与 `style-comparison.html` diff --git a/harness/feature_list.json b/harness/feature_list.json index d6e50be..33aa8de 100644 --- a/harness/feature_list.json +++ b/harness/feature_list.json @@ -1,6 +1,6 @@ { "project": "Street Tasks / 街区任务", - "last_updated": "2026-07-10", + "last_updated": "2026-07-14", "rules": { "single_active_feature": true, "passing_requires_evidence": true, @@ -175,7 +175,8 @@ "2026-07-09: Reviewed the current local main dirty visual refresh and fixed map cover-view overlay regressions by keeping selected task card/list/tool/diagnostic cover-view styles hard-coded instead of CSS-variable based; scripts/check-map-list-resilience.mjs now guards this.", "2026-07-10: WeChat DevTools Stable v2.01.2510290 direct UI smoke rendered map tiles, the collapsed list entry, locate/discover controls, selected-task cover-view card, expanded task drawer, and custom tabBar; the existing WAServiceMainContext timeout remained and no real-device claim was made.", "2026-07-10: Fixed the screenshot-reported blank strip above the home map and map bleed around the tabBar bottom edge. The map viewport is now explicitly pinned to top:0 and ends above the 116rpx tabBar plus safe area, while the fixed map-page background stays white behind device corners; DevTools confirmed both collapsed and expanded list states.", - "2026-07-10: Replaced the custom tabBar cover-view/cover-image tree with normal view/image nodes after the map stopped above the dock. DevTools confirmed the map tiles and the single custom tabBar remain visible together." + "2026-07-10: Replaced the custom tabBar cover-view/cover-image tree with normal view/image nodes after the map stopped above the dock. DevTools confirmed the map tiles and the single custom tabBar remain visible together.", + "2026-07-14: Moved raw map posts from Page.data to controller-only this.posts, removed unrendered NearbyPreview/open-count work, and skipped no-selection region-end rebuilds. A 100-post synthetic benchmark reduced average refresh setData payload from 183,539 to 119,848 bytes (-34.7%); static/readiness checks passed, while DevTools/real-device performance remains unverified." ], "notes": "已有实现未等于 harness passing;需要手动小程序验证证据。2026-05-18 UI 刷新采用 TDesign 式设计 token 和原生小程序控件落地,未引入 tdesign-miniprogram npm 依赖,避免在当前无小程序 npm 构建链路下增加编译风险。底部 tabBar 已由悬浮胶囊改为贴底不透明导航栏。2026-05-19 发布页和地图抽屉已落地 design-ui-designer 组件化优化。2026-05-21 白屏排查未发现 WXML/WXSS/JS 语法错误,已移除 tabBar 中新增的空 cover-view 指示条以降低渲染兼容风险;同日继续排查确认 DevTools hot reload/getAppConfig 缓存会触发 subPackages 内部错误,清编译缓存并终止重编译后地图恢复,同时加入页面诊断和云读取 fallback;随后针对 `Error: timeout` 将地图首屏改为本地优先,定位、云函数列表和地图 region 读取都不再阻塞启动;详情页已补上本地 fallback,避免本地列表任务因云端不存在而显示“任务不存在”。信息列表已改为铺满抽屉、标题后置标签、底部轻量统计。A 组地图迭代新增 NearbyPreview 首屏附近优先预览和选中态联动,并收敛长标题风险;2026-06-15 用户手测暴露普通 view/button 覆盖原生 map 不可靠,因此折叠态任务入口、定位和找一找都保持为 cover-view/cover-image;经过多轮用户截图反馈,最终布局按偏好收敛为右上角白色“列表 N”单行居中按钮、右下角定位/找一找工具行、选中任务卡上移避让工具行,打开列表时仍缩小 native map 到 38vh 让普通任务卡抽屉位于 map 下方。2026-06-14 已新增地图列表静态韧性检查并接入 readiness/preflight,且手测准备 helper 会显式运行并提示该 preflight;这只降低结构、样式和执行漏跑风险,不代表 DevTools 或真机视觉验收通过。S 组补充了地图列表真实视觉 smoke 的模板 journey 和检查清单,T 组把该 journey 变成 manual evidence 必备项,U 组提供 blocked local draft helper,V 组把 blocked draft 与 sanitized summary 生成串联,W 组为 blocked JSON/summary 状态一致性增加自动 guard,X 组进一步守住 summary 与 JSON 的 branch/commit/blocker/followUp 同源完整性,Y 组把增强 guard 接入 wrapper 默认生成链路,Z 组又把生成后手工编辑必须复跑 guard 的命令直接打印到 wrapper 成功输出,AA 组新增评审前一键 preflight 扫描 ignored local blocked summaries 并逐对复跑 guard,AB 组把该 preflight 接入 harness/init.sh 和 DevTools readiness 默认检查,AC 组把 JSON/harness/readiness 门禁暴露为 npm 级 `check` 脚本,AD 组新增最小 GitHub Actions workflow 在 push/pull_request 上运行该检查,AE 组补充 required-check runbook 和稳定 job display name 以便远端 Actions/分支保护后续验证,AF 组补充 `inspect:devtools-port` 与 `check:devtools-smoke` 手动入口,明确当前 9420 service-port blocker 可复查但不进入默认 CI/check,AG 组补充 `inspect:devtools-recovery` dry-run 入口,明确恢复动作前后的 before/actions/after/next-step 证据且不执行 quit/open 副作用,AH 组补充 ignored local recovery dry-run report 生成与 guard,降低交接草稿被改写成恢复成功或 UI passed 的风险,AI 组再补充手动 preflight 扫描所有 ignored local recovery reports 并逐份复跑 guard,降低多份本地草稿交接前漏检风险;这些仍只是在证据结构上预留、守护、演练阻塞记录并提升评审可读性。viral 侧 AD/AE/AF 又把七条旅程证据包、JSON/Markdown summary 同源和附件 manifest 同源/脱敏 guard 串起来,能让真实手测后的截图/录屏/payload/readback 附件更安全地被评审引用;但这些 guard 仍不产生真实系统分享、朋友圈、payload、CloudBase readback、窄屏或真机 passed evidence。真实 safe area、地图抽屉、原生地图层、列表滚动、带图/无图和详情点击仍需 WeChat DevTools 或真机观察。仍需在 WeChat DevTools/真机中确认 safe area、键盘、地图抽屉、定位授权分支和多分类详情链路。" }, diff --git a/pages/map/map.js b/pages/map/map.js index c265212..c0a8e2b 100644 --- a/pages/map/map.js +++ b/pages/map/map.js @@ -1,7 +1,6 @@ import { listPosts } from '../../utils/store.js'; import { markerFromPost } from '../../utils/geo.js'; import { - buildNearbyPreviewPosts, decorateMapPost, isOpenPost } from '../../utils/post-presenter.js'; @@ -48,12 +47,9 @@ Page({ center: app.globalData.center, categoryFilters: categoryOptions.map((item) => ({ ...item, count: 0 })), activeCategory: 'all', - openPostCount: 0, mapRegion: null, selectedPost: null, - posts: [], visiblePosts: [], - nearbyPreviewPosts: [], markers: [], showList: false, showLocation: false, @@ -66,6 +62,8 @@ Page({ onLoad(options = {}) { addDiagnostic('map.onLoad', 'entered'); this.mapPageActive = true; + // Raw posts are controller state; keeping them off Page.data avoids a duplicate setData bridge payload. + this.posts = []; const focusPostId = safeDecode(options.focusPostId || options.postId || options.id); this.pendingLaunchFocus = focusPostId ? { id: focusPostId, showList: shouldOpenList(options.showList) } @@ -233,13 +231,10 @@ Page({ if (!this.mapPageActive) { return; } + this.posts = posts; const viewportPosts = []; const categoryCounts = {}; - let openPostCount = 0; posts.forEach((post) => { - if (isOpenPost(post)) { - openPostCount += 1; - } if (!isPostInRegion(post, mapRegion)) { return; } @@ -266,25 +261,19 @@ Page({ const selectedPost = selectedPostId ? visiblePosts.find((post) => post.id === selectedPostId) || decorateMapPost(selectedPostCandidate, selectedPostId) : null; - const nextData = { + this.setData({ visiblePosts, - nearbyPreviewPosts: buildNearbyPreviewPosts(baseVisiblePosts, selectedPostId), categoryFilters, activeCategory, - openPostCount, mapRegion, selectedPost, markers: visiblePosts.map(markerFromPost) - }; - if (posts !== this.data.posts) { - nextData.posts = posts; - } - this.setData(nextData); + }); }, changeCategory(event) { const category = event.currentTarget.dataset.category || 'all'; - this.applyPostFilters(this.data.posts, category, this.data.mapRegion); + this.applyPostFilters(this.posts, category, this.data.mapRegion); }, setBootStatus(status) { @@ -367,7 +356,11 @@ Page({ if (event.detail && event.detail.causedBy === 'update') { return; } - this.applyPostFilters(this.data.posts, this.data.activeCategory, null, { + // Panning only changes view data when it needs to clear an existing selection. + if (!this.data.selectedPost && !this.pendingSelectedPost) { + return; + } + this.applyPostFilters(this.posts, this.data.activeCategory, null, { clearSelection: true }); }, @@ -388,7 +381,7 @@ Page({ const selectedPost = this.data.visiblePosts.find((post) => post.id === marker.postId); this.pendingSelectedPost = selectedPost || null; this.setData({ showList: false }, () => { - this.applyPostFilters(this.data.posts, this.data.activeCategory, this.data.mapRegion); + this.applyPostFilters(this.posts, this.data.activeCategory, this.data.mapRegion); }); } }, @@ -398,7 +391,7 @@ Page({ }, async discoveryCandidates(activeCategory) { - const posts = this.data.posts.length ? this.data.posts : await this.buildPosts(this.data.center); + const posts = this.posts.length ? this.posts : await this.buildPosts(this.data.center); const scopedPosts = activeCategory === 'all' ? posts : posts.filter((post) => post.category === activeCategory); @@ -484,7 +477,7 @@ Page({ clearSelectedPost() { this.setData({ selectedPost: null }, () => { - this.applyPostFilters(this.data.posts, this.data.activeCategory, this.data.mapRegion, { + this.applyPostFilters(this.posts, this.data.activeCategory, this.data.mapRegion, { clearSelection: true }); }); diff --git a/project.config.json b/project.config.json index a272efa..cd08c79 100644 --- a/project.config.json +++ b/project.config.json @@ -48,6 +48,10 @@ "type": "folder", "value": ".github" }, + { + "type": "folder", + "value": ".gstack" + }, { "type": "folder", "value": ".understand-anything" @@ -63,10 +67,14 @@ { "type": "folder", "value": "log" + }, + { + "type": "file", + "value": "style-comparison.html" } ], "include": [] }, "editorSetting": {}, "cloudfunctionTemplateRoot": "cloudfunctionTemplate/" -} \ No newline at end of file +} diff --git a/utils/post-presenter.js b/utils/post-presenter.js index cda35a3..f557bfc 100644 --- a/utils/post-presenter.js +++ b/utils/post-presenter.js @@ -32,28 +32,6 @@ function safeDistance(value) { return Number.isFinite(distance) ? distance : Number.POSITIVE_INFINITY; } -function safeCount(value) { - return Math.max(0, Number(value) || 0); -} - -function browseHintForPost(post, index) { - if (index === 0) { - return '最近'; - } - if (post.status === 'stale' || safeCount(post.staleCount) >= 3) { - return '待复核'; - } - if (safeCount(post.confirmations) > 0) { - return '已确认'; - } - return '附近'; -} - -function previewTitle(title, maxLength = 14) { - const value = String(title || '').trim(); - return value.length > maxLength ? `${value.slice(0, maxLength)}...` : value; -} - export function decoratePost(post) { const imageUrls = Array.isArray(post.imageUrls) ? post.imageUrls : []; return { @@ -79,26 +57,6 @@ export function decorateMapPost(post, selectedPostId = '') { }; } -export function buildNearbyPreviewPosts(posts, selectedPostId = '', limit = 3) { - return posts - .filter(isOpenPost) - .slice() - .sort((a, b) => { - const distanceDiff = safeDistance(a.distance) - safeDistance(b.distance); - if (distanceDiff !== 0) { - return distanceDiff; - } - return (Number(b.createdAt) || 0) - (Number(a.createdAt) || 0); - }) - .slice(0, limit) - .map((post, index) => ({ - ...decorateMapPost(post, selectedPostId), - browseRank: index + 1, - browseHint: browseHintForPost(post, index), - previewTitle: previewTitle(post.title) - })); -} - export function buildActivities(posts, reactions) { const postById = {}; posts.forEach((post) => { From fb9e50077da47751a4498ec11656ef0a5a3565ca Mon Sep 17 00:00:00 2001 From: anarkh lee <710809606@qq.com> Date: Tue, 28 Jul 2026 21:28:22 +0800 Subject: [PATCH 2/2] chore: retire harness apparatus, promote knowledge base This project no longer needs the AI/dev-collaboration harness. Migrate the durable product knowledge it held into a first-class knowledge/ base and delete the rest. - Add knowledge/ (14 articles): product, journeys, architecture, data model, trust/safety, privacy/security, sharing/attribution, design, verification, deployment, release readiness, plus an index and maintenance guide. - Fill two gaps from the old harness: WeChat timeline single-page platform constraints and the attribution user_id_hash (irreversible, dedup-only). - Delete harness/ (~135 files) and 24 harness-only evidence scripts that are now dead code. - Rebuild `npm run check` into three real stages: JSON syntax, knowledge-base structure, and 25 product-behavior checks (new scripts/check-readiness.mjs replaces the deleted devtools-readiness runner). Repoint check-content-safety and related scripts off harness onto the product code they verify. - Reconcile AGENTS.md, PROJECT_SUMMARY.md, LAUNCH_TODO.md, and project.config.json to reference knowledge/ and `npm run check`. - Refresh .gitignore: drop stale harness ignores, ignore the mcp-gateway/ tool artifact. Verified: `npm run check` passes (13 JSON files, 14 articles, 25 product checks) and `git diff --check` is clean. Co-Authored-By: Claude --- .gitignore | 8 +- AGENTS.md | 61 +- LAUNCH_TODO.md | 79 +- PROJECT_SUMMARY.md | 14 +- README.md | 4 +- app.js | 2 +- app.json | 4 +- cloudfunctions/posts/config.json | 8 + cloudfunctions/posts/index.js | 411 ++++- harness/README.md | 28 - harness/admin-design-brief.md | 56 - harness/admin-product-brief.md | 38 - harness/candidate-publish-trust-report.md | 48 - harness/check-harness.mjs | 75 - harness/ci-readiness-gate-checklist.md | 75 - harness/ci-readiness-gate-product-brief.md | 31 - .../ci-required-check-runbook-checklist.md | 32 - ...ci-required-check-runbook-product-brief.md | 31 - harness/ci-required-check-runbook.md | 61 - harness/claude-progress.md | 1422 ----------------- harness/clean-state-checklist.md | 10 - .../devtools-port-deep-forensics-checklist.md | 384 ----- ...tools-port-deep-forensics-product-brief.md | 168 -- harness/devtools-port-forensics-checklist.md | 195 --- .../devtools-port-forensics-product-brief.md | 130 -- harness/devtools-readiness-checklist.md | 337 ---- harness/devtools-readiness-product-brief.md | 166 -- .../devtools-recovery-command-checklist.md | 95 -- .../devtools-recovery-command-design-note.md | 108 -- ...devtools-recovery-command-product-brief.md | 46 - harness/devtools-recovery-report-checklist.md | 137 -- .../devtools-recovery-report-design-note.md | 143 -- ...ols-recovery-report-preflight-checklist.md | 126 -- ...s-recovery-report-preflight-design-note.md | 166 -- ...recovery-report-preflight-product-brief.md | 46 - .../devtools-recovery-report-product-brief.md | 44 - ...service-port-config-forensics-checklist.md | 148 -- ...ice-port-config-forensics-product-brief.md | 101 -- ...-service-port-ui-confirmation-checklist.md | 205 --- ...vice-port-ui-confirmation-product-brief.md | 169 -- .../devtools-service-recovery-checklist.md | 239 --- ...devtools-service-recovery-product-brief.md | 102 -- harness/devtools-smoke-checklist.md | 202 --- harness/devtools-smoke-command-checklist.md | 90 -- harness/devtools-smoke-command-design-note.md | 59 - .../devtools-smoke-command-product-brief.md | 28 - harness/devtools-smoke-product-brief.md | 95 -- harness/evaluator-rubric.md | 24 - harness/evidence-hygiene-product-brief.md | 96 -- harness/evidence-redaction-checklist.md | 68 - harness/feature_list.json | 427 ----- harness/hardening-design-checklist.md | 114 -- harness/hardening-product-brief.md | 68 - harness/init.sh | 41 - harness/manual-evidence-product-brief.md | 176 -- .../manual-preflight-alignment-checklist.md | 94 -- ...anual-preflight-alignment-product-brief.md | 56 - harness/manual-runbook-checklist.md | 181 --- harness/manual-runbook-product-brief.md | 140 -- harness/manual-test-results.example.json | 190 --- .../map-list-blocked-evidence-checklist.md | 396 ----- ...map-list-blocked-evidence-product-brief.md | 84 - harness/map-list-blocked-summary-checklist.md | 136 -- .../map-list-blocked-summary-product-brief.md | 78 - harness/map-list-evidence-gate-checklist.md | 370 ----- .../map-list-evidence-gate-product-brief.md | 63 - harness/map-list-preflight-checklist.md | 71 - harness/map-list-preflight-product-brief.md | 53 - harness/map-list-resilience-checklist.md | 193 --- harness/map-list-resilience-product-brief.md | 77 - harness/map-list-summary-guard-checklist.md | 186 --- .../map-list-summary-guard-product-brief.md | 81 - .../map-list-summary-integrity-checklist.md | 260 --- ...ap-list-summary-integrity-product-brief.md | 82 - ...p-list-summary-postedit-guard-checklist.md | 127 -- ...st-summary-postedit-guard-product-brief.md | 26 - .../map-list-summary-preflight-checklist.md | 190 --- ...ap-list-summary-preflight-product-brief.md | 29 - ...t-summary-readiness-preflight-checklist.md | 209 --- ...mmary-readiness-preflight-product-brief.md | 37 - ...-list-summary-wrapper-guarded-checklist.md | 162 -- ...t-summary-wrapper-guarded-product-brief.md | 69 - harness/map-list-visual-evidence-checklist.md | 216 --- .../map-list-visual-evidence-product-brief.md | 70 - harness/package-readiness-gate-checklist.md | 36 - .../package-readiness-gate-product-brief.md | 34 - harness/profile-design-brief.md | 47 - harness/profile-product-brief.md | 36 - harness/quality-document.md | 43 - harness/sanitized-summary-checklist.md | 66 - harness/sanitized-summary-product-brief.md | 59 - harness/session-handoff.md | 31 - harness/viral-attribution-events-checklist.md | 85 - .../viral-attribution-events-product-brief.md | 138 -- ...iral-blocked-evidence-capture-checklist.md | 276 ---- ...-blocked-evidence-capture-product-brief.md | 114 -- harness/viral-candidate-design-checklist.md | 41 - harness/viral-candidate-product-brief.md | 32 - .../viral-comment-relay-design-checklist.md | 40 - harness/viral-comment-relay-product-brief.md | 23 - .../viral-comment-source-design-checklist.md | 38 - harness/viral-comment-source-product-brief.md | 22 - .../viral-confirm-relay-design-checklist.md | 29 - harness/viral-confirm-relay-product-brief.md | 24 - .../viral-devtools-journey-run-checklist.md | 229 --- ...iral-devtools-journey-run-product-brief.md | 153 -- ...viral-journey-evidence-design-checklist.md | 45 - .../viral-journey-evidence-product-brief.md | 32 - ...viral-journey-manual-evidence-checklist.md | 47 - ...l-journey-manual-evidence-product-brief.md | 40 - .../viral-journey-manual-results.example.json | 226 --- .../viral-loop-candidate-design-checklist.md | 29 - harness/viral-loop-candidate-product-brief.md | 23 - ...iral-manual-artifact-manifest-checklist.md | 173 -- ...-manual-artifact-manifest-product-brief.md | 147 -- ...anual-journey-evidence-packet-checklist.md | 267 ---- ...l-journey-evidence-packet-product-brief.md | 216 --- ...iral-manual-summary-integrity-checklist.md | 150 -- ...-manual-summary-integrity-product-brief.md | 170 -- harness/viral-publish-design-checklist.md | 39 - harness/viral-publish-product-brief.md | 35 - .../viral-real-evidence-recovery-checklist.md | 216 --- ...al-real-evidence-recovery-product-brief.md | 154 -- .../viral-receiver-action-design-checklist.md | 56 - .../viral-receiver-action-product-brief.md | 32 - ...receiver-action-source-design-checklist.md | 99 -- ...al-receiver-action-source-product-brief.md | 127 -- ...al-receiver-conversion-design-checklist.md | 41 - ...viral-receiver-conversion-product-brief.md | 31 - harness/viral-receiver-design-checklist.md | 41 - harness/viral-receiver-product-brief.md | 33 - ...l-relay-channel-picker-design-checklist.md | 79 - ...iral-relay-channel-picker-product-brief.md | 129 -- harness/viral-share-design-checklist.md | 34 - harness/viral-share-product-brief.md | 34 - .../viral-share-reason-design-checklist.md | 72 - harness/viral-share-reason-product-brief.md | 104 -- .../viral-targeted-relay-design-checklist.md | 109 -- harness/viral-targeted-relay-product-brief.md | 126 -- harness/viral-timeline-evidence-checklist.md | 96 -- .../viral-timeline-evidence-product-brief.md | 102 -- harness/viral-timeline-landing-checklist.md | 86 - .../viral-timeline-landing-product-brief.md | 157 -- .../viral-timeline-share-design-checklist.md | 94 -- harness/viral-timeline-share-product-brief.md | 135 -- knowledge/AGENTS.md | 35 + knowledge/architecture.md | 92 ++ knowledge/data-model.md | 71 + knowledge/deployment-operations.md | 76 + knowledge/design-ui.md | 57 + knowledge/development-verification.md | 71 + knowledge/index.md | 26 + knowledge/log.md | 9 + knowledge/privacy-security.md | 64 + knowledge/product-overview.md | 58 + knowledge/release-readiness.md | 63 + knowledge/sharing-attribution.md | 107 ++ knowledge/trust-safety.md | 70 + knowledge/user-journeys.md | 71 + package.json | 20 +- pages/admin/admin.js | 93 +- pages/admin/admin.wxml | 47 + pages/admin/admin.wxss | 39 + pages/agreement/agreement.js | 28 + pages/agreement/agreement.json | 4 + pages/agreement/agreement.wxml | 50 + pages/agreement/agreement.wxss | 77 + pages/detail/detail.js | 108 +- pages/detail/detail.wxml | 29 + pages/detail/detail.wxss | 58 + pages/feedback/feedback.js | 42 +- pages/feedback/feedback.wxml | 6 +- pages/map/map.js | 15 +- pages/me/me-state.js | 2 +- pages/me/me.js | 15 +- pages/me/me.wxml | 16 + pages/my-posts/my-posts.js | 4 +- pages/privacy/privacy.js | 74 + pages/privacy/privacy.json | 4 + pages/privacy/privacy.wxml | 80 + pages/privacy/privacy.wxss | 96 ++ pages/publish/publish.js | 93 +- project.config.json | 4 - ...capture-viral-journey-blocked-evidence.mjs | 416 ----- scripts/check-admin-auth-errors.mjs | 13 +- scripts/check-comment-moderation.mjs | 88 + scripts/check-content-safety.mjs | 343 ++++ scripts/check-devtools-port-forensics.mjs | 19 - scripts/check-devtools-readiness.mjs | 380 ----- ...eck-devtools-recovery-report-preflight.mjs | 51 - scripts/check-devtools-recovery-report.mjs | 130 -- scripts/check-devtools-ui-confirmation.mjs | 176 -- scripts/check-evidence-hygiene.mjs | 184 --- scripts/check-knowledge.mjs | 64 + scripts/check-manual-evidence.mjs | 265 --- {harness => scripts}/check-map-feed.mjs | 1 + ...eck-map-list-blocked-summary-preflight.mjs | 84 - scripts/check-map-list-blocked-summary.mjs | 447 ------ scripts/check-performance-guards.mjs | 16 + scripts/check-privacy-compliance.mjs | 142 ++ scripts/check-readiness.mjs | 57 + scripts/check-share-receiver-action.mjs | 2 +- {harness => scripts}/check-trust-insight.mjs | 1 + scripts/check-viral-attribution.mjs | 59 + scripts/check-viral-candidate.mjs | 11 - .../check-viral-journey-evidence-packet.mjs | 260 --- scripts/check-viral-journey-evidence.mjs | 596 ------- .../check-viral-journey-manual-evidence.mjs | 766 --------- ...ral-manual-artifact-manifest-preflight.mjs | 86 - .../check-viral-manual-artifact-manifest.mjs | 886 ---------- ...ral-manual-summary-integrity-preflight.mjs | 86 - .../check-viral-manual-summary-integrity.mjs | 672 -------- scripts/create-manual-summary.mjs | 464 ------ scripts/prepare-devtools-recovery-report.mjs | 176 -- .../prepare-devtools-ui-confirmation-run.mjs | 451 ------ scripts/prepare-manual-test-run.mjs | 172 -- scripts/prepare-map-list-blocked-evidence.mjs | 210 --- scripts/prepare-map-list-blocked-summary.mjs | 139 -- .../prepare-viral-journey-devtools-run.mjs | 300 ---- .../prepare-viral-journey-evidence-packet.mjs | 391 ----- .../prepare-viral-journey-manual-evidence.mjs | 241 --- utils/auth.js | 15 +- utils/config.js | 1 + utils/feedback.js | 86 +- utils/privacy.js | 161 ++ utils/store.js | 283 +++- utils/viral-attribution.js | 25 +- 227 files changed, 3588 insertions(+), 24291 deletions(-) create mode 100644 cloudfunctions/posts/config.json delete mode 100644 harness/README.md delete mode 100644 harness/admin-design-brief.md delete mode 100644 harness/admin-product-brief.md delete mode 100644 harness/candidate-publish-trust-report.md delete mode 100644 harness/check-harness.mjs delete mode 100644 harness/ci-readiness-gate-checklist.md delete mode 100644 harness/ci-readiness-gate-product-brief.md delete mode 100644 harness/ci-required-check-runbook-checklist.md delete mode 100644 harness/ci-required-check-runbook-product-brief.md delete mode 100644 harness/ci-required-check-runbook.md delete mode 100644 harness/claude-progress.md delete mode 100644 harness/clean-state-checklist.md delete mode 100644 harness/devtools-port-deep-forensics-checklist.md delete mode 100644 harness/devtools-port-deep-forensics-product-brief.md delete mode 100644 harness/devtools-port-forensics-checklist.md delete mode 100644 harness/devtools-port-forensics-product-brief.md delete mode 100644 harness/devtools-readiness-checklist.md delete mode 100644 harness/devtools-readiness-product-brief.md delete mode 100644 harness/devtools-recovery-command-checklist.md delete mode 100644 harness/devtools-recovery-command-design-note.md delete mode 100644 harness/devtools-recovery-command-product-brief.md delete mode 100644 harness/devtools-recovery-report-checklist.md delete mode 100644 harness/devtools-recovery-report-design-note.md delete mode 100644 harness/devtools-recovery-report-preflight-checklist.md delete mode 100644 harness/devtools-recovery-report-preflight-design-note.md delete mode 100644 harness/devtools-recovery-report-preflight-product-brief.md delete mode 100644 harness/devtools-recovery-report-product-brief.md delete mode 100644 harness/devtools-service-port-config-forensics-checklist.md delete mode 100644 harness/devtools-service-port-config-forensics-product-brief.md delete mode 100644 harness/devtools-service-port-ui-confirmation-checklist.md delete mode 100644 harness/devtools-service-port-ui-confirmation-product-brief.md delete mode 100644 harness/devtools-service-recovery-checklist.md delete mode 100644 harness/devtools-service-recovery-product-brief.md delete mode 100644 harness/devtools-smoke-checklist.md delete mode 100644 harness/devtools-smoke-command-checklist.md delete mode 100644 harness/devtools-smoke-command-design-note.md delete mode 100644 harness/devtools-smoke-command-product-brief.md delete mode 100644 harness/devtools-smoke-product-brief.md delete mode 100644 harness/evaluator-rubric.md delete mode 100644 harness/evidence-hygiene-product-brief.md delete mode 100644 harness/evidence-redaction-checklist.md delete mode 100644 harness/feature_list.json delete mode 100644 harness/hardening-design-checklist.md delete mode 100644 harness/hardening-product-brief.md delete mode 100755 harness/init.sh delete mode 100644 harness/manual-evidence-product-brief.md delete mode 100644 harness/manual-preflight-alignment-checklist.md delete mode 100644 harness/manual-preflight-alignment-product-brief.md delete mode 100644 harness/manual-runbook-checklist.md delete mode 100644 harness/manual-runbook-product-brief.md delete mode 100644 harness/manual-test-results.example.json delete mode 100644 harness/map-list-blocked-evidence-checklist.md delete mode 100644 harness/map-list-blocked-evidence-product-brief.md delete mode 100644 harness/map-list-blocked-summary-checklist.md delete mode 100644 harness/map-list-blocked-summary-product-brief.md delete mode 100644 harness/map-list-evidence-gate-checklist.md delete mode 100644 harness/map-list-evidence-gate-product-brief.md delete mode 100644 harness/map-list-preflight-checklist.md delete mode 100644 harness/map-list-preflight-product-brief.md delete mode 100644 harness/map-list-resilience-checklist.md delete mode 100644 harness/map-list-resilience-product-brief.md delete mode 100644 harness/map-list-summary-guard-checklist.md delete mode 100644 harness/map-list-summary-guard-product-brief.md delete mode 100644 harness/map-list-summary-integrity-checklist.md delete mode 100644 harness/map-list-summary-integrity-product-brief.md delete mode 100644 harness/map-list-summary-postedit-guard-checklist.md delete mode 100644 harness/map-list-summary-postedit-guard-product-brief.md delete mode 100644 harness/map-list-summary-preflight-checklist.md delete mode 100644 harness/map-list-summary-preflight-product-brief.md delete mode 100644 harness/map-list-summary-readiness-preflight-checklist.md delete mode 100644 harness/map-list-summary-readiness-preflight-product-brief.md delete mode 100644 harness/map-list-summary-wrapper-guarded-checklist.md delete mode 100644 harness/map-list-summary-wrapper-guarded-product-brief.md delete mode 100644 harness/map-list-visual-evidence-checklist.md delete mode 100644 harness/map-list-visual-evidence-product-brief.md delete mode 100644 harness/package-readiness-gate-checklist.md delete mode 100644 harness/package-readiness-gate-product-brief.md delete mode 100644 harness/profile-design-brief.md delete mode 100644 harness/profile-product-brief.md delete mode 100644 harness/quality-document.md delete mode 100644 harness/sanitized-summary-checklist.md delete mode 100644 harness/sanitized-summary-product-brief.md delete mode 100644 harness/session-handoff.md delete mode 100644 harness/viral-attribution-events-checklist.md delete mode 100644 harness/viral-attribution-events-product-brief.md delete mode 100644 harness/viral-blocked-evidence-capture-checklist.md delete mode 100644 harness/viral-blocked-evidence-capture-product-brief.md delete mode 100644 harness/viral-candidate-design-checklist.md delete mode 100644 harness/viral-candidate-product-brief.md delete mode 100644 harness/viral-comment-relay-design-checklist.md delete mode 100644 harness/viral-comment-relay-product-brief.md delete mode 100644 harness/viral-comment-source-design-checklist.md delete mode 100644 harness/viral-comment-source-product-brief.md delete mode 100644 harness/viral-confirm-relay-design-checklist.md delete mode 100644 harness/viral-confirm-relay-product-brief.md delete mode 100644 harness/viral-devtools-journey-run-checklist.md delete mode 100644 harness/viral-devtools-journey-run-product-brief.md delete mode 100644 harness/viral-journey-evidence-design-checklist.md delete mode 100644 harness/viral-journey-evidence-product-brief.md delete mode 100644 harness/viral-journey-manual-evidence-checklist.md delete mode 100644 harness/viral-journey-manual-evidence-product-brief.md delete mode 100644 harness/viral-journey-manual-results.example.json delete mode 100644 harness/viral-loop-candidate-design-checklist.md delete mode 100644 harness/viral-loop-candidate-product-brief.md delete mode 100644 harness/viral-manual-artifact-manifest-checklist.md delete mode 100644 harness/viral-manual-artifact-manifest-product-brief.md delete mode 100644 harness/viral-manual-journey-evidence-packet-checklist.md delete mode 100644 harness/viral-manual-journey-evidence-packet-product-brief.md delete mode 100644 harness/viral-manual-summary-integrity-checklist.md delete mode 100644 harness/viral-manual-summary-integrity-product-brief.md delete mode 100644 harness/viral-publish-design-checklist.md delete mode 100644 harness/viral-publish-product-brief.md delete mode 100644 harness/viral-real-evidence-recovery-checklist.md delete mode 100644 harness/viral-real-evidence-recovery-product-brief.md delete mode 100644 harness/viral-receiver-action-design-checklist.md delete mode 100644 harness/viral-receiver-action-product-brief.md delete mode 100644 harness/viral-receiver-action-source-design-checklist.md delete mode 100644 harness/viral-receiver-action-source-product-brief.md delete mode 100644 harness/viral-receiver-conversion-design-checklist.md delete mode 100644 harness/viral-receiver-conversion-product-brief.md delete mode 100644 harness/viral-receiver-design-checklist.md delete mode 100644 harness/viral-receiver-product-brief.md delete mode 100644 harness/viral-relay-channel-picker-design-checklist.md delete mode 100644 harness/viral-relay-channel-picker-product-brief.md delete mode 100644 harness/viral-share-design-checklist.md delete mode 100644 harness/viral-share-product-brief.md delete mode 100644 harness/viral-share-reason-design-checklist.md delete mode 100644 harness/viral-share-reason-product-brief.md delete mode 100644 harness/viral-targeted-relay-design-checklist.md delete mode 100644 harness/viral-targeted-relay-product-brief.md delete mode 100644 harness/viral-timeline-evidence-checklist.md delete mode 100644 harness/viral-timeline-evidence-product-brief.md delete mode 100644 harness/viral-timeline-landing-checklist.md delete mode 100644 harness/viral-timeline-landing-product-brief.md delete mode 100644 harness/viral-timeline-share-design-checklist.md delete mode 100644 harness/viral-timeline-share-product-brief.md create mode 100644 knowledge/AGENTS.md create mode 100644 knowledge/architecture.md create mode 100644 knowledge/data-model.md create mode 100644 knowledge/deployment-operations.md create mode 100644 knowledge/design-ui.md create mode 100644 knowledge/development-verification.md create mode 100644 knowledge/index.md create mode 100644 knowledge/log.md create mode 100644 knowledge/privacy-security.md create mode 100644 knowledge/product-overview.md create mode 100644 knowledge/release-readiness.md create mode 100644 knowledge/sharing-attribution.md create mode 100644 knowledge/trust-safety.md create mode 100644 knowledge/user-journeys.md create mode 100644 pages/agreement/agreement.js create mode 100644 pages/agreement/agreement.json create mode 100644 pages/agreement/agreement.wxml create mode 100644 pages/agreement/agreement.wxss create mode 100644 pages/privacy/privacy.js create mode 100644 pages/privacy/privacy.json create mode 100644 pages/privacy/privacy.wxml create mode 100644 pages/privacy/privacy.wxss delete mode 100644 scripts/capture-viral-journey-blocked-evidence.mjs create mode 100644 scripts/check-comment-moderation.mjs create mode 100644 scripts/check-content-safety.mjs delete mode 100644 scripts/check-devtools-readiness.mjs delete mode 100644 scripts/check-devtools-recovery-report-preflight.mjs delete mode 100644 scripts/check-devtools-recovery-report.mjs delete mode 100644 scripts/check-devtools-ui-confirmation.mjs delete mode 100644 scripts/check-evidence-hygiene.mjs create mode 100644 scripts/check-knowledge.mjs delete mode 100644 scripts/check-manual-evidence.mjs rename {harness => scripts}/check-map-feed.mjs (99%) delete mode 100644 scripts/check-map-list-blocked-summary-preflight.mjs delete mode 100644 scripts/check-map-list-blocked-summary.mjs create mode 100644 scripts/check-privacy-compliance.mjs create mode 100644 scripts/check-readiness.mjs rename {harness => scripts}/check-trust-insight.mjs (97%) delete mode 100644 scripts/check-viral-journey-evidence-packet.mjs delete mode 100644 scripts/check-viral-journey-evidence.mjs delete mode 100644 scripts/check-viral-journey-manual-evidence.mjs delete mode 100644 scripts/check-viral-manual-artifact-manifest-preflight.mjs delete mode 100644 scripts/check-viral-manual-artifact-manifest.mjs delete mode 100644 scripts/check-viral-manual-summary-integrity-preflight.mjs delete mode 100644 scripts/check-viral-manual-summary-integrity.mjs delete mode 100644 scripts/create-manual-summary.mjs delete mode 100644 scripts/prepare-devtools-recovery-report.mjs delete mode 100644 scripts/prepare-devtools-ui-confirmation-run.mjs delete mode 100644 scripts/prepare-manual-test-run.mjs delete mode 100644 scripts/prepare-map-list-blocked-evidence.mjs delete mode 100644 scripts/prepare-map-list-blocked-summary.mjs delete mode 100644 scripts/prepare-viral-journey-devtools-run.mjs delete mode 100644 scripts/prepare-viral-journey-evidence-packet.mjs delete mode 100644 scripts/prepare-viral-journey-manual-evidence.mjs create mode 100644 utils/privacy.js diff --git a/.gitignore b/.gitignore index 64d35b1..cc089a5 100644 --- a/.gitignore +++ b/.gitignore @@ -11,16 +11,10 @@ project.private.config.json .claude/ .gstack/ .understand-anything/ +mcp-gateway/ skills-lock.json style-comparison.html # Runtime logs log/ *.log - -# Local manual-test evidence artifacts -harness/manual-test-results.local*.json -harness/manual-test-summary.local*.md -harness/manual-artifact-manifest.local*.json -harness/manual-evidence-artifacts/ -harness/devtools-recovery-report.local*.md diff --git a/AGENTS.md b/AGENTS.md index b3492c2..59c8bd9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,43 +8,41 @@ Street Tasks is a native WeChat mini program for short-lived neighborhood tasks. The app can run locally from mock data and `wx` local storage, and it also has CloudBase-backed paths for shared posts, reactions, comments, feedback, viral attribution events, images, and admin role checks. Treat `utils/store.js` as the main persistence boundary; page code should not duplicate storage or cloud fallback logic. -## Harness Operating Loop +## Project Knowledge Base -This repository keeps agent state, verification, and handoff files under `harness/`. Treat those files as the durable source of truth for long-running AI-assisted work. +This repository keeps durable product, architecture, data, safety, privacy, and +launch knowledge under `knowledge/`. Treat it as the source of truth for how the +product is supposed to behave; code and platform state take priority when they +disagree, and the docs should then be corrected. Before changing code: 1. Run `pwd` and confirm the repo root is `/Users/bytedance/git/x`. -2. Read `harness/claude-progress.md` for the latest verified state, blocker, and next action. -3. Read `harness/feature_list.json` and pick the highest-priority unfinished feature. Keep at most one feature `in_progress`. -4. Check recent history with `git log --oneline -5`. -5. Run `bash harness/init.sh`. If it fails, fix the base state before adding feature work. +2. Read `knowledge/index.md` and the relevant topic article for the area you are + touching (e.g. `knowledge/data-model.md`, `knowledge/trust-safety.md`). +3. Check recent history with `git log --oneline -5`. +4. Run `npm run check`. If it fails, fix the base state before adding new work. -Required harness files: +Key knowledge articles: -- `harness/feature_list.json`: machine-readable feature status and verification evidence. -- `harness/claude-progress.md`: session log and current verified state. -- `harness/init.sh`: single bootstrap and baseline verification entrypoint. -- `harness/session-handoff.md`: short handoff summary for longer sessions. -- `harness/clean-state-checklist.md`: closeout checklist before handing work back. -- `harness/evaluator-rubric.md`: acceptance rubric for completed work. -- `harness/quality-document.md`: project quality snapshot by product area and architecture layer. +- `knowledge/index.md`: entry point linking every topic article. +- `knowledge/product-overview.md`: product scope, categories, and non-goals. +- `knowledge/architecture.md`: page layer, shared modules, and cloud boundary. +- `knowledge/data-model.md`: entities, fields, and status-transition rules. +- `knowledge/trust-safety.md` / `knowledge/privacy-security.md`: moderation, + content safety, consent, and public-data limits. +- `knowledge/development-verification.md` / `knowledge/release-readiness.md`: + how to verify changes and what still blocks launch. + +Follow `knowledge/AGENTS.md` when maintaining the knowledge base itself; record +structural changes in `knowledge/log.md`. Completion definition: - The target behavior is implemented. -- Required verification actually ran. -- Evidence is recorded in `harness/feature_list.json` or `harness/claude-progress.md`. -- The repo can still be restarted from `bash harness/init.sh`. +- Required verification actually ran (`npm run check` plus targeted `node --check`). - Any skipped manual WeChat DevTools checks are called out as unverified, not implied passing. - -Session closeout: - -1. Update `harness/claude-progress.md`. -2. Update `harness/feature_list.json` when feature status or evidence changes. -3. Note unresolved risks or blockers. -4. Run the relevant verification commands. -5. Leave the next session able to continue from repo files alone. +- Knowledge articles are updated if the change alters documented product behavior. ## Tech Stack @@ -70,7 +68,7 @@ Session closeout: - `utils/post-presenter.js`: shared presentation helpers for profile/activity pages. - `utils/diagnostics.js`: runtime diagnostics used by map startup and fallback paths. - `utils/mock-posts.js`: seed data used when local storage is empty. -- `harness/*`: agent harness state, verification, closeout, and quality tracking files. +- `knowledge/*`: durable product, architecture, data, safety, privacy, and launch knowledge base. - `DESIGN_SYSTEM.md`: current visual design rules and native/TDesign-style component patterns. - `PROJECT_SUMMARY.md`: high-level project summary for humans and future agents. - `pages/map/*`: map feed, marker interactions, and list overlay. @@ -108,12 +106,13 @@ There is no general unit test suite yet. Use these baseline checks for most changes: ```bash -bash harness/init.sh -node scripts/check-json.mjs -node harness/check-harness.mjs +npm run check git diff --check ``` +`npm run check` runs the JSON syntax check, the knowledge-base structure check, +and the product-behavior readiness checks under `scripts/`. + After editing JavaScript, run targeted syntax checks, for example: ```bash @@ -121,7 +120,7 @@ node --check pages/map/map.js node --check utils/store.js ``` -User-visible mini program behavior still needs WeChat DevTools or real-device verification. Record any manual evidence, skipped checks, or remaining risks in `harness/claude-progress.md` or `harness/feature_list.json`. +User-visible mini program behavior still needs WeChat DevTools or real-device verification. Record any manual evidence, skipped checks, or remaining risks alongside the related change (commit, PR, or issue), and update the relevant `knowledge/` article if documented behavior changes. ## Data Model Notes @@ -164,7 +163,7 @@ CloudBase is optional for local development but required for shared multi-user d - `viral_attribution_events` - `admins` -Text-only posts may fall back to local storage when cloud APIs are unavailable. Image posts are stricter: selected images are compressed, capped at 4 files under 1.5MB each, uploaded to CloudBase Storage, and saved as `cloud://` file IDs. If cloud upload or cloud post creation fails for an image post, fail explicitly instead of saving local-only temp image paths. +Local storage is used only when CloudBase is disabled for development. Once CloudBase is enabled, publish and comment failures must be surfaced instead of silently falling back to local success. Image posts are stricter: selected images are compressed, capped at 4 JPG/JPEG/PNG files under 1MB each and no larger than 750×1334, and images with unknown types or dimensions are rejected. Accepted images are uploaded to CloudBase Storage and saved as `cloud://` file IDs. If cloud upload, safety checking, or cloud post creation fails for an image post, fail explicitly instead of saving local-only temp image paths. ## Coding Conventions diff --git a/LAUNCH_TODO.md b/LAUNCH_TODO.md index 55f7ced..aab9572 100644 --- a/LAUNCH_TODO.md +++ b/LAUNCH_TODO.md @@ -44,15 +44,15 @@ - [ ] `viral_attribution_events` — 传播归因事件 - [ ] `admins` — 管理员列表 - [ ] 为每个集合配置安全规则(最小权限原则): - - `posts`:所有用户可读,仅创建者/管理员可写 - - `post_reactions`:登录用户可创建自己的反应,可读 - - `post_comments`:登录用户可创建,所有可读 - - `feedback_items`:登录用户可创建自己的,仅管理员可读 + - `posts`、`post_reactions`、`post_comments`、`feedback_items`、`admins`:客户端不得直读直写,只能经云函数校验 - `viral_attribution_events`:仅服务端可写 - - `admins`:仅服务端可读 +- [ ] 创建并验证组合索引: + - `post_comments(postId, status, createdAt desc)`(普通评论列表) + - `post_comments(reportCount, lastReportedAt desc)`(管理员举报队列) ### 2.2 云函数部署 - [ ] 部署 `posts` 云函数(处理帖子 CRUD、评论、反馈、信任动作、传播归因、图片上传) +- [ ] 确认 `posts/config.json` 的 `security.msgSecCheck`、`security.imgSecCheck` OpenAPI 权限已随部署生效(权限缓存可能延迟) - [ ] 部署 `getMyRole` 云函数(管理员角色查询) - [ ] 为两个云函数配置超时时间(建议 10-20 秒) - [ ] 确认云函数运行环境 Node.js 版本与本地一致 @@ -61,7 +61,7 @@ ### 2.3 云存储 - [ ] 开通 CloudBase 云存储(图片发布需要 `cloud://` fileID) -- [ ] 创建存储目录:`posts/images/` +- [ ] 确认随机化 `posts/` 上传路径可写,且公开 fileID 不含稳定用户标识 - [ ] 配置存储安全规则:用户可上传,所有用户可读 - [ ] 设置存储容量上限和清理策略(过期任务的图片定期清理) @@ -86,18 +86,25 @@ - [ ] 清理 `utils/post-presenter.js` 中已移除的无用代码(当前 diff 显示删除了 42 行) ### 3.2 发布前必须修复的问题 -- [ ] 评论治理:补充评论举报、隐藏、删除能力(当前 TODOS.md 已记录,上线前至少需要举报+管理员隐藏) +- [ ] 评论治理:补充评论举报与管理员隐藏(删除能力尚未实现,不在本次完成声明中) + - [x] 本地代码已完成:5 种举报原因、事务内去重计数、2 次自动隐藏、管理员手动隐藏、跨设备已举报状态、最多 500 条分页队列、加载错误显式展示 + - 静态验证已通过:`scripts/check-comment-moderation.mjs`、`npm run check` + - 待真实 CloudBase/DevTools/真机验证:并发举报、跨设备去重、阈值隐藏、管理员队列/隐藏、组合索引 - [ ] 内容安全:接入微信内容安全 API(`msgSecCheck`、`imgSecCheck`) - - 发布任务时校验标题和正文 - - 上传图片时校验图片 - - 评论内容校验 -- [ ] 隐私合规:位置信息单独同意弹窗(PIPL 要求) - - 首次使用定位前展示单独的隐私说明 - - 用户拒绝后仍可使用默认中心浏览 + - [x] 本地代码已完成:`msgSecCheck` v2 使用任务 scene=3、评论 scene=2 和可信 OPENID;仅 `suggest=pass` 放行,`review/risky/87014` 拦截;API、权限或格式异常 fail closed + - [x] 图片同步审核收紧为 1MB、JPG/JPEG/PNG、最大 750×1334;未知类型或尺寸拒绝;发布/评论在已配置 CloudBase 时不得静默落本地 + - 静态验证已通过:`scripts/check-content-safety.mjs`、`npm run check` + - 待真实 AppID 部署验证:正常、review/risky、87014、权限缺失、超限/异常图片;长期需迁移 `mediaCheckAsync` +- [ ] 隐私合规:位置信息单独同意与完整告知 + - [x] 本地工程已完成:协议/政策/位置用途分版本记录,定位前单独同意,撤回后停止新的云端业务读取;拒绝后地图使用默认中心,发布明确要求当前位置 + - [x] 普通帖子、评论、反馈响应改为 DTO 白名单,不返回 raw OPENID/登录时间;公开图片路径不再含稳定用户哈希 + - 静态验证已通过:`scripts/check-privacy-compliance.mjs`、`npm run check` + - [ ] 待运营/法务补齐:实际运营主体、直接联系方式、各类数据保存期限/删除机制、CloudBase 实际地域、未成年人专门规则 + - [ ] 待公众平台和真机验证:《小程序用户隐私保护指引》配置、平台隐私授权 API、定位/相册/昵称头像声明 - [ ] 用户协议与隐私政策页面 - - 创建用户协议页面 - - 创建隐私政策页面 - - 在登录/发布前展示并获得同意 + - [x] 本地工程草案已完成:两份文本可分别阅读;隐私页按钮明确同时同意;单页不再暗中代表另一份文件;提供撤回、微信设置和带回执的个人信息权利申请入口 + - 静态验证已通过:`scripts/check-privacy-compliance.mjs`、`npm run check` + - [ ] 正式文本仍需运营方/法务确认并补齐上述占位信息,之后才可标记上线 ### 3.3 性能优化 - [ ] 地图高密度场景:当前 100 marker 上限,上线前需确认种子区域不会超过 @@ -123,7 +130,7 @@ - [ ] 未登录点击发布显示登录引导 - [ ] 登录后每个分类(打卡/失物招领/地点动态/求助问答)均可发布 - [ ] 位置确认、有效期选择、自定义时间正常 - - [ ] 图片选择、压缩、上传正常(最多 4 张,每张 <1.5MB) + - [ ] 图片选择、压缩、上传正常(最多 4 张,每张 <1MB,JPG/JPEG/PNG,最大 750×1334) - [ ] 发布成功后地图/详情页可见新任务 - [ ] 必填项校验、字数限制、图片数量限制正常 - [ ] 详情页: @@ -134,11 +141,17 @@ - [ ] 同一用户不能重复同一动作 - [ ] 发布者可关闭任务 - [ ] resolved/expired 任务评论只读 + - [ ] 评论举报:每条评论显示举报按钮(已举报后隐藏) + - [ ] 评论举报弹窗显示 5 种原因,选择后可提交 + - [ ] 同一用户不能重复举报同一评论 + - [ ] 评论举报达 2 次后自动隐藏,普通用户不可见 - [ ] 管理后台: - [ ] 普通用户无管理员能力 - [ ] 管理员可查看风险筛选、搜索、隐藏/关闭任务 - [ ] 反馈列表查看 - [ ] admins 集合缺失时不崩溃 + - [ ] 被举报评论列表加载正常 + - [ ] 管理员可隐藏被举报评论,隐藏后普通用户不可见 - [ ] 个人中心: - [ ] 登录/头像昵称设置 - [ ] 我的发布列表和统计 @@ -153,7 +166,7 @@ ### 4.2 真机测试(至少 2 台设备) - [ ] iPhone(最新 iOS + 微信最新版)全流程测试 - [ ] Android(主流机型 + 微信最新版)全流程测试 -- [ ] 弱网/无网环境测试(本地 fallback 是否正常) +- [ ] 弱网/无网环境测试(读取可降级;发布、评论、举报、管理和权利申请不得出现“仅本机成功”的假成功) - [ ] 定位授权拒绝/允许/仅一次三种场景 - [ ] 后台/前台切换后状态保持 - [ ] 小程序冷启动/热启动表现 @@ -161,9 +174,7 @@ - [ ] 图片拍摄/相册选择权限处理 ### 4.3 自动化检查 -- [ ] `npm run check:json` — JSON 配置语法检查 -- [ ] `node harness/check-harness.mjs` — Harness 状态检查 -- [ ] `bash harness/init.sh` — 基线验证 +- [ ] `npm run check` — JSON 配置、知识库结构和产品行为静态检查 - [ ] `node --check` 所有 JS 文件语法检查 - [ ] `git diff --check` — 空白和冲突标记检查 @@ -199,21 +210,21 @@ ## 六、合规与法律(优先级:P0,阻塞项) ### 6.1 隐私合规 -- [ ] 编写并上线《用户协议》 -- [ ] 编写并上线《隐私政策》 - - 明确说明收集的信息:位置、昵称、头像、发布内容 - - 明确说明位置信息的用途和存储方式 - - 明确说明第三方服务(CloudBase、微信广告) - - 提供用户注销/删除数据的方式 -- [ ] 位置信息单独同意弹窗(PIPL 第 28 条要求) -- [ ] 首次启动展示隐私协议弹窗,用户同意后才初始化 -- [ ] 不收集不必要的敏感信息(不收集精确通讯录、不收集身份证号等) +- [ ] 运营方/法务确认并上线《用户协议》(仓库内仅为工程草案) +- [ ] 运营方/法务确认并上线《隐私政策》(仓库内明确保留主体、联系方式、期限、地域占位) +- [ ] 在微信公众平台配置并审核《小程序用户隐私保护指引》,用真实 AppID 验证平台授权流程 +- [x] 位置信息单独同意弹窗与版本化记录;拒绝后仅默认中心浏览,发布仍需位置 +- [x] 协议和政策分开记录版本,版本变化需重新同意,并提供应用内撤回 +- [x] 未同意当前协议/政策时,云端业务读取和分享归因停用,仅展示本机示例;`wx.cloud.init` 使用 `traceUser: false` +- [x] 公开 API 使用 DTO 白名单,不返回 raw OPENID、内部登录时间或举报人标识 +- [x] 个人信息权利申请必须线上送达并返回回执;失败明确显示未送达 +- [ ] 实现并验证各类数据保存期限、到期删除及用户删除请求处置 SOP ### 6.2 内容安全 -- [ ] 接入微信 `msgSecCheck` 文本内容安全 API -- [ ] 接入微信 `imgSecCheck` 图片内容安全 API -- [ ] 设置关键词黑名单(政治、色情、暴力、广告等) -- [ ] 建立用户举报→管理员审核→处理的流程 +- [ ] 部署并用真实 AppID 验证微信 `msgSecCheck` 文本内容安全 API(本地代码已接入) +- [ ] 部署并用真实 AppID 验证微信 `imgSecCheck` 图片内容安全 API(本地代码已接入) +- [x] 本地开发模式设置基础关键词黑名单;生产 CloudBase 配置下不以它代替微信审核 +- [ ] 在真实云端验证用户举报→管理员审核→处理流程(本地代码与静态门禁已完成) - [ ] 准备内容审核 SOP 和应急响应方案 ### 6.3 广告合规(如上线广告) diff --git a/PROJECT_SUMMARY.md b/PROJECT_SUMMARY.md index 498da5c..ff1cdfa 100644 --- a/PROJECT_SUMMARY.md +++ b/PROJECT_SUMMARY.md @@ -23,7 +23,7 @@ Street Tasks(街区任务)是一个原生微信小程序,用于发布、 - 本地持久化:`wx.getStorageSync` / `wx.setStorageSync` - 地图与定位:微信 `map` 组件、`wx.getLocation`,坐标类型使用 `gcj02` - 云端能力:CloudBase 云函数与数据库集合 -- 自动化基线:`node scripts/check-json.mjs`、`node harness/check-harness.mjs` +- 自动化基线:`npm run check`(JSON、知识库结构、产品行为检查) 项目没有前端框架、构建器或小程序 npm 组件依赖。UI 以原生组件实现,并参考 TDesign 风格规则维护统一视觉语言。 @@ -127,18 +127,16 @@ Street Tasks(街区任务)是一个原生微信小程序,用于发布、 基础验证命令: ```bash -bash harness/init.sh -node scripts/check-json.mjs -node harness/check-harness.mjs +npm run check ``` -`harness/init.sh` 会执行当前项目的基线检查,并提示用 WeChat DevTools 打开项目。由于这是原生微信小程序,用户可见行为仍需要在 WeChat DevTools 或真机中手动验证。 +`npm run check` 会执行 JSON 语法、知识库结构和产品行为静态检查。由于这是原生微信小程序,用户可见行为仍需要在 WeChat DevTools 或真机中手动验证。 ## 当前项目状态 -截至最近 harness 记录,基础 JSON 与 harness 自检通过。地图、发布、详情、评论、反馈、管理和个人页已有可运行实现,但部分产品链路仍需要 WeChat DevTools 或真机补充验证证据。 +截至最近记录,`npm run check` 的 JSON、知识库结构和产品行为静态检查通过。地图、发布、详情、评论、反馈、管理和个人页已有可运行实现,但部分产品链路仍需要 WeChat DevTools 或真机补充验证证据。 -当前最高优先级未完成功能是 `map-feed-001`:地图首页附近任务浏览。最近的实现重点包括: +地图首页附近任务浏览是核心体验,最近的实现重点包括: - 地图首屏改为默认中心 + 本地数据优先,避免启动时被定位、云函数或地图 region 读取阻塞。 - 地图页增加运行诊断,用于白屏或原生层超时排查。 @@ -159,4 +157,4 @@ node harness/check-harness.mjs Street Tasks 的核心价值是把“附近短时信息”从松散聊天变成可确认、可更新、可关闭的轻量任务流。它不是完整社区平台,而是一个地图优先的本地信息工具:用户可以快速发布身边状态,附近的人可以用低成本动作帮助信息保持可信,管理员也有基本能力处理风险内容。 -当前代码结构已经把页面层、共享业务边界、本地持久化和云函数职责分开,适合继续沿着“小范围功能 + harness 证据”的方式迭代。 +当前代码结构已经把页面层、共享业务边界、本地持久化和云函数职责分开,适合继续沿着“小范围功能 + 明确验证证据”的方式迭代。 diff --git a/README.md b/README.md index 7fba76c..da8077f 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ The app has no fixed service area. Users can browse and publish tasks from any c - Local duplicate prevention for repeated trust actions on the same post. - Local login, with an admin-only management tab controlled by the local admin code in `utils/config.js`. - Management console for search, risk filtering, reported/stale/hidden review, user feedback, and hide/close actions. -- CloudBase-backed posts, reactions, comments, feedback, and viral attribution events when `utils/config.js` cloud settings are enabled, with local mock storage fallback for development. +- CloudBase-backed posts, reactions, comments, feedback, and viral attribution events when `utils/config.js` cloud settings are enabled. Local mock storage is used only when CloudBase is disabled for development; enabled CloudBase publish/comment failures do not silently fall back to local success. ## Run Locally @@ -29,7 +29,7 @@ The app has no fixed service area. Users can browse and publish tasks from any c For local-only development, the app falls back to `wx` local storage. For shared user data, deploy the `posts` cloud function and create the `posts`, `post_reactions`, `post_comments`, `feedback_items`, `viral_attribution_events`, and `admins` collections in CloudBase. -Posts with images require CloudBase Storage. The publish flow compresses selected images, accepts up to 4 images under 1.5MB each, uploads them to CloudBase Storage, and saves only `cloud://` file IDs. If the cloud upload or cloud post save is unavailable, image posts fail explicitly instead of falling back to local-only image paths. +Posts with images require CloudBase Storage. The current synchronous safety-check path accepts up to 4 JPG/JPEG/PNG images under 1MB each and no larger than 750×1334, rejects unknown types or dimensions, uploads them to CloudBase Storage, and saves only `cloud://` file IDs. If cloud upload, safety checking, or cloud post creation fails, image posts fail explicitly instead of falling back to local-only image paths. ## Product Scope diff --git a/app.js b/app.js index b2fcefe..b81862d 100644 --- a/app.js +++ b/app.js @@ -26,7 +26,7 @@ App({ try { wx.cloud.init({ env: config.cloud.envId, - traceUser: true + traceUser: false }); this.globalData.cloudReady = true; } catch (error) { diff --git a/app.json b/app.json index ef15a62..d3cd893 100644 --- a/app.json +++ b/app.json @@ -7,7 +7,9 @@ "pages/me/me", "pages/my-posts/my-posts", "pages/activities/activities", - "pages/feedback/feedback" + "pages/feedback/feedback", + "pages/agreement/agreement", + "pages/privacy/privacy" ], "window": { "navigationBarTitleText": "街区任务", diff --git a/cloudfunctions/posts/config.json b/cloudfunctions/posts/config.json new file mode 100644 index 0000000..0afb7df --- /dev/null +++ b/cloudfunctions/posts/config.json @@ -0,0 +1,8 @@ +{ + "permissions": { + "openapi": [ + "security.msgSecCheck", + "security.imgSecCheck" + ] + } +} diff --git a/cloudfunctions/posts/index.js b/cloudfunctions/posts/index.js index 1525e59..7317ba4 100644 --- a/cloudfunctions/posts/index.js +++ b/cloudfunctions/posts/index.js @@ -16,22 +16,36 @@ const FEEDBACK_COLLECTION = 'feedback_items'; const VIRAL_ATTRIBUTION_COLLECTION = 'viral_attribution_events'; const MAX_VISIBLE_POSTS = 100; const MAX_IMAGE_COUNT = 4; -const MAX_IMAGE_SIZE_BYTES = 1536 * 1024; +const MAX_IMAGE_SIZE_BYTES = 1024 * 1024; const MAX_IMAGE_URL_LENGTH = 500; const MAX_COMMENTS_PER_POST = 50; +const MAX_REPORTED_COMMENTS = 500; const MAX_COMMENT_LENGTH = 120; +const COMMENT_REPORT_THRESHOLD = 2; +const COMMENT_REPORT_REASONS = ['spam', 'inappropriate', 'advertising', 'abuse', 'other']; const MAX_FEEDBACKS = 100; const MAX_FEEDBACK_BODY_LENGTH = 500; const MAX_FEEDBACK_CONTACT_LENGTH = 80; const CLOSED_STATUSES = ['hidden', 'resolved']; const CATEGORIES = ['check_in', 'lost_found', 'street_update', 'help_needed']; +const CONTENT_SECURITY_ERROR_CODE = 87014; +const CONTENT_SECURITY_SCENES = { + 'post title': 3, + 'post body': 3, + comment: 2 +}; +const IMAGE_SECURITY_CONTENT_TYPES = { + jpg: 'image/jpeg', + jpeg: 'image/jpeg', + png: 'image/png' +}; const LOST_FOUND_INTENTS = ['lost', 'found']; -const FEEDBACK_TYPES = ['suggestion', 'bug', 'content', 'other']; +const FEEDBACK_TYPES = ['suggestion', 'bug', 'content', 'privacy', 'other']; const LONG_TERM_EXPIRY_HOURS = 24 * 365 * 10; const EXPIRY_HOURS = [168, 720, LONG_TERM_EXPIRY_HOURS]; const HOUR_MS = 60 * 60 * 1000; const CUSTOM_EXPIRY_MIN_OFFSET_MS = 30 * 60 * 1000; -const IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'webp']; +const IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png']; const VIRAL_EVENT_TYPES = [ 'share_detail_landing', 'share_detail_loaded', @@ -213,19 +227,72 @@ function decorateForUser(post, openid) { return null; } return { - ...normalized, + id: normalized.id, + markerId: normalized.markerId, + title: normalized.title, + body: normalized.body, + imageUrls: Array.isArray(normalized.imageUrls) ? normalized.imageUrls : [], + category: normalized.category, + intent: normalized.intent || '', + placeName: normalized.placeName, + latitude: normalized.latitude, + longitude: normalized.longitude, + status: normalized.status, + confirmations: Number(normalized.confirmations) || 0, + lastConfirmedAt: Number(normalized.lastConfirmedAt) || 0, + staleCount: Number(normalized.staleCount) || 0, + reportCount: Number(normalized.reportCount) || 0, + createdAt: Number(normalized.createdAt) || 0, + expiresAt: Number(normalized.expiresAt) || 0, + expiryType: normalized.expiryType || '', + publisher: normalized.publisher || '匿名用户', + publisherAvatarUrl: normalized.publisherAvatarUrl || '', + publisherRole: normalized.publisherRole || 'user', isMine: normalized.publisherId === openid }; } -function decorateCommentForUser(comment, openid) { +function decorateCommentForUser(comment, openid, options = {}) { const normalized = normalizeComment(comment); if (!normalized) { return null; } + const decorated = { + id: normalized.id, + postId: normalized.postId, + body: normalized.body, + status: normalized.status, + author: normalized.author || '附近用户', + authorAvatarUrl: normalized.authorAvatarUrl || '', + authorRole: normalized.authorRole || 'user', + createdAt: Number(normalized.createdAt) || 0, + isMine: normalized.authorId === openid, + reportedByMe: Boolean(options.reportedByMe) + }; + if (options.includeModeration) { + decorated.reportCount = Number(normalized.reportCount) || 0; + decorated.lastReportedAt = Number(normalized.lastReportedAt) || 0; + decorated.lastReportReason = COMMENT_REPORT_REASONS.includes(normalized.lastReportReason) + ? normalized.lastReportReason + : ''; + } + return decorated; +} + +function decorateFeedbackForUser(feedback) { + const normalized = normalizeFeedback(feedback); + if (!normalized) { + return null; + } return { - ...normalized, - isMine: normalized.authorId === openid + id: normalized.id, + type: normalized.type, + body: normalized.body, + contact: normalized.contact || '', + nickname: normalized.nickname || '匿名用户', + role: normalized.role || 'user', + status: normalized.status || 'open', + createdAt: Number(normalized.createdAt) || 0 }; } @@ -247,6 +314,131 @@ function cleanUploadExtension(value) { return ext; } +function contentSecurityError(scene) { + const error = new Error(`Content security check failed for ${scene}`); + error.code = 'CONTENT_SECURITY'; + return error; +} + +function contentSecurityUnavailableError(scene) { + const error = new Error(`Content security check unavailable for ${scene}`); + error.code = 'CONTENT_SECURITY_UNAVAILABLE'; + return error; +} + +function verifyTextSecurityResult(result, scene) { + const errCode = Number(result && result.errCode); + if (errCode === CONTENT_SECURITY_ERROR_CODE) { + throw contentSecurityError(scene); + } + if (!result || errCode !== 0) { + throw contentSecurityUnavailableError(scene); + } + const suggestion = result.result && result.result.suggest; + if (suggestion === 'pass') { + return; + } + if (suggestion === 'review' || suggestion === 'risky') { + throw contentSecurityError(scene); + } + throw contentSecurityUnavailableError(scene); +} + +function verifyImageSecurityResult(result) { + const errCode = Number(result && result.errCode); + if (errCode === CONTENT_SECURITY_ERROR_CODE) { + throw contentSecurityError('image'); + } + if (!result || errCode !== 0) { + throw contentSecurityUnavailableError('image'); + } +} + +async function checkTextContent(text, scene, openid) { + if (!text) { + return; + } + if (!openid || !CONTENT_SECURITY_SCENES[scene] || !cloud.openapi || !cloud.openapi.security) { + throw contentSecurityUnavailableError(scene); + } + try { + const result = await cloud.openapi.security.msgSecCheck({ + content: text, + version: 2, + scene: CONTENT_SECURITY_SCENES[scene], + openid + }); + verifyTextSecurityResult(result, scene); + } catch (error) { + if (Number(error && error.errCode) === CONTENT_SECURITY_ERROR_CODE) { + throw contentSecurityError(scene); + } + if (error && ['CONTENT_SECURITY', 'CONTENT_SECURITY_UNAVAILABLE'].includes(error.code)) { + throw error; + } + console.warn('[posts] content security check error', { + scene, + message: error && (error.message || error.errMsg) + }); + throw contentSecurityUnavailableError(scene); + } +} + +async function checkImageSecurity(fileId) { + if (!fileId) { + return; + } + if (!cloud.openapi || !cloud.openapi.security) { + throw contentSecurityUnavailableError('image'); + } + try { + const downloadResult = await cloud.downloadFile({ fileID: fileId }); + const buffer = downloadResult && downloadResult.fileContent; + if (!buffer || buffer.length === 0) { + throw contentSecurityUnavailableError('image'); + } + if (buffer.length > MAX_IMAGE_SIZE_BYTES) { + const error = new Error('Image exceeds content-security API size limit'); + error.code = 'IMAGE_TOO_LARGE'; + throw error; + } + const ext = String(fileId.split('.').pop() || '').toLowerCase(); + const contentType = IMAGE_SECURITY_CONTENT_TYPES[ext]; + if (!contentType) { + const error = new Error('Unsupported image type'); + error.code = 'IMAGE_TYPE_UNSUPPORTED'; + throw error; + } + const result = await cloud.openapi.security.imgSecCheck({ + media: { + contentType, + value: buffer + } + }); + verifyImageSecurityResult(result); + } catch (error) { + if (Number(error && error.errCode) === CONTENT_SECURITY_ERROR_CODE) { + throw contentSecurityError('image'); + } + if ( + error + && [ + 'CONTENT_SECURITY', + 'CONTENT_SECURITY_UNAVAILABLE', + 'IMAGE_TOO_LARGE', + 'IMAGE_TYPE_UNSUPPORTED' + ].includes(error.code) + ) { + throw error; + } + console.warn('[posts] image security check error', { + fileId, + message: error && (error.message || error.errMsg) + }); + throw contentSecurityUnavailableError('image'); + } +} + async function isAdminOpenid(openid) { if (!openid) { return false; @@ -313,12 +505,20 @@ async function createPost(event, openid, isAdmin) { const input = event.input || {}; const now = Date.now(); const category = cleanCategory(input.category); + const title = cleanString(input.title, 32, 'title', true); + const body = cleanString(input.body, 180, 'body', true); + const imageUrls = cleanImageUrls(input.imageUrls); + await checkTextContent(title, 'post title', openid); + await checkTextContent(body, 'post body', openid); + for (const fileId of imageUrls) { + await checkImageSecurity(fileId); + } const post = { id: `post_${now}_${hashId(`${openid}:${now}:${Math.random()}`).slice(0, 8)}`, markerId: await nextMarkerId(), - title: cleanString(input.title, 32, 'title', true), - body: cleanString(input.body, 180, 'body', true), - imageUrls: cleanImageUrls(input.imageUrls), + title, + body, + imageUrls, category, intent: cleanIntent(category, input.intent), placeName: cleanString(input.placeName, 40, 'placeName') || '当前位置', @@ -335,8 +535,7 @@ async function createPost(event, openid, isAdmin) { publisherId: openid, publisher: cleanString(input.publisher, 32, 'publisher') || '匿名用户', publisherAvatarUrl: cleanString(input.publisherAvatarUrl, 500, 'publisherAvatarUrl'), - publisherRole: isAdmin ? 'admin' : 'user', - publisherLoggedInAt: Number(input.publisherLoggedInAt) || 0 + publisherRole: isAdmin ? 'admin' : 'user' }; await db.collection(POSTS_COLLECTION).add({ data: post }); return ok({ post: decorateForUser(post, openid) }); @@ -473,8 +672,11 @@ async function listComments(event, openid, isAdmin) { .orderBy('createdAt', 'desc') .limit(MAX_COMMENTS_PER_POST) .get(); + const reporterKey = hashId(`comment-report:${openid}`); const comments = (result.data || []) - .map((comment) => decorateCommentForUser(comment, openid)) + .map((comment) => decorateCommentForUser(comment, openid, { + reportedByMe: Array.isArray(comment.reporterKeys) && comment.reporterKeys.includes(reporterKey) + })) .filter(Boolean) .sort((a, b) => b.createdAt - a.createdAt); return ok({ comments, isAdmin }); @@ -489,30 +691,171 @@ async function createComment(event, openid, isAdmin) { if (!canComment(post, now)) { return fail('POST_CLOSED', 'This post no longer accepts comments'); } + const body = cleanString(event.body, MAX_COMMENT_LENGTH, 'body', true); + await checkTextContent(body, 'comment', openid); const comment = { id: `comment_${now}_${hashId(`${openid}:${post.id}:${now}:${Math.random()}`).slice(0, 8)}`, postId: post.id, - body: cleanString(event.body, MAX_COMMENT_LENGTH, 'body', true), + body, status: 'visible', + reportCount: 0, + lastReportedAt: 0, + lastReportReason: '', + reporterKeys: [], authorId: openid, author: cleanString(event.author, 32, 'author') || '附近用户', authorAvatarUrl: cleanString(event.authorAvatarUrl, 500, 'authorAvatarUrl'), authorRole: isAdmin ? 'admin' : 'user', - authorLoggedInAt: Number(event.authorLoggedInAt) || 0, createdAt: now }; await db.collection(COMMENTS_COLLECTION).add({ data: comment }); return ok({ comment: decorateCommentForUser(comment, openid) }); } +async function findCommentById(commentId) { + const id = cleanString(commentId, 120, 'commentId', true); + const result = await db.collection(COMMENTS_COLLECTION) + .where({ id }) + .limit(1) + .get(); + return normalizeComment(result.data && result.data[0]); +} + +async function reportComment(event, openid, isAdmin) { + const now = Date.now(); + const post = await findPostById(event.id); + if (!isViewable(post, isAdmin)) { + return fail('NOT_FOUND', 'Post not found'); + } + const comment = await findCommentById(event.commentId); + if (!comment || comment.postId !== post.id || comment.status === 'hidden') { + return ok({ comment: null, duplicate: false }); + } + const reason = cleanEnum(event.reason, COMMENT_REPORT_REASONS, 'reason', 'other'); + const reporterKey = hashId(`comment-report:${openid}`); + const reactionId = safeDocId([openid, comment.id, 'comment_report']); + const transactionResult = await db.runTransaction(async (transaction) => { + const commentRef = transaction.collection(COMMENTS_COLLECTION).doc(comment._id); + const snapshot = await commentRef.get(); + const current = normalizeComment(snapshot.data); + if (!current || current.postId !== post.id || current.status === 'hidden') { + return { comment: null, firstReport: false }; + } + const reporterKeys = Array.isArray(current.reporterKeys) + ? current.reporterKeys.filter((value) => typeof value === 'string') + : []; + const firstReport = !reporterKeys.includes(reporterKey); + if (!firstReport) { + return { comment: current, firstReport: false }; + } + const nextReporterKeys = [...reporterKeys, reporterKey]; + const reportCount = Math.max( + (Number(current.reportCount) || 0) + 1, + nextReporterKeys.length + ); + const updated = { + ...current, + reporterKeys: nextReporterKeys, + reportCount, + lastReportedAt: now, + lastReportReason: reason, + status: reportCount >= COMMENT_REPORT_THRESHOLD ? 'hidden' : current.status + }; + await transaction.collection(REACTIONS_COLLECTION).doc(reactionId).set({ + data: { + openid, + postId: post.id, + commentId: current.id, + action: 'comment_report', + reason, + createdAt: now + } + }); + await commentRef.update({ + data: { + reporterKeys: updated.reporterKeys, + reportCount: updated.reportCount, + lastReportedAt: updated.lastReportedAt, + lastReportReason: updated.lastReportReason, + status: updated.status + } + }); + return { comment: updated, firstReport: true }; + }); + return ok({ + comment: decorateCommentForUser(transactionResult.comment, openid, { + reportedByMe: true + }), + duplicate: !transactionResult.firstReport, + reportedAt: transactionResult.firstReport ? now : 0 + }); +} + +async function hideComment(event, openid, isAdmin) { + if (!isAdmin) { + return fail('FORBIDDEN', 'Only admins can hide comments'); + } + const post = await findPostById(event.id); + const comment = await findCommentById(event.commentId); + if (!post || !comment || comment.postId !== post.id) { + return ok({ comment: null }); + } + await db.collection(COMMENTS_COLLECTION) + .where({ id: comment.id }) + .update({ data: { status: 'hidden' } }); + const updated = await findCommentById(comment.id); + return ok({ comment: decorateCommentForUser(updated, openid) }); +} + +async function listReportedComments(event, openid, isAdmin) { + if (!isAdmin) { + return fail('FORBIDDEN', 'Only admins can list reported comments'); + } + const offset = Math.min( + Math.floor(Math.max(Number(event.offset) || 0, 0)), + MAX_REPORTED_COMMENTS - 1 + ); + const requestedLimit = Math.min( + Math.max(Number(event.limit) || MAX_COMMENTS_PER_POST, 1), + MAX_COMMENTS_PER_POST + ); + const limit = Math.min(requestedLimit, MAX_REPORTED_COMMENTS - offset); + const result = await db.collection(COMMENTS_COLLECTION) + .where({ + reportCount: _.gt(0) + }) + .orderBy('lastReportedAt', 'desc') + .skip(offset) + .limit(limit) + .get(); + const comments = (result.data || []) + .map((comment) => decorateCommentForUser(comment, openid, { + includeModeration: true + })) + .filter(Boolean); + return ok({ + comments, + isAdmin, + hasMore: comments.length === limit && offset + comments.length < MAX_REPORTED_COMMENTS, + nextOffset: offset + comments.length + }); +} + async function createFeedback(event, openid, isAdmin) { const input = event.input || {}; const now = Date.now(); + const type = cleanFeedbackType(input.type); + const contact = cleanString(input.contact, MAX_FEEDBACK_CONTACT_LENGTH, 'contact'); + if (type === 'privacy' && !contact) { + const error = new Error('Privacy request contact is required'); + error.code = 'VALIDATION_FAILED'; + throw error; + } const feedback = { id: `feedback_${now}_${hashId(`${openid}:${now}:${Math.random()}`).slice(0, 8)}`, - type: cleanFeedbackType(input.type), + type, body: cleanString(input.body, MAX_FEEDBACK_BODY_LENGTH, 'body', true), - contact: cleanString(input.contact, MAX_FEEDBACK_CONTACT_LENGTH, 'contact'), + contact, userId: openid, nickname: cleanString(input.nickname, 32, 'nickname') || '街区用户', role: isAdmin ? 'admin' : 'user', @@ -520,7 +863,7 @@ async function createFeedback(event, openid, isAdmin) { createdAt: now }; await db.collection(FEEDBACK_COLLECTION).add({ data: feedback }); - return ok({ feedback: normalizeFeedback(feedback) }); + return ok({ feedback: decorateFeedbackForUser(feedback) }); } async function listFeedback(event, openid, isAdmin) { @@ -533,7 +876,7 @@ async function listFeedback(event, openid, isAdmin) { .limit(limit) .get(); const feedbacks = (result.data || []) - .map(normalizeFeedback) + .map(decorateFeedbackForUser) .filter(Boolean); return ok({ feedbacks, isAdmin }); } @@ -571,13 +914,12 @@ async function recordViralAttribution(event, openid) { return ok({ recorded: true }); } -function prepareUpload(event, openid) { +function prepareUpload(event) { const ext = cleanUploadExtension(event.ext); const index = Math.max(0, Math.min(Number(event.index) || 0, MAX_IMAGE_COUNT - 1)); - const ownerHash = hashId(openid).slice(0, 16); - const nonce = hashId(`${openid}:${Date.now()}:${Math.random()}`).slice(0, 10); + const nonce = crypto.randomBytes(16).toString('hex'); return ok({ - cloudPath: `posts/${ownerHash}/${Date.now()}_${index}_${nonce}.${ext}`, + cloudPath: `posts/${Date.now()}_${index}_${nonce}.${ext}`, maxImageCount: MAX_IMAGE_COUNT, maxImageSizeBytes: MAX_IMAGE_SIZE_BYTES, allowedExtensions: IMAGE_EXTENSIONS @@ -615,6 +957,15 @@ exports.main = async (event = {}) => { if (event.action === 'createComment') { return createComment(event, OPENID, admin); } + if (event.action === 'reportComment') { + return reportComment(event, OPENID, admin); + } + if (event.action === 'hideComment') { + return hideComment(event, OPENID, admin); + } + if (event.action === 'listReportedComments') { + return listReportedComments(event, OPENID, admin); + } if (event.action === 'createFeedback') { return createFeedback(event, OPENID, admin); } @@ -625,13 +976,25 @@ exports.main = async (event = {}) => { return recordViralAttribution(event, OPENID); } if (event.action === 'prepareUpload') { - return prepareUpload(event, OPENID); + return prepareUpload(event); } return fail('UNKNOWN_ACTION', 'Unknown action'); } catch (error) { if (isMissingCollectionError(error)) { return fail('MISSING_COLLECTION', 'Required CloudBase collection is missing'); } + if (error && error.code === 'CONTENT_SECURITY') { + return fail('CONTENT_SECURITY', '内容包含不当信息,请修改后重试'); + } + if (error && error.code === 'CONTENT_SECURITY_UNAVAILABLE') { + return fail('CONTENT_SECURITY_UNAVAILABLE', '内容审核暂不可用,请稍后再试'); + } + if (error && error.code === 'IMAGE_TOO_LARGE') { + return fail('IMAGE_TOO_LARGE', '图片需小于1MB'); + } + if (error && error.code === 'IMAGE_TYPE_UNSUPPORTED') { + return fail('IMAGE_TYPE_UNSUPPORTED', '仅支持 JPG、JPEG 或 PNG 图片'); + } if (error && error.code === 'VALIDATION_FAILED') { return fail('VALIDATION_FAILED', 'Invalid post data'); } diff --git a/harness/README.md b/harness/README.md deleted file mode 100644 index 9d30ca1..0000000 --- a/harness/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# Street Tasks Harness - -这个目录是本仓库的 agent harness。它把教程里的最小闭环落到一个地方:开工入口、功能状态、进度日志、验证证据、交接和质量快照。 - -## 使用顺序 - -1. 从根目录读 `AGENTS.md`。 -2. 读 `harness/claude-progress.md`,确认当前已验证状态和下一步。 -3. 读 `harness/feature_list.json`,选择最高优先级未完成项。 -4. 运行 `bash harness/init.sh`,确认基础验证可用。 -5. 做窄范围改动。 -6. 把验证证据写回 `harness/feature_list.json` 或 `harness/claude-progress.md`。 -7. 按 `harness/clean-state-checklist.md` 收尾。 - -## 文件分工 - -- `init.sh`: 统一初始化和基础验证入口。 -- `check-harness.mjs`: 验证 harness 文件结构和关键规则。 -- `feature_list.json`: 功能状态和验收证据的机器可读清单。 -- `claude-progress.md`: 跨会话进度日志。 -- `session-handoff.md`: 当前会话交接摘要。 -- `clean-state-checklist.md`: 收尾检查清单。 -- `evaluator-rubric.md`: 单轮工作验收评分表。 -- `quality-document.md`: 项目质量快照。 - -## 本仓库验证边界 - -当前自动化验证覆盖 JSON 配置和 harness 自检。小程序真实交互仍需要 WeChat DevTools 或真机预览手动确认,因此不能只凭 `npm run check:json` 把用户可见功能标为 `passing`。 diff --git a/harness/admin-design-brief.md b/harness/admin-design-brief.md deleted file mode 100644 index e54b4b0..0000000 --- a/harness/admin-design-brief.md +++ /dev/null @@ -1,56 +0,0 @@ -# 管理台轻量设计简报 - -## 1. 当前视觉与信息层级观察 - -- 现有管理台已经贴合 Street Tasks 的本地生活工具感:暖灰背景、白色卡片、低饱和绿色主色、橙红风险色,整体克制,不像后台大屏或营销页。 -- 首屏信息顺序是“管理身份说明 → 四个统计 → 用户反馈 → 搜索筛选 → 处置队列”。统计和队列可扫读,但“为什么这条需要处理”主要藏在风险标签、过时/举报数字里,管理员需要自己把线索拼起来。 -- 处置卡片的信息比较完整:风险、分类、状态、标题、正文、地点、发布者、时间、有效期、确认/过时/举报、操作按钮都在一张卡里。问题是同等权重的信息偏多,真正的处理依据和下一步动作没有被优先推到视线前面。 -- 用户反馈区独立放在任务队列之前,符合“先看主动反馈”的运营逻辑;但如果反馈很多,管理任务队列会被下推,后续需要注意列表长度对效率的影响。 -- 当前样式已考虑了一些移动端约束,例如标签可换行、ID 固定在右侧、meta 文本省略、按钮等分。但长标题、长 ID 和三按钮同时出现时,仍有挤压风险。 - -## 2. 本轮设计目标 - -让管理员在移动端快速回答两个问题: - -1. 为什么要处理:一眼看到触发处置的核心风险,例如“2 次举报”“3 次过时”“已隐藏但仍需复核”。 -2. 下一步做什么:一眼知道建议动作是查看详情、关闭还是隐藏,并能理解动作后果。 - -设计上不追求增加功能密度,而是把已有信息重新分层:风险理由优先、任务内容次之、元信息折叠成辅助、操作保持少而明确。 - -## 3. 推荐界面变化 - -1. 在处置卡片顶部增加一行“处置理由”摘要。 - - 形式建议:保留现有风险标签,但旁边用短句说明主要触发原因,例如“举报 2 次,建议先查看详情”或“过时 3 次,可关闭任务”。 - - 好处:管理员不用在风险标签和 signal-grid 之间来回比对,直接理解为什么这条排在队列里。 - -2. 调整卡片内的权重顺序:标题和处置理由优先,完整 meta 后置。 - - 标题下方仍展示正文摘要;地点、发布者、发布时间、有效期保持两列网格,但视觉上继续弱化。 - - 对管理决策最有用的“举报/过时/确认”可从大块三等分改为更轻的 `SignalPill`,减少卡片高度,同时突出非零风险项。 - -3. 操作按钮按风险状态给出更明确的主次。 - - 默认主操作建议只有一个:高举报优先“隐藏”,高过时优先“关闭”,其他状态优先“查看详情”。 - - 其他可用动作保留为次级按钮,避免每张卡都像有三个同等紧急的选择。 - - 按钮文案可从“关闭”微调为“关闭任务”,从“隐藏”微调为“隐藏内容”,降低误操作含义不清的问题。 - -4. 筛选区保持现有复杂度,但强化当前队列语义。 - - 搜索框和筛选 chip 已足够,不建议新增排序、批量操作或二级筛选。 - - 可以在“处置队列”副文案中说明当前排序依据,例如“优先显示举报多、过时多的内容”,与管理员心理模型对齐。 - -5. 用户反馈区做轻量收敛策略。 - - 反馈卡片保持当前结构即可;若反馈数较多,建议默认只展示最近 2-3 条,并提供“查看全部反馈”的入口。 - - 这样不会让任务处置队列被反馈列表长期挤到屏幕下方,也不需要引入复杂 tabs。 - -## 4. 文案语气建议 - -- 保持中文、克制、可信,不用刺激性词汇。推荐“待处理”“建议复核”“可能过时”“已隐藏”“读取失败”,避免“违规”“危险”“严重”等缺少依据的定性。 -- 动作文案要说明对象:用“关闭任务”“隐藏内容”“查看详情”,少用单字或泛化动作。 -- 风险说明以事实为主:例如“收到 2 次举报”“已有 3 人标记过时”,避免“高危内容”“异常用户”这类判断。 -- 错误和空状态继续采用温和语气:例如“暂时没有匹配内容”“反馈读取失败,请稍后刷新”,不要把系统问题归因给用户。 - -## 5. 移动端风险 - -- 小屏:四列统计卡在窄屏上数字可读,但标签空间紧;如果数字超过两位,需要确认不会压缩到难以扫读。 -- 长标题:标题当前无明确行数限制,长标题会推高卡片并挤压后续信息;建议最多两行,正文摘要继续截断。 -- 长 ID:`post-id` 固定右侧且不换行,长 ID 可能压缩左侧标签;建议对 ID 做中间省略或只展示后 6-8 位。 -- 按钮挤压:三枚等宽按钮在 320px 等效宽度下容易显得拥挤,尤其“查看详情 / 关闭任务 / 隐藏内容”同时出现时;建议主按钮占更大权重,次级动作可缩短或收进更多操作。 -- 长地点/发布者:meta 当前单行省略是正确方向,但要确认长中文地名、英文昵称、手机号式联系方式不会撑破两列布局。 diff --git a/harness/admin-product-brief.md b/harness/admin-product-brief.md deleted file mode 100644 index c64007b..0000000 --- a/harness/admin-product-brief.md +++ /dev/null @@ -1,38 +0,0 @@ -# admin-001 产品迭代简报 - -## 核心场景与最高价值目标 - -管理员每天快速扫一遍风险任务:先处理被举报、疑似过时、已过期的内容,再决定隐藏、关闭或继续观察。本轮最高价值目标是让管理员在 1 分钟内看懂风险原因并完成低误伤处置,而不是扩展成完整后台系统。 - -## 已有能力 - -- 管理员权限校验:普通用户进入管理页会被提示并跳回“我的”页。 -- 风险队列:已有待处理、全部、有举报、过时、有效、已关闭、已隐藏筛选。 -- 搜索:支持按标题、地点、发布者、ID 等搜索。 -- 风险排序与标识:按举报、过时、过期风险排序,并展示高优先级、需复核、可能过时等标签。 -- 处置动作:已有“查看详情”“关闭”“隐藏”,且关闭/隐藏前已有确认弹窗。 -- 辅助信息:已展示地点、发布者、发布时间、有效期、确认数、过时数、举报数和用户反馈列表。 - -## 建议交给开发落地的可验证点 - -1. 风险解释更具体:每张任务卡直接展示触发原因文案,例如“2 次举报,达到隐藏阈值”“3 次过时,建议关闭或标记过时”,让管理员不用猜标签来源。 -2. 处置效率更高:待处理队列默认只展示需要动作的任务,并在卡片顶部突出“建议动作:隐藏 / 关闭 / 复核”,与现有筛选和排序复用。 -3. 误操作防护更明确:隐藏和关闭弹窗补充任务标题、ID、影响范围;隐藏使用更强确认文案,避免把“关闭”误当“隐藏”。 -4. 处置后反馈可见:隐藏或关闭成功后保留当前筛选与搜索,并出现成功提示;该任务从待处理队列移出或状态立即更新。 -5. 权限异常不崩溃:`getMyRole` 或 admins 集合缺失时展示“无法校验管理员身份”的温和状态,不进入空白页或无限跳转。 - -## 明确不做 - -- 不做多管理员角色、审批流、批量处理、封禁用户、内容机器审核。 -- 不新增后台服务或外部依赖,优先复用现有本地/云函数边界。 -- 不改普通用户举报、过时、确认规则。 -- 不重做管理台信息架构和视觉体系。 - -## 可验收清单 - -- 普通用户打开管理 tab,没有管理能力入口并被正确引导。 -- 管理员打开管理 tab,待处理、搜索、风险筛选都可用。 -- reported 任务卡能说明举报次数和建议动作;执行隐藏后普通地图列表不再显示。 -- stale 或 active 任务执行关闭后,详情页状态同步为 resolved。 -- 隐藏、关闭弹窗包含任务标题/ID/影响说明,取消不会改变数据。 -- admins 集合缺失或角色接口失败时,小程序不崩溃,并给出可理解提示。 diff --git a/harness/candidate-publish-trust-report.md b/harness/candidate-publish-trust-report.md deleted file mode 100644 index 3e02c11..0000000 --- a/harness/candidate-publish-trust-report.md +++ /dev/null @@ -1,48 +0,0 @@ -# F 组组合候选报告 - -日期:2026-06-13 - -说明:这是历史候选分支报告,不是当前发布页手测清单;当前 UI 已移除单独的发布准备度清单卡片,发布状态由定位块和底部主按钮承载。 - -分支:`codex/iter-candidate-publish-trust` - -基础:`codex/iter-publish-flow` HEAD `2187007` - -叠加:`codex/iter-detail-trust` 提交 `77f2387`、`46552d5`、`4637a2a` - -## 组合目标 - -把当前评分最高的 B 组发布准备度和 C 组详情信任解释放到同一个候选分支,验证二者是否能共存,并为后续手测提供一个更接近合入候选的版本。 - -## 已包含能力 - -- 发布页准备度清单、标题/详情计数、位置确认、定位失败/重试文案、有效期直接按钮。 -- 发布主动作由 `primaryAction` 状态机驱动,区分登录、补字段、确认位置、等待定位、发布和提交中。 -- 详情页新增 TrustInsight 面板,在信任动作前解释确认、过时、举报和评论数。 -- 评论区标题右侧新增写评论入口,保留原有悬浮评论按钮。 -- 设计系统同时记录 `PublishReadiness`、`LocationCheck` 和 `TrustInsight` 组件规则。 - -## 运行过的验证 - -- `node --check pages/publish/publish.js` -- `node --check pages/publish/publish-state.js` -- `node --check pages/detail/detail.js` -- `node --check utils/format.js` -- `node --check harness/check-trust-insight.mjs` -- `node --no-warnings scripts/check-publish-flow.mjs` -- `node harness/check-trust-insight.mjs` -- `node scripts/check-json.mjs` -- `node harness/check-harness.mjs` -- `git diff --check` -- WeChat DevTools local `wcc` compiled all WXML files with exit code 0. -- WeChat DevTools local `wcsc -lc` compiled all WXSS files with exit code 0. - -## 未验证项 - -- 未在 WeChat DevTools 或真机中验证发布定位授权、键盘安全区、图片上传失败回滚、发布后详情跳转。 -- 未验证详情页 TrustInsight 在窄屏、评论入口、信任动作刷新、resolved/expired 只读状态和云端评论路径中的真实表现。 -- DevTools CLI `open`/`preview` 在 B 组已记录受 9420 服务端口超时阻塞;本组合分支尚未再次尝试 CLI 预览。 - -## 初步判断 - -代码层面 B+C 可以组合,当前自动验证未发现冲突。合入优先级仍取决于 B 组的真实发布手测是否通过;若发布链路通过,F 组比单独 B 更适合作为候选,因为它同时补齐发布前准备和详情后判断。 diff --git a/harness/check-harness.mjs b/harness/check-harness.mjs deleted file mode 100644 index b42072d..0000000 --- a/harness/check-harness.mjs +++ /dev/null @@ -1,75 +0,0 @@ -import { existsSync, readFileSync } from 'node:fs'; - -const requiredFiles = [ - 'AGENTS.md', - 'harness/README.md', - 'harness/init.sh', - 'harness/feature_list.json', - 'harness/claude-progress.md', - 'harness/session-handoff.md', - 'harness/clean-state-checklist.md', - 'harness/evaluator-rubric.md', - 'harness/quality-document.md' -]; - -const forbiddenRootHarnessFiles = [ - 'init.sh', - 'feature_list.json', - 'claude-progress.md', - 'session-handoff.md', - 'clean-state-checklist.md', - 'evaluator-rubric.md', - 'quality-document.md' -]; - -const allowedStatuses = new Set(['not_started', 'in_progress', 'blocked', 'passing']); - -function assert(condition, message) { - if (!condition) { - throw new Error(message); - } -} - -for (const file of requiredFiles) { - assert(existsSync(file), `Missing required harness file: ${file}`); -} - -for (const file of forbiddenRootHarnessFiles) { - assert(!existsSync(file), `Harness file should live under harness/, not repo root: ${file}`); -} - -const agents = readFileSync('AGENTS.md', 'utf8'); -assert(agents.includes('bash harness/init.sh'), 'AGENTS.md must point agents at bash harness/init.sh'); -assert(agents.includes('harness/feature_list.json'), 'AGENTS.md must mention harness/feature_list.json'); -assert(agents.includes('harness/claude-progress.md'), 'AGENTS.md must mention harness/claude-progress.md'); - -const progress = readFileSync('harness/claude-progress.md', 'utf8'); -assert(progress.includes('## 当前已验证状态'), 'claude-progress.md must include current verified state'); -assert(progress.includes('## 会话记录'), 'claude-progress.md must include session records'); - -const featureList = JSON.parse(readFileSync('harness/feature_list.json', 'utf8')); -assert(featureList.project, 'feature_list.json must include project'); -assert(featureList.last_updated, 'feature_list.json must include last_updated'); -assert(Array.isArray(featureList.features), 'feature_list.json must include features array'); - -let activeCount = 0; -for (const feature of featureList.features) { - assert(feature.id, 'Every feature must include id'); - assert(Number.isInteger(feature.priority), `${feature.id} priority must be an integer`); - assert(feature.area, `${feature.id} must include area`); - assert(feature.title, `${feature.id} must include title`); - assert(feature.user_visible_behavior, `${feature.id} must include user_visible_behavior`); - assert(allowedStatuses.has(feature.status), `${feature.id} has invalid status: ${feature.status}`); - assert(Array.isArray(feature.verification) && feature.verification.length > 0, `${feature.id} needs verification steps`); - assert(Array.isArray(feature.evidence), `${feature.id} evidence must be an array`); - if (feature.status === 'in_progress') { - activeCount += 1; - } - if (feature.status === 'passing') { - assert(feature.evidence.length > 0, `${feature.id} is passing but has no evidence`); - } -} - -assert(activeCount <= 1, 'Only one feature can be in_progress'); - -console.log(`Harness OK: ${featureList.features.length} features checked.`); diff --git a/harness/ci-readiness-gate-checklist.md b/harness/ci-readiness-gate-checklist.md deleted file mode 100644 index 859c3d3..0000000 --- a/harness/ci-readiness-gate-checklist.md +++ /dev/null @@ -1,75 +0,0 @@ -# CI Readiness Gate Checklist - -## 目标 - -AD 轮要把 AC 轮已有的 `npm run check` 从本地常用命令推进到 GitHub Actions 自动门禁。这个门禁只覆盖 JSON、harness、readiness 和 ignored local blocked summary preflight,不代表地图列表或小程序 UI 已经通过 DevTools/真机验收。 - -## Workflow 结构 - -- [ ] 仓库存在 GitHub Actions workflow 文件,本轮路径为 `.github/workflows/readiness.yml`。 -- [ ] workflow 使用 `on: push` 触发,确保分支推送会自动运行门禁。 -- [ ] workflow 使用 `on: pull_request` 触发,确保合入前会自动运行门禁。 -- [ ] job 中使用 `actions/setup-node` 配置 Node 环境。 -- [ ] job 中运行 `npm ci`,避免跳过锁文件安装路径。 -- [ ] job 中运行 `npm run check`,且不拆成只跑 `npm run check:json` 的弱化版本。 -- [ ] workflow 不读取、声明或打印任何 secrets。 -- [ ] workflow 不上传、提交、缓存或引用 `harness/manual-test-results.local*.json`、`harness/manual-test-summary.local*.md` 等 ignored local evidence。 - -## 本地正向验证 - -- [ ] `npm run check` 在无 local blocked summary 时通过。 -- [ ] 生成一对 ignored local blocked summary/result 后,`npm run check` 仍通过,并能看到 blocked summary preflight 检查到该 pair。 -- [ ] `npm run check` 输出中仍保留“不代表 DevTools/真机 UI passed”的口径。 - -建议正向命令: - -```bash -npm run check -node scripts/prepare-map-list-blocked-summary.mjs \ - --reason "DevTools service port blocked; map-list visual smoke was not executed." \ - --results-out harness/manual-test-results.local-ad-ci.json \ - --summary-out harness/manual-test-summary.local-ad-ci.md \ - --force -npm run check -rm -f harness/manual-test-results.local-ad-ci.json harness/manual-test-summary.local-ad-ci.md -``` - -## 负向验证 - -- [ ] 生成 local blocked summary/result 后删除 matching results JSON,`npm run check` 必须失败并提示缺失 results JSON。 -- [ ] 重新生成 local blocked summary/result 后,把 summary 的 `map-list-visual-smoke` 行改成 `passed`,`npm run check` 必须失败。 -- [ ] 负向验证结束后清理所有 `harness/manual-test-results.local-ad-ci*.json` 和 `harness/manual-test-summary.local-ad-ci*.md` 本地产物。 - -建议负向命令: - -```bash -node scripts/prepare-map-list-blocked-summary.mjs \ - --reason "DevTools service port blocked; map-list visual smoke was not executed." \ - --results-out harness/manual-test-results.local-ad-ci.json \ - --summary-out harness/manual-test-summary.local-ad-ci.md \ - --force -rm -f harness/manual-test-results.local-ad-ci.json -npm run check - -node scripts/prepare-map-list-blocked-summary.mjs \ - --reason "DevTools service port blocked; map-list visual smoke was not executed." \ - --results-out harness/manual-test-results.local-ad-ci.json \ - --summary-out harness/manual-test-summary.local-ad-ci.md \ - --force -# 手工把 harness/manual-test-summary.local-ad-ci.md 的 map-list-visual-smoke 状态改成 passed。 -npm run check -rm -f harness/manual-test-results.local-ad-ci.json harness/manual-test-summary.local-ad-ci.md -``` - -## 报告口径 - -- [ ] 评测报告只能写 CI/readiness/static/preflight gate 通过,不能写 UI passed、DevTools passed 或真机 passed。 -- [ ] 如果 workflow 通过,只能说明 `npm run check` 自动化路径可运行,不能说明地图列表抽屉、原生地图层、safe area、滚动、长标题、带图卡片或详情跳转已视觉验收。 -- [ ] 如果 workflow 失败,需要先定位是安装、JSON、harness、readiness 还是 blocked summary preflight 阻断,再记录具体错误输出。 - -## 未验证项 - -- [ ] 未验证真实 WeChat DevTools 编译和模拟器运行。 -- [ ] 未验证真机定位授权、拒绝、超时和重试路径。 -- [ ] 未验证地图列表真实视觉 smoke,包括 safe area、原生地图层覆盖、长标题/长正文、带图/无图、滚动和详情入口。 -- [ ] 未验证 GitHub Actions 在远端仓库实际触发后的日志和分支保护配置。 diff --git a/harness/ci-readiness-gate-product-brief.md b/harness/ci-readiness-gate-product-brief.md deleted file mode 100644 index 91cfa3e..0000000 --- a/harness/ci-readiness-gate-product-brief.md +++ /dev/null @@ -1,31 +0,0 @@ -# AD 轮产品 Brief:CI readiness gate - -## 目标 - -新增一个最小 CI workflow,在 PR 或 push 时自动运行 `npm run check`,把 AC 轮已有的 JSON、harness、readiness 和 blocked summary preflight 检查接入自动化门禁。 - -AD 轮只把现有检查放到 CI 默认路径里,不重新定义检查内容。CI 的职责是减少“本地忘记运行 `npm run check`”的风险,并让评审者能从 workflow 结果看到基础准入是否通过。 - -## 用户价值 - -- 开发者和 agent 推送后可以自动获得基础质量反馈,不必完全依赖手工记忆。 -- 评审者可以优先查看 CI 是否运行过 `npm run check`,降低只跑局部检查就进入评审的风险。 -- blocked summary preflight 会随 readiness 一起进入自动化路径,避免 ignored local summary 被改坏后仍被引用。 -- 继续复用 AC 的 npm 入口,CI、人工本地验证和未来自动化保持同一条命令。 - -## 验收标准 - -- 新增最小 GitHub Actions workflow,触发范围覆盖 pull request 和 push。 -- workflow 只需要安装 Node 依赖并运行 `npm run check`,不新增额外业务检查或 UI smoke 断言。 -- CI 失败时应阻止把该次基础 readiness 视为通过;失败来源仍由 `npm run check` 下游脚本输出。 -- 无 ignored local evidence 文件时,CI 应能通过现有仓库基线。 -- 如存在被错误提交的 local summary 或 matching results 问题,`npm run check` 应通过现有 preflight 失败,而不是把它当作可提交证据。 -- 主 agent 验证应至少覆盖 `npm run check` 本地通过、workflow YAML 结构可读、以及不提交 ignored local evidence。 - -## 非目标 - -- 不恢复 WeChat DevTools 9420 服务端口,不改变 DevTools 打开、预览或真机调试流程。 -- CI 运行 `npm run check` 不等于真实 UI passed、DevTools passed 或真机 passed。 -- 不提交 `harness/manual-test-results.local*.json`、`harness/manual-test-summary.local*.md` 等 ignored local evidence。 -- 不新增页面 UI、业务逻辑、云函数、数据模型或 npm 依赖。 -- 不引入复杂矩阵、缓存策略、部署、制品上传或强制 hook;本轮只需要最小 CI 门禁。 diff --git a/harness/ci-required-check-runbook-checklist.md b/harness/ci-required-check-runbook-checklist.md deleted file mode 100644 index f4ac26b..0000000 --- a/harness/ci-required-check-runbook-checklist.md +++ /dev/null @@ -1,32 +0,0 @@ -# CI Required Check Runbook QA Checklist - -## 验收目标 - -确认 AE 轮 runbook 能把 AD 轮新增的 GitHub Actions 门禁推进到可执行的远端验证流程:本地 `npm run check` 仍是基础,但最终必须能说明远端 workflow 是否触发、required check 的实际名称是什么、branch protection 如何配置,以及哪些 UI/真机项仍未验证。 - -## 必跑检查 - -- [ ] 本地执行 `npm run check`,确认它覆盖 JSON、harness、readiness,以及 ignored local blocked summary preflight。 -- [ ] 检查 `.github/workflows/readiness.yml` 可解析且保持最小门禁:`push` / `pull_request` 触发、`contents: read`、Node 20、`npm ci --ignore-scripts`、`npm run check`。 -- [ ] runbook 必须包含远端验证步骤:推送 AE 分支或打开 PR 后,在 GitHub Actions / PR Checks 页面确认 `Readiness checks` workflow 已实际运行。 -- [ ] runbook 必须要求记录 required check 的实际显示名称;候选名称是 `readiness / npm run check` 或 GitHub UI 中与 `Readiness checks` workflow 组合显示的等价名称,最终以 GitHub PR Checks 页面显示为准。 -- [ ] runbook 必须包含 branch protection 配置步骤:进入仓库 Settings -> Branches,选择目标分支规则,启用 required status checks,并勾选上一步确认的 required check 名称。 -- [ ] runbook 必须说明配置后要用一个测试 PR 验证阻断效果:required check 未完成或失败时不能合并,通过后才允许合并。 -- [ ] runbook 必须保留未验证口径:远端 Actions 触发、required check 名称、branch protection 生效状态如果未在 GitHub 上实际验证,只能标为未验证,不能写成已完成。 -- [ ] runbook 和报告中不能写 “UI passed”、真机通过、DevTools 视觉 smoke 通过,除非有真实 DevTools 或真机证据。 - -## 负向检查 - -- [ ] 如果 runbook 只写本地 `npm run check`,没有远端 Actions / PR Checks 验证步骤,应判定不通过。 -- [ ] 如果 runbook 写死 required check 名称但没有要求从 PR Checks 页面确认实际名称,应判定有风险。 -- [ ] 如果 runbook 只说“配置分支保护”,没有写启用 required status checks 和选择 check 名称的步骤,应判定不通过。 -- [ ] 如果 runbook 声称 CI 通过等同于地图列表 UI 通过、DevTools 通过或真机通过,应判定不通过。 -- [ ] 如果 runbook 没有列出未验证项,应判定不通过。 - -## 未验证项 - -- 远端 GitHub Actions 是否已在 AE 分支或 PR 上实际触发。 -- PR Checks 页面显示的 required check 最终名称。 -- 目标分支的 branch protection 是否已经配置并强制 required check。 -- required check 失败时是否真实阻止合并。 -- WeChat DevTools 地图列表视觉 smoke、真机定位授权、真机交互路径。 diff --git a/harness/ci-required-check-runbook-product-brief.md b/harness/ci-required-check-runbook-product-brief.md deleted file mode 100644 index 1627aa7..0000000 --- a/harness/ci-required-check-runbook-product-brief.md +++ /dev/null @@ -1,31 +0,0 @@ -# AE 轮产品 Brief:CI required check runbook - -## 目标 - -在 AD 轮已有最小 GitHub Actions workflow 后,补充一份可执行 runbook,说明如何验证远端 Actions 是否真实触发,以及如何把稳定的 workflow/check 名称配置为分支保护 required check。 - -AE 轮的重点是把“远端验证和分支保护配置仍未完成”显式化,并让后续执行者按步骤确认,而不是把本地 workflow 文件存在误读为远端已经通过或分支保护已经生效。 - -## 用户价值 - -- 维护者能按 runbook 检查 PR/push 是否真的触发远端 Actions,而不是只相信本地 YAML 解析通过。 -- 评审者能知道应该引用哪个 workflow/check 名称配置 required check,减少分支保护名称漂移带来的误配风险。 -- 后续 agent 可以清楚记录远端 run URL、结论和未完成项,避免把“待配置”写成“已配置”。 -- 本地 `npm run check`、CI workflow 和分支保护之间的责任边界更清楚:本地检查证明静态/readiness gate 可运行,远端 run 证明 GitHub Actions 触发,required check 证明合并门禁被仓库设置采用。 - -## 验收标准 - -- 新增 runbook 文档,包含远端 Actions 触发验证步骤:推送分支或打开 PR、查看 workflow run、确认运行的是 AD/AE 期望的 workflow、确认执行 `npm run check`。 -- runbook 必须写明应记录的证据:workflow run URL、分支或 PR、触发事件、最终状态、失败时的下一步。 -- runbook 必须说明如何配置分支保护 required check,并要求引用稳定、易识别的 workflow/check 名称。 -- runbook 必须要求配置后再用一次测试 PR 验证 required check 会阻止未通过检查的合并。 -- 文档口径必须明确:runbook 只是远端验证和配置步骤,不声称当前仓库已经远端通过,也不声称分支保护已经配置完成。 -- 文档口径必须明确:CI/readiness/required check 通过不等于真实 UI passed、DevTools passed 或真机 passed。 - -## 非目标 - -- 不在 AE 产品 brief 中直接修改 `.github`、`package.json`、脚本、README 或 harness 状态文件。 -- 不假设当前远端仓库已启用 GitHub Actions、已执行成功,或已配置分支保护 required checks。 -- 不恢复 WeChat DevTools 9420 服务端口,不新增真实 UI smoke、真机验收或截图证据。 -- 不提交 ignored local evidence;`harness/manual-test-results.local*.json` 和 `harness/manual-test-summary.local*.md` 仍只用于本地阻塞证据演练。 -- 不把 runbook 当成自动执行器、CI hook 或仓库设置 API;它只定义人工可执行、可记录的操作流程。 diff --git a/harness/ci-required-check-runbook.md b/harness/ci-required-check-runbook.md deleted file mode 100644 index 790893c..0000000 --- a/harness/ci-required-check-runbook.md +++ /dev/null @@ -1,61 +0,0 @@ -# CI Required Check Runbook - -## Purpose - -This runbook explains how to verify that the remote GitHub Actions readiness workflow actually runs, how to identify the required check name, and how to configure that check in branch protection. - -Local `npm run check`, this workflow, and branch protection are separate gates. Passing any of them does not prove WeChat DevTools, real-device UI, or map-list visual acceptance passed. - -## Stable Check Target - -- Workflow file: `.github/workflows/readiness.yml` -- Workflow name: `Readiness checks` -- Job id: `readiness` -- Job display name: `readiness / npm run check` -- Command guarded by the job: `npm run check` - -Use the exact check name shown by GitHub when configuring branch protection. GitHub may display the check as the job display name alone or together with the workflow name, depending on the repository UI. - -## Verify Remote Workflow Trigger - -1. Push the branch containing `.github/workflows/readiness.yml`, or open/update a pull request from that branch. -2. Open the repository's GitHub Actions tab and select `Readiness checks`. -3. Confirm the run is for the expected branch or pull request and trigger event (`push` or `pull_request`). -4. Open the run and confirm the `readiness / npm run check` job executed `npm ci --ignore-scripts` and `npm run check`. -5. Record the workflow run URL, branch or PR, trigger event, commit SHA, final status, and any failure summary. - -Do not record the workflow as verified until a remote run exists and its final status has been checked in GitHub. - -## Configure Branch Protection - -1. In GitHub repository settings, open Branches and edit the target branch protection rule. -2. Enable required status checks before merging. -3. Search for and select the exact readiness check reported by a completed remote run, expected around `readiness / npm run check`. -4. Save the rule. -5. Open or update a test pull request and confirm GitHub marks that check as required. -6. If practical, verify a failing run blocks merge and a passing run clears the required check. - -Do not mark branch protection as configured until the repository settings show the readiness check as required and a pull request displays it as a required check. - -## Evidence Log Template - -```text -Status: unverified | verified | blocked -Repository: -Branch or PR: -Commit SHA: -Trigger event: -Workflow run URL: -Observed check name: -Required check configured: yes | no | blocked -Result: -Next action: -``` - -## Reporting Rules - -- If there is no remote workflow run URL, report the workflow as local-only and unverified. -- If branch protection has not been inspected or changed in GitHub settings, report required check configuration as unverified. -- If the check is configured but no pull request has shown it as required, report branch protection behavior as partially verified. -- If `npm run check` passes in CI, report only static/readiness gates as passed. -- Keep DevTools, real-device UI, and visual smoke evidence separate from CI readiness evidence. diff --git a/harness/claude-progress.md b/harness/claude-progress.md deleted file mode 100644 index 4fba25b..0000000 --- a/harness/claude-progress.md +++ /dev/null @@ -1,1422 +0,0 @@ -# 进度日志 - -## 当前已验证状态 - -- 仓库根目录:`/Users/bytedance/git/x` -- 标准启动路径:在 WeChat DevTools 中打开仓库,使用 `project.config.json`,公开 appid 保持 `touristappid` -- 标准初始化入口:`bash harness/init.sh` -- 标准基础验证:`npm run check:json`,`node harness/check-harness.mjs` -- 当前最高优先级未完成功能:`map-feed-001` -- 当前正在实现的用户请求:全量性能优化和安全性检查,优先修复高置信、低风险问题并记录验证证据 -- 当前 blocker:静态门禁已通过;WeChat DevTools service port 9420 仍不可连接,因此真实 DevTools/真机视觉和点击验收尚未执行 - -## 会话记录 - -### Session 001 - -- 日期:2026-05-13 -- 本轮目标:按 Learn Harness Engineering 中文教程和模板,为本仓库建立最小可用 harness;除根 `AGENTS.md` 外,新增文件都放在 `harness/` -- 已完成:已创建 harness 入口、功能清单、进度日志、交接、收尾、评审和质量快照;根 `AGENTS.md` 已指向 `harness/` -- 运行过的验证:`npm run check:json`;`node harness/check-harness.mjs`;`bash harness/init.sh` -- 已记录证据:`npm run check:json` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`bash harness/init.sh` 完整跑通,`npm ci --ignore-scripts` 报告 up to date,随后两条验证通过 -- 提交记录:未提交 -- 更新过的文件或工件:`AGENTS.md`,`harness/*` -- 已知风险或未解决问题:工作树在本轮开始前已有大量业务改动,本轮不归属这些改动;小程序用户流程尚未经 WeChat DevTools 手动验证 -- 下一步最佳动作:从 `map-feed-001` 开始做 WeChat DevTools 手动验证,补充地图首页证据 - -### Session 002 - -- 日期:2026-05-14 -- 本轮目标:对任务详情添加评论功能 -- 已完成:为详情页新增评论区、空内容/长度校验、评论列表展示;详情页已移除内嵌地图和独立指标卡,确认/过时/举报改为一行三列并在控件上直接显示数量;评论列表默认不展示发布面板,右下方悬浮按钮会打开底部评论弹窗;`utils/store.js` 新增本地 `post_comments` 存储和 CloudBase fallback;`cloudfunctions/posts` 新增 `listComments` 与 `createComment` action -- 运行过的验证:`node --check utils/store.js`;`node --check pages/detail/detail.js`;`node --check cloudfunctions/posts/index.js`;`npm run check:json`;`node harness/check-harness.mjs`;`bash harness/init.sh` -- 已记录证据:三条 `node --check` 均通过,无语法错误输出;`npm run check:json` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`bash harness/init.sh` 完整跑通,`npm ci --ignore-scripts` 报告 up to date,随后两条验证通过;紧凑详情布局和悬浮评论弹窗分别改完后再次跑同一组验证仍通过 -- 更新过的文件或工件:`utils/store.js`,`cloudfunctions/posts/index.js`,`pages/detail/detail.js`,`pages/detail/detail.wxml`,`pages/detail/detail.wxss`,`README.md`,`TODOS.md`,`harness/feature_list.json` -- 已知风险或未解决问题:评论用户流程仍需 WeChat DevTools 手动验证;云端使用前需要部署更新后的 `posts` 云函数并准备 `post_comments` 集合,未部署时客户端会回落本地评论存储 -- 下一步最佳动作:在 WeChat DevTools 中验证 active、resolved/expired、游客和登录用户评论流程,并确认详情页无地图、信任动作一行展示、右下悬浮评论按钮和弹窗发布体验正常 - -### Session 003 - -- 日期:2026-05-14 -- 本轮目标:修复“反馈建议只存在用户本地,管理员无法集中查看”的产品缺口 -- 已完成:`utils/feedback.js` 改为调用 `posts` 云函数的 `createFeedback`/`listFeedback` action;`cloudfunctions/posts` 新增 `feedback_items` 集合写入和管理员读取;反馈提交页改为等待云端结果,失败时提示;管理台异步读取反馈并在云函数或集合配置异常时显示错误;README 补充 CloudBase 集合要求 -- 运行过的验证:`node --check utils/feedback.js`;`node --check pages/feedback/feedback.js`;`node --check pages/admin/admin.js`;`node --check cloudfunctions/posts/index.js`;`git diff --check`;`npm run check:json`;`node harness/check-harness.mjs`;`bash harness/init.sh` -- 已记录证据:四条 `node --check` 均通过,无语法错误输出;`git diff --check` 通过;`npm run check:json` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`bash harness/init.sh` 完整跑通,依赖 up to date,随后 JSON 和 harness 自检通过 -- 更新过的文件或工件:`utils/feedback.js`,`pages/feedback/feedback.js`,`pages/admin/admin.js`,`pages/admin/admin.wxml`,`cloudfunctions/posts/index.js`,`utils/config.js`,`README.md`,`harness/claude-progress.md` -- 已知风险或未解决问题:尚未在 WeChat DevTools 中提交真实反馈;尚未部署更新后的 `posts` 云函数;CloudBase 需要存在 `feedback_items` 集合,否则提交/管理台会显式失败而不是静默落本地 -- 下一步最佳动作:部署 `posts` 云函数并创建 `feedback_items` 集合后,用普通用户提交反馈,再用管理员账号打开管理台确认能看到该反馈 - -### Session 004 - -- 日期:2026-05-15 -- 本轮目标:修复发布图片只存在本地、其他用户浏览不到的问题,同时限制图片存储压力 -- 已完成:发布页选图改为最多 4 张,压缩后单张需小于 1.5MB;图片提交时必须上传 CloudBase Storage 并保存 `cloud://` fileID;图片帖子设置 `requireCloud`,云端帖子保存失败时不再回退成本地帖子;定位先于上传执行,上传后若帖子保存失败会清理新上传的云文件并重置图片项;`posts` 云函数图片数量上限同步为 4,并在 `prepareUpload` 返回大小上限;README 补充图片云存储要求;`harness/init.sh` 支持当前无 npm 且无依赖的 Codex 环境 -- 运行过的验证:`node --check pages/publish/publish.js`;`node --check utils/store.js`;`node --check cloudfunctions/posts/index.js`;`git diff --check`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`bash harness/init.sh` -- 已记录证据:三条 `node --check` 均通过,无语法错误输出;`git diff --check` 通过;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`bash harness/init.sh` 在 npm 不存在但 package-lock 无外部依赖时跳过安装,随后 JSON 和 harness 自检通过 -- 更新过的文件或工件:`pages/publish/publish.js`,`pages/publish/publish.wxml`,`utils/store.js`,`cloudfunctions/posts/index.js`,`README.md`,`harness/init.sh`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:尚未在 WeChat DevTools 或真机中实际选择图片发布;尚未部署更新后的 `posts` 云函数;CloudBase Storage 读权限和跨用户图片显示仍需线上验证 -- 下一步最佳动作:部署 `posts` 云函数后,用用户 A 发布含图片任务,再用用户 B 打开地图/详情确认能看到图片,并尝试上传超大图片确认被拦截 - -### Session 005 - -- 日期:2026-05-18 -- 本轮目标:按照小程序/TDesign 式设计系统改进现有 UI,降低 AI 生成页面的模板感和旧感 -- 已完成:新增 `DESIGN_SYSTEM.md`,明确本项目的用途、视觉方向、TDesign 策略、语义色、字号和 QA 清单;统一全局背景、卡片、标签、按钮、表单控件和语义状态色;优化自定义 tabBar 为浮动胶囊式底栏;重做地图页列表入口、工具按钮、选中卡片和列表抽屉样式;同步刷新详情、发布、反馈、管理、我的、我的发布和参与记录页面的主要视觉样式 -- 运行过的验证:`node scripts/check-json.mjs`;`git diff --check`;`node harness/check-harness.mjs`;`bash harness/init.sh` -- 已记录证据:`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`git diff --check` 通过,无输出;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`bash harness/init.sh` 完整跑通,npm-free fallback 跳过安装,随后 JSON 和 harness 自检通过 -- 更新过的文件或工件:`DESIGN_SYSTEM.md`,`app.json`,`app.wxss`,`custom-tab-bar/index.wxml`,`custom-tab-bar/index.wxss`,`pages/map/map.wxml`,`pages/map/map.wxss`,`pages/detail/detail.wxml`,`pages/detail/detail.wxss`,`pages/publish/publish.wxss`,`pages/feedback/feedback.wxss`,`pages/admin/admin.wxss`,`pages/me/me.wxss`,`pages/my-posts/my-posts.wxss`,`pages/activities/activities.wxss`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:没有安装 `tdesign-miniprogram` 依赖,原因是当前仓库没有小程序 npm 构建链路且本地 baseline 会在 npm 不存在时跳过安装;本轮采用 TDesign 式 token 和控件规则落地。尚未在 WeChat DevTools 或真机中做截图验收,tabBar safe area、地图浮层和表单视觉仍需手动确认。 -- 下一步最佳动作:在 WeChat DevTools 中打开地图、发布、详情、我的和管理页,确认浮动 tab、地图抽屉、详情图片、评论弹窗和表单控件在常见手机宽度下无遮挡和溢出。 - -### Session 006 - -- 日期:2026-05-18 -- 本轮目标:修正底部 tabBar 悬浮导致表单页底部内容透出、主按钮被压在后方的视觉问题 -- 已完成:自定义 tabBar 从浮动胶囊改为贴底不透明导航栏,保留 active 背景和橙色短指示条;地图页工具按钮、选中任务卡和列表抽屉的底部偏移同步调整,列表抽屉贴到 tabBar 上沿;`DESIGN_SYSTEM.md` 增加“tabBar 贴底且不留透底间隙”的规则 -- 运行过的验证:`bash harness/init.sh`;`git diff --check`;`node harness/check-harness.mjs` -- 已记录证据:`bash harness/init.sh` 完整跑通,npm-free fallback 跳过安装,随后 JSON 和 harness 自检通过;`git diff --check` 通过,无输出;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.` -- 更新过的文件或工件:`custom-tab-bar/index.wxss`,`pages/map/map.wxss`,`DESIGN_SYSTEM.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:尚未在 WeChat DevTools/真机中重新截图确认发布页底部、地图抽屉和 safe area 表现。 -- 下一步最佳动作:在 WeChat DevTools 中打开发布页,确认底部按钮不再从 tabBar 下方透出;再打开地图页列表抽屉,确认抽屉底部与 tabBar 上沿衔接自然。 - -### Session 007 - -- 日期:2026-05-19 -- 本轮目标:调用 `design-ui-designer` skill,把上一轮 UI 优化方案落地到核心页面,优先让发布页和地图页产生可见质感变化 -- 已完成:新增全局 `BottomAction` 固定底部操作区样式;发布页改成“任务内容”和“分类与位置”两个 `FormSection`,分类从 picker 改为两列直接点选按钮,提交按钮固定在 tabBar 上方;地图列表抽屉新增 `DrawerCounter` 屏幕内计数,任务卡信号改成确认/过时/有效期三枚 `SignalPill`;`DESIGN_SYSTEM.md` 补充 BottomAction、FormSection、CategoryOption、DrawerCounter、SignalPill 组件规范 -- 运行过的验证:`node --check pages/publish/publish.js`;`node scripts/check-json.mjs`;`git diff --check`;`node harness/check-harness.mjs`;`bash harness/init.sh` -- 已记录证据:`node --check pages/publish/publish.js` 通过;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`git diff --check` 通过,无输出;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`bash harness/init.sh` 完整跑通,npm-free fallback 跳过安装,随后 JSON 和 harness 自检通过 -- 更新过的文件或工件:`app.wxss`,`pages/publish/publish.wxml`,`pages/publish/publish.wxss`,`pages/publish/publish.js`,`pages/map/map.wxml`,`pages/map/map.wxss`,`DESIGN_SYSTEM.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:尚未在 WeChat DevTools/真机中确认发布页固定底部操作区是否与键盘、tabBar、safe area 完全协调;地图抽屉新增计数和三指标信息栏仍需截图验收。 -- 下一步最佳动作:在 WeChat DevTools 中打开发布页逐项填写并滚动到底部,确认固定提交区不遮挡表单;打开地图页列表抽屉,确认任务卡三指标不挤压、不溢出。 - -### Session 008 - -- 日期:2026-05-21 -- 本轮目标:修复 UI 优化后小程序打开白屏/报错的问题 -- 已完成:检查项目日志、微信开发者工具本地日志、WXML/WXSS 全量本地编译和 JS 语法;未发现模板、样式或 JS 语法错误;将自定义 tabBar 中新增的空 `cover-view` 指示条、绝对定位和裁剪样式移除,保留贴底不透明导航栏与 active 背景,降低 `cover-view` 渲染兼容风险 -- 运行过的验证:微信开发者工具内置 `wcc` 全量编译 WXML;微信开发者工具内置 `wcsc -lc` 全量编译 WXSS;`node --check pages/map/map.js`;`node --check pages/publish/publish.js`;`node scripts/check-json.mjs`;`git diff --check`;`bash harness/init.sh` -- 已记录证据:全量 WXML/WXSS 本地编译均通过,无错误输出;两条 `node --check` 均通过,无语法错误输出;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`git diff --check` 通过,无输出;`bash harness/init.sh` 完整跑通,npm install up to date,随后 JSON 和 harness 自检通过 -- 更新过的文件或工件:`custom-tab-bar/index.wxml`,`custom-tab-bar/index.wxss`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:无法在当前安全约束下调用 DevTools preview 生成预览包;仍需用户在已打开的 WeChat DevTools 中重新编译确认白屏是否消失。若仍白屏,需要记录控制台第一条红色错误。 -- 下一步最佳动作:在 WeChat DevTools 中重新编译项目,先看地图 tab 是否恢复;再切到发布 tab,确认底部固定提交区和贴底 tabBar 正常显示。 - -### Session 009 - -- 日期:2026-05-21 -- 本轮目标:继续修复“打开页面依然白屏,并且没有日志可确认错误原因” -- 已完成:用 WeChat DevTools 直接确认模拟器先报 `TypeError: Cannot read property 'subPackages' of undefined`,该错误来自 DevTools hot reload/getAppConfig;新增 `utils/diagnostics.js` 记录运行诊断;`app.js` 对 CloudBase 初始化、用户初始化、全局错误和未处理 Promise 增加保护;`utils/store.js` 在云端读取失败时记录诊断并回落本地 mock;地图页新增启动诊断面板,启动失败时可直接在页面上看到最近错误;通过 DevTools UI 清除编译缓存、终止模拟器并重新编译后,地图页恢复显示,控制台清空后再次编译无调试器错误 -- 运行过的验证:`node --check app.js`;`node --check utils/diagnostics.js`;`node --check utils/store.js`;`node --check pages/map/map.js`;`node scripts/check-json.mjs`;`git diff --check`;微信开发者工具内置 `wcc` 全量编译 WXML;微信开发者工具内置 `wcsc -lc` 全量编译 WXSS;`node harness/check-harness.mjs`;`bash harness/init.sh`;WeChat DevTools 手动清编译缓存、终止模拟器、重新编译并观察地图页 -- 已记录证据:四条 `node --check` 均通过,无语法错误输出;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`git diff --check` 通过,无输出;全量 WXML/WXSS 本地编译均通过,无错误输出;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`bash harness/init.sh` 完整跑通,npm install up to date,随后 JSON 和 harness 自检通过;WeChat DevTools 地图页显示地图、底部 tabBar 和列表入口,页面路径为 `pages/map/map`;清空控制台后再次编译,调试器错误为 0 -- 更新过的文件或工件:`utils/diagnostics.js`,`app.js`,`utils/store.js`,`pages/map/map.js`,`pages/map/map.wxml`,`pages/map/map.wxss`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:DevTools CLI 清缓存需要开启服务端口,本轮没有开启该设置,改用 DevTools UI 完成清编译缓存;尚未真机验证定位授权、地图 marker 点击和发布页完整流程;云函数/CloudBase Storage 仍需部署后验证 -- 下一步最佳动作:在 DevTools 中点“列表”打开地图抽屉,再切到发布 tab 检查固定底部按钮和 tabBar 贴底效果;随后真机验证定位授权和云端数据路径 - -### Session 010 - -- 日期:2026-05-21 -- 本轮目标:修复清缓存后仍可能出现的 `Error: timeout`,并确认打开地图页不再白屏 -- 已完成:`utils/store.js` 新增 `listPosts(center, { localOnly: true })` 路径;地图首页首屏改为默认中心 + 本地帖子优先展示,不再启动时自动请求定位、不再启动时调用 CloudBase 列表、不再启动时读取 `mapContext.getRegion`;`map` 组件的 `show-location` 改为定位成功后才开启;地图抽屉文案从“屏幕内”调整为“附近”,避免取消首屏 region 读取后的语义不一致 -- 运行过的验证:`node --check utils/store.js`;`node --check pages/map/map.js`;`node scripts/check-json.mjs`;`git diff --check`;微信开发者工具内置 `wcc` 全量编译 WXML;微信开发者工具内置 `wcsc -lc` 全量编译 WXSS;`node harness/check-harness.mjs`;`bash harness/init.sh`;WeChat DevTools 清空控制台后重新编译地图页 -- 已记录证据:两条 `node --check` 均通过,无语法错误输出;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`git diff --check` 通过,无输出;`wcc` 和 `wcsc -lc` 全量编译退出码为 0;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`bash harness/init.sh` 完整跑通,npm install up to date,随后 JSON 和 harness 自检通过;WeChat DevTools 地图页显示地图、marker、底部 tabBar 和列表入口,清空控制台并重新编译后调试器显示 Errors: 0,未再出现 `Error: timeout` -- 更新过的文件或工件:`utils/store.js`,`pages/map/map.js`,`pages/map/map.wxml`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:定位按钮仍会在用户点击时调用 `wx.getLocation`,需要真机验证授权/拒绝/超时三种路径;地图首屏现在以本地 mock/storage 数据兜底,云端列表不会阻塞首屏,后续若要实时云端 feed 需要加后台刷新策略;DevTools 仍有热重载、SharedArrayBuffer、getSystemInfo 三条非阻塞警告 -- 下一步最佳动作:在真机或 DevTools 中点击“返回当前位置”验证定位授权分支;再打开列表抽屉和任务详情确认交互链路完整 - -### Session 011 - -- 日期:2026-05-21 -- 本轮目标:解释并修复“任务列表里好几个任务点击详情提示任务不存在” -- 已完成:确认根因是地图列表首屏使用本地 mock/storage 数据,而详情页 `getPost` 在 CloudBase 可用时只查云函数;当云端没有这些本地任务 id 时会返回 `post: null`,详情页因此显示不存在。`utils/store.js` 现在会先记住本地同 id 任务,云端 get 返回空或 `NOT_FOUND` 时回落本地;`NOT_FOUND` 也纳入评论/信任动作的本地 fallback 范围,保证本地列表任务进入详情后评论区和操作链路不再被云端缺失拦住。 -- 运行过的验证:`node --check utils/store.js`;`node --check pages/detail/detail.js`;`node scripts/check-json.mjs`;`git diff --check`;`bash harness/init.sh`;WeChat DevTools 从地图列表打开“测试”任务详情 -- 已记录证据:两条 `node --check` 均通过,无语法错误输出;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`git diff --check` 通过,无输出;`bash harness/init.sh` 完整跑通,npm install up to date,随后 JSON 和 harness 自检通过;WeChat DevTools 中打开列表后点击“测试”的“查看详情”,详情页显示任务标题、正文、状态、信任动作和评论区,没有再显示“任务不存在” -- 更新过的文件或工件:`utils/store.js`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:DevTools 仍会偶发输出 `WAServiceMainContext timeout`,当前观察它来自小程序/地图原生层而不是详情数据缺失;若要彻底压掉,需要继续单独排查地图组件本身或 DevTools 模拟器行为 -- 下一步最佳动作:再点列表里的失物招领、求助问答、地点动态各一条进入详情,确认多分类 id 都能走本地 fallback;随后再决定是否继续单独处理原生 timeout - -### Session 012 - -- 日期:2026-05-21 -- 本轮目标:详情页添加发布者头像和名称,并与地址/时间同排展示 -- 已完成:`pages/detail/detail.js` 为详情数据补充发布者名称、头像 URL 和无头像首字兜底;`pages/detail/detail.wxml` 将原底部元信息改成左侧发布者、右侧地点/距离/发布时间的同排布局;`pages/detail/detail.wxss` 增加头像、名称省略和右侧元信息右对齐样式 -- 运行过的验证:`bash harness/init.sh`;`node --check pages/detail/detail.js`;`node scripts/check-json.mjs`;`git diff --check`;微信开发者工具内置 `wcc` 全量编译 WXML;微信开发者工具内置 `wcsc -lc` 全量编译 WXSS;`node harness/check-harness.mjs`;WeChat DevTools 查看详情页渲染 -- 已记录证据:`bash harness/init.sh` 完整跑通,npm install up to date,随后 JSON 和 harness 自检通过;`node --check pages/detail/detail.js`、`node scripts/check-json.mjs`、`git diff --check`、全量 WXML/WXSS 编译和 `node harness/check-harness.mjs` 均通过;WeChat DevTools 详情页可见左侧“匿名用户”和右侧“大钟寺广场 · 226m · 23天前”同排展示 -- 更新过的文件或工件:`pages/detail/detail.js`,`pages/detail/detail.wxml`,`pages/detail/detail.wxss`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:DevTools 控制台仍显示既有的原生 `WAServiceMainContext timeout`,本轮观察该错误未阻断详情页渲染;发布者行的真实头像展示仍需在已设置头像的账号上验证 -- 下一步最佳动作:用一个已登录且设置头像的发布者发布任务后进入详情,确认真实头像展示;再在更长地点名称的数据上确认右侧文本省略符合预期 - -### Session 013 - -- 日期:2026-05-21 -- 本轮目标:按反馈优化地图信息列表卡片布局和抽屉高度 -- 已完成:地图列表抽屉改为 `flex` 纵向布局并撑满 tabBar 上方空间;任务卡取消三块指标卡,分类/状态标签移动到标题后面;标题下展示任务正文;底部左侧改为 `确认:数量`、`过时:数量` 的轻量文字,右侧显示发布时间和距离;右侧详情入口从粗边框箭头改为居中轻量 `›` -- 运行过的验证:`bash harness/init.sh`;`node --check pages/map/map.js`;`node scripts/check-json.mjs`;`git diff --check`;微信开发者工具内置 `wcc` 全量编译 WXML;微信开发者工具内置 `wcsc -lc` 全量编译 WXSS;`node harness/check-harness.mjs`;WeChat DevTools 查看打开的信息列表 -- 已记录证据:`bash harness/init.sh` 完整跑通,npm install up to date,随后 JSON 和 harness 自检通过;`node --check pages/map/map.js`、`node scripts/check-json.mjs`、`git diff --check`、全量 WXML/WXSS 编译和 `node harness/check-harness.mjs` 均通过;WeChat DevTools 中信息列表铺到 tabBar 上沿,卡片显示“标题 + 打卡/已过期标签”、正文、底部统计和右侧时间距离 -- 更新过的文件或工件:`pages/map/map.wxml`,`pages/map/map.wxss`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:DevTools 控制台仍显示既有的原生 `WAServiceMainContext timeout`,本轮观察该错误未阻断地图列表渲染;更长标题和带图卡片仍建议真机再看一次 -- 下一步最佳动作:在真机或 DevTools 不同机型宽度下打开信息列表,确认标题较长、正文较长和带图卡片都不挤压底部统计 - -### Session 014Docs - -- 日期:2026-05-26 -- 本轮目标:总结当前项目并写出 Markdown 文档 -- 已完成:新增根目录 `PROJECT_SUMMARY.md`,从项目定位、核心用户场景、技术栈、页面结构、共享模块、数据模型、状态规则、云端/本地降级、设计系统、运行验证方式、当前状态和后续风险等维度总结 Street Tasks 小程序 -- 运行过的验证:`bash harness/init.sh`;`git diff --check`;`rg --files -g 'AGENT*'` -- 已记录证据:`bash harness/init.sh` 完整跑通;当前无 npm 时基于 package-lock 无外部依赖跳过安装,随后 `node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`,`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`git diff --check` 通过无输出;`rg --files -g 'AGENT*'` 仅输出 `AGENTS.md`,确认误建的 `AGENT.MD` 已移除 -- 更新过的文件或工件:`PROJECT_SUMMARY.md`,`harness/claude-progress.md` -- 已知风险或未解决问题:本轮仅新增项目总结文档,没有改变小程序运行逻辑;既有 WeChat DevTools/真机验证缺口仍然存在 -- 下一步最佳动作:继续围绕 `map-feed-001` 做地图首页、定位授权、marker 详情链路和不同机型列表抽屉的手动验证 - -### Session 015Docs - -- 日期:2026-05-26 -- 本轮目标:生成/更新根目录 `AGENTS.md` -- 已完成:按用户纠正移除误建的 `AGENT.MD`;更新现有 `AGENTS.md`,补充当前项目快照、CloudBase/本地降级、图片发布约束、关键模块与页面、验证命令、评论/反馈/反应数据说明和收尾要求 -- 运行过的验证:`bash harness/init.sh` -- 已记录证据:`bash harness/init.sh` 完整跑通;当前无 npm 时基于 package-lock 无外部依赖跳过安装,随后 `node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`,`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.` -- 更新过的文件或工件:`AGENTS.md`,`harness/claude-progress.md` -- 已知风险或未解决问题:本轮仅新增 agent 文档,没有改变小程序运行逻辑;既有 WeChat DevTools/真机验证缺口仍然存在 -- 下一步最佳动作:继续围绕 `map-feed-001` 做地图首页、定位授权、marker 详情链路和不同机型列表抽屉的手动验证 - -### Session 016Understand - -- 日期:2026-05-28 -- 本轮目标:运行 `understand` skill,为当前仓库生成 Understand Anything 知识图谱 -- 已完成:构建本地 understand-anything core 依赖;生成并调整 `.understand-anything/.understandignore`,排除 `.agents/`、`log/`、本地私有配置和分析器临时目录;扫描 73 个项目文件;生成 `.understand-anything/knowledge-graph.json`、`meta.json` 和 `fingerprints.json` -- 运行过的验证:`bash harness/init.sh`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`node` 解析 `.understand-anything/knowledge-graph.json`、`meta.json`、`fingerprints.json`;Understand inline graph validation -- 已记录证据:图谱验证结果为 0 issues、0 warnings;图谱包含 164 个节点、188 条边、7 个架构层和 8 个 guided tour steps;`build-fingerprints.mjs` 输出 `Fingerprints baseline: 73 files`;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`bash harness/init.sh` 完整跑通 -- 更新过的文件或工件:`.understand-anything/.understandignore`,`.understand-anything/knowledge-graph.json`,`.understand-anything/meta.json`,`.understand-anything/fingerprints.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:本轮仅生成代码理解图谱,没有改变小程序运行逻辑;WeChat DevTools/真机验证缺口仍沿用既有记录 -- 下一步最佳动作:需要浏览架构时打开 Understand dashboard;产品验证仍继续围绕 `map-feed-001` 的定位授权、marker 详情链路和列表抽屉做手动确认 - -### Session 014A - -- 日期:2026-06-12 -- 分支:`codex/iter-map-ux` -- 本轮目标:A 组产品/设计/开发探索地图首页附近任务浏览迭代 -- 产品假设:地图首屏虽然有列表入口,但用户进入后仍需要打开抽屉才知道最近可处理任务;新增附近优先预览应降低首次浏览成本 -- 已完成:新增 `NearbyPreview` 首屏横向预览,按距离优先展示 active/stale 任务;地图任务装饰抽到 `utils/post-presenter.js`;marker、预览和列表选中态联动;选中 marker callout 使用深色强调;新增 `harness/check-map-feed.mjs` -- 运行过的验证:`node --check pages/map/map.js`;`node --check utils/post-presenter.js`;`node --check utils/geo.js`;`node harness/check-map-feed.mjs` -- 已记录证据:三条 `node --check` 通过;`node harness/check-map-feed.mjs` 输出 `Map feed checks passed.` -- 已知风险或未解决问题:尚未在 WeChat DevTools/真机验证地图原生层、首屏预览是否遮挡地图工具、窄屏文案省略、marker 点击和详情链路 -- 下一步最佳动作:在 WeChat DevTools 打开地图页,观察 NearbyPreview、点击预览/marker/列表之间的选中联动,再决定是否合入主线 - -### Session 015A - -- 日期:2026-06-12 -- 分支:`codex/iter-map-ux` -- 本轮目标:按用户评测报告收敛 NearbyPreview 遮挡和长标题风险 -- 已完成:NearbyPreview 改用 `previewTitle` 短标题;`buildNearbyPreviewPosts` 为预览卡生成截断标题;检查脚本覆盖长标题截断,避免首屏预览被超长标题撑开 -- 运行过的验证:`node harness/check-map-feed.mjs`;微信开发者工具内置 `wcc` 全量编译 WXML;微信开发者工具内置 `wcsc -lc` 全量编译 WXSS -- 已记录证据:`node harness/check-map-feed.mjs` 先因 `previewTitle` 缺失按预期失败,补实现后输出 `Map feed checks passed.`;`wcc` 和 `wcsc -lc` 全量编译退出码为 0 且无输出 -- 已知风险或未解决问题:尚未在 WeChat DevTools/真机验证真实地图覆盖层、横向滚动和 marker callout 渲染 -- 下一步最佳动作:跑完整自动验证后,用 DevTools 验证窄屏首屏遮挡关系 - -### Session 014B - -- 日期:2026-06-12 -- 分支:`codex/iter-publish-flow` -- 本轮目标:B 组产品/设计/开发探索发布流程迭代 -- 产品假设:用户在提交时才触发定位和校验会造成失败感;把发布准备度和位置确认前置,应提升表单完成率 -- 已完成:新增 `pages/publish/publish-state.js` 计算身份、内容、分类和位置准备度;发布页新增发布准备清单、标题/详情字数提示、当前位置显式确认、有效期直接按钮和动态底部提交文案;新增 `scripts/check-publish-flow.mjs` -- 运行过的验证:`node --check pages/publish/publish.js`;`node --check pages/publish/publish-state.js`;`node --no-warnings scripts/check-publish-flow.mjs` -- 已记录证据:两条 `node --check` 通过;`node --no-warnings scripts/check-publish-flow.mjs` 输出 `Publish flow checks passed.` -- 已知风险或未解决问题:尚未在 WeChat DevTools/真机验证定位授权、键盘遮挡、位置确认失败恢复、图片上传和真实发布闭环 -- 下一步最佳动作:在 WeChat DevTools 用游客和登录用户分别打开发布页,验证准备度清单、定位确认、必填提示、图片和发布成功链路 - -### Session 015B - -- 日期:2026-06-12 -- 分支:`codex/iter-publish-flow` -- 本轮目标:按用户评测报告收敛发布准备度的定位失败和重试路径 -- 已完成:`buildPublishState` 细化 locating/failed/ready 文案;只缺当前位置时底部按钮可直接触发定位确认;定位失败文案改为检查授权或重试;发布检查脚本覆盖定位中、待确认、失败重试和失物招领方向补齐 -- 运行过的验证:`node --no-warnings scripts/check-publish-flow.mjs` -- 已记录证据:`node --no-warnings scripts/check-publish-flow.mjs` 输出 `Publish flow checks passed.` -- 已知风险或未解决问题:尚未在 WeChat DevTools/真机验证定位授权弹窗、拒绝/超时恢复、键盘遮挡和图片上传失败回滚 -- 下一步最佳动作:跑完整自动验证后,在真机或 DevTools 中验证定位允许、拒绝、重试三条路径 - -### Session 016B - -- 日期:2026-06-12 -- 分支:`codex/iter-publish-flow` -- 本轮目标:第三轮聚焦发布准备度状态机稳定性 -- 已完成:`buildPublishState` 新增 `primaryAction`,显式区分 `login`、`fill`、`confirmLocation`、`waitLocation`、`publish`、`submitting`;发布页 `submit()` 改为按 `primaryAction` 分派,避免页面层重复推断 missing 数组 -- 运行过的验证:`node --no-warnings scripts/check-publish-flow.mjs`;微信开发者工具内置 `wcc` 全量编译 WXML;微信开发者工具内置 `wcsc -lc` 全量编译 WXSS -- 已记录证据:检查脚本先因 `primaryAction` 缺失按预期失败,补实现后输出 `Publish flow checks passed.`;`wcc` 和 `wcsc -lc` 全量编译退出码为 0 且无输出 -- 已知风险或未解决问题:尚未在 WeChat DevTools/真机验证定位授权、键盘安全区、图片上传失败回滚和发布后详情跳转;CLI `open`/`preview` 已尝试启用服务端口并连接 9420,但均因 `wait IDE port timeout` 失败,`preview` 未产出二维码信息 -- 下一步最佳动作:在本机 WeChat DevTools UI 中确认“设置 -> 安全设置 -> 服务端口”已开启并重新打开 `/tmp/street-tasks-iter-worktrees/publish`,再执行游客、缺位置、定位失败/重试、图片发布和发布后详情跳转手测 - -### Session 014C - -- 日期:2026-06-12 -- 分支:`codex/iter-detail-trust` -- 本轮目标:C 组产品/设计/开发探索详情页信任判断迭代 -- 产品假设:详情页已有评论和信任动作,但用户仍需从原始计数自行判断可信度;信任判断摘要应让下一步行动更清楚 -- 已完成:新增 `formatTrustInsight` 将确认、过时、举报和评论数归纳为信任判断;详情页在信任动作前新增 TrustInsight 面板和分段指标;评论区标题右侧新增写评论入口,保留长页面上的悬浮评论按钮 -- 运行过的验证:`node --check pages/detail/detail.js`;`node --check utils/format.js`;Node import check for `formatTrustInsight` -- 已记录证据:两条 `node --check` 通过;Node import check 输出 `Trust insight checks passed.` -- 已知风险或未解决问题:尚未在 WeChat DevTools/真机验证窄屏布局、信任动作后的面板刷新、游客登录评论引导、resolved/expired 只读状态和云端评论路径 -- 下一步最佳动作:在 WeChat DevTools 打开 active、stale、resolved、expired 任务详情,验证 TrustInsight 文案、指标换行和评论入口 - -### Session 017F - -- 日期:2026-06-13 -- 分支:`codex/iter-candidate-publish-trust` -- 本轮目标:组合 B 组发布准备度和 C 组详情信任解释,形成更接近合入候选的第六组实验 -- 已完成:从 `codex/iter-publish-flow` HEAD 创建候选分支,cherry-pick C 组详情信任三个提交;合并 `DESIGN_SYSTEM.md` 和 harness 记录冲突,保留 `PublishReadiness`、`LocationCheck` 和谨慎版 `TrustInsight` 规则;新增 `harness/candidate-publish-trust-report.md` 记录组合目标、验证和未手测项 -- 运行过的验证:`node --check pages/publish/publish.js`;`node --check pages/publish/publish-state.js`;`node --check pages/detail/detail.js`;`node --check utils/format.js`;`node --check harness/check-trust-insight.mjs`;`node --no-warnings scripts/check-publish-flow.mjs`;`node harness/check-trust-insight.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;微信开发者工具内置 `wcc` 全量编译 WXML;微信开发者工具内置 `wcsc -lc` 全量编译 WXSS -- 已记录证据:发布检查输出 `Publish flow checks passed.`;详情信任检查输出 `Trust insight checks passed.`;JSON 检查输出 `Checked 11 JSON files.`;harness 检查输出 `Harness OK: 6 features checked.`;`git diff --check` 通过;全量 WXML/WXSS 本地编译退出码均为 0 -- 已知风险或未解决问题:尚未在 WeChat DevTools/真机验证发布定位授权、键盘安全区、图片上传失败回滚、发布后详情跳转;尚未验证详情页 TrustInsight 窄屏、评论入口、信任动作刷新和云端评论路径;DevTools CLI 9420 端口超时问题未在本组合分支重新尝试 -- 下一步最佳动作:启动用户评测 agent 对 F 组组合候选评分;随后在 WeChat DevTools UI 中优先手测 F 组发布闭环,再手测详情 TrustInsight - -### Session 018G - -- 日期:2026-06-14 -- 分支:`codex/iter-candidate-hardening` -- 本轮目标:第七组候选硬化实验,为 F 组组合候选补充产品风险排序、视觉手测清单和跨页面 smoke 检查 -- 已完成:产品 agent 输出 `harness/hardening-product-brief.md`,明确发布闭环、位置确认、详情信任解释和边界状态是最高风险;设计 agent 输出 `harness/hardening-design-checklist.md`,覆盖发布准备度、定位卡片、底部按钮、TrustInsight、评论入口、信任动作、窄屏、键盘、安全区和图片状态;开发 agent 新增 `scripts/check-candidate-flow.mjs`,用纯逻辑同时覆盖发布 `primaryAction` 状态机和 TrustInsight 谨慎文案 -- 运行过的验证:`node --check scripts/check-candidate-flow.mjs`;`node scripts/check-candidate-flow.mjs` -- 已记录证据:`node scripts/check-candidate-flow.mjs` 输出 `Candidate flow checks passed.`,同时出现项目既有的 `MODULE_TYPELESS_PACKAGE_JSON` ESM 警告 -- 更新过的文件或工件:`harness/hardening-product-brief.md`,`harness/hardening-design-checklist.md`,`scripts/check-candidate-flow.mjs` -- 已知风险或未解决问题:本轮不改功能代码,仍未完成 WeChat DevTools/真机真实发布闭环、图片上传、定位授权、详情信任动作刷新、评论入口和云端路径验证 -- 下一步最佳动作:运行完整候选验证命令并提交 G 组硬化;随后更新用户评测报告,记录 G 组不是新功能合入候选,而是降低 F 组手测风险的验证增强 - -### Session 015C - -- 日期:2026-06-12 -- 分支:`codex/iter-detail-trust` -- 本轮目标:按用户评测报告收敛 TrustInsight 文案误导和冲突优先级风险 -- 已完成:确认文案从“有人确认有效”改为更谨慎的“有确认信号”;评论-only 场景改为“先看评论线索”;新增 `harness/check-trust-insight.mjs` 覆盖举报优先、过时优先、resolved/expired、comments-only 和四指标短 note -- 运行过的验证:`node harness/check-trust-insight.mjs`;微信开发者工具内置 `wcc` 全量编译 WXML;微信开发者工具内置 `wcsc -lc` 全量编译 WXSS -- 已记录证据:`node harness/check-trust-insight.mjs` 先因旧文案按预期失败,补实现后输出 `Trust insight checks passed.`;`wcc` 和 `wcsc -lc` 全量编译退出码为 0 且无输出 -- 已知风险或未解决问题:尚未在 WeChat DevTools/真机验证 TrustInsight 面板密度、信任动作点击刷新和云端评论路径 -- 下一步最佳动作:跑完整自动验证后,优先做 DevTools 手测以决定是否将 C 组合入主线 - -### Session 019H - -- 日期:2026-06-14 -- 分支:`codex/iter-devtools-readiness` -- 本轮目标:第八组 DevTools/真机手测准入实验,为 G/F 候选补齐产品准入、手测清单和自动前置门禁,避免把脚本结果误判为真实用户旅程通过 -- 已完成:产品 agent 输出 `harness/devtools-readiness-product-brief.md`,明确 H 组目标、非目标、自动检查门槛、必测用户旅程、证据升级规则和阻塞记录口径;设计/QA agent 输出 `harness/devtools-readiness-checklist.md`,覆盖测试环境记录、DevTools 编译/控制台、地图、发布、详情、跨用户和云端路径的待执行手测清单;开发 agent 新增 `scripts/check-devtools-readiness.mjs`,检查 readiness 文档和既有候选脚本齐备,并串联发布、TrustInsight、候选 flow 检查 -- 运行过的验证:`node --check scripts/check-devtools-readiness.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`git diff --check`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`bash harness/init.sh`;微信开发者工具内置 `wcc` 全量编译 WXML;微信开发者工具内置 `wcsc -lc` 全量编译 WXSS -- 已记录证据:`node --no-warnings scripts/check-devtools-readiness.mjs` 输出 `Publish flow checks passed.`、`Trust insight checks passed.`、`Candidate flow checks passed.`、`DevTools readiness checks passed.`;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`bash harness/init.sh` 完整跑通;`git diff --check` 通过;`wcc` 和 `wcsc -lc` 全量编译退出码为 0 且无输出 -- 更新过的文件或工件:`harness/devtools-readiness-product-brief.md`,`harness/devtools-readiness-checklist.md`,`scripts/check-devtools-readiness.mjs` -- 已知风险或未解决问题:H 组不新增功能,也不代表已经完成 WeChat DevTools/真机手测;真实定位授权、键盘/安全区、图片上传、发布后详情跳转、信任动作刷新、评论入口、跨用户和云端路径仍必须按清单执行并记录证据 -- 下一步最佳动作:启动用户评测 agent 评估 H 组相对 G 组的价值;若继续推进,优先在 WeChat DevTools UI 打开 `/tmp/street-tasks-iter-worktrees/devtools-readiness`,按 readiness 清单执行完整手测并把结果写回 harness - -### Session 020I - -- 日期:2026-06-14 -- 分支:`codex/iter-manual-evidence-gate` -- 本轮目标:第九组手测证据完整性实验,为 H 组 readiness 后续真实手测补充机器可读结果模板和校验脚本,防止“已通过”缺少环境、步骤、实际结果和证据 -- 已完成:产品 agent 输出 `harness/manual-evidence-product-brief.md`,定义 `passed`、`failed`、`blocked`、`not_covered` 状态和发布准入影响;QA/数据 agent 输出 `harness/manual-test-results.example.json`,提供不声称真实通过的手测结果示例;开发 agent 新增 `scripts/check-manual-evidence.mjs`,校验顶层环境、journey 字段、状态枚举和不同状态的证据要求 -- 运行过的验证:`node --check scripts/check-manual-evidence.mjs`;`node scripts/check-manual-evidence.mjs`;`node -e` 解析 `harness/manual-test-results.example.json`;`git diff --check`;用 `/tmp/manual-evidence-bad.json` 临时坏样例验证缺 evidence 的 `passed` 会失败 -- 已记录证据:`node scripts/check-manual-evidence.mjs` 输出 `Manual evidence checks passed.`;JSON 解析输出 `manual evidence example JSON parsed`;坏样例检查输出 `Bad passed evidence rejected as expected.` -- 更新过的文件或工件:`harness/manual-evidence-product-brief.md`,`harness/manual-test-results.example.json`,`scripts/check-manual-evidence.mjs` -- 已知风险或未解决问题:I 组仍不代表已经完成真实 WeChat DevTools/真机手测;示例 JSON 只覆盖 `not_covered` 和 `blocked` 写法,没有真实 `passed` 证据;后续若填真实结果,必须附可复核截图、录屏、日志、任务 id 或云端记录 -- 下一步最佳动作:运行完整候选验证并提交 I 组;随后启动用户评测 agent,评估 I 组相对 H 组是否进一步降低“手测口头通过但证据不足”的风险 - -### Session 021J - -- 日期:2026-06-14 -- 分支:`codex/iter-evidence-hygiene` -- 本轮目标:第十组证据卫生实验,为 I 组手测结果证据补充提交前脱敏边界、本地附件忽略规则和敏感内容扫描,避免真实截图、云端 ID、本机路径或 token 进入仓库 -- 已完成:产品 agent 输出 `harness/evidence-hygiene-product-brief.md`,区分可提交摘要与本地原始证据;安全/QA agent 输出 `harness/evidence-redaction-checklist.md`,列出提交前脱敏和 reviewer 检查项;开发 agent 新增 `scripts/check-evidence-hygiene.mjs`,校验 `.gitignore`、可提交 evidence 文档和 example JSON;主线程更新 `.gitignore`,忽略 `harness/manual-test-results.local*.json` 与 `harness/manual-evidence-artifacts/` -- 运行过的验证:`node --check scripts/check-evidence-hygiene.mjs`;`node scripts/check-evidence-hygiene.mjs`;`node scripts/check-manual-evidence.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`git diff --check`;用 `/tmp/evidence-hygiene-bad-root` 临时坏样例验证具体 `cloud://` 路径和 example 中 `passed` 会失败 -- 已记录证据:`node scripts/check-evidence-hygiene.mjs` 输出 `Evidence hygiene checks passed.`;`node scripts/check-manual-evidence.mjs` 输出 `Manual evidence checks passed.`;readiness 检查输出 `Publish flow checks passed.`、`Trust insight checks passed.`、`Candidate flow checks passed.`、`DevTools readiness checks passed.`;坏样例检查输出 `Bad evidence hygiene sample rejected as expected.` -- 更新过的文件或工件:`.gitignore`,`harness/evidence-hygiene-product-brief.md`,`harness/evidence-redaction-checklist.md`,`scripts/check-evidence-hygiene.mjs` -- 已知风险或未解决问题:J 组不代表真实 DevTools/真机手测或真实脱敏审查已经完成;它只保护 future 手测证据提交边界。真实附件仍应保存在本地或受控系统,提交前还需按清单人工复核 -- 下一步最佳动作:运行完整候选验证并提交 J 组;随后启动用户评测 agent,评估 J 组是否进一步降低“完整证据但含敏感原始材料”的风险 - -### Session 022K - -- 日期:2026-06-14 -- 分支:`codex/iter-manual-runbook` -- 本轮目标:第十一组手测运行手册实验,为 J 组证据卫生链路补充可执行入口,帮助后续执行者准备 ignored local 结果文件、确认 worktree、运行前置门禁和收尾门禁 -- 已完成:产品 agent 输出 `harness/manual-runbook-product-brief.md`,定义 helper 目标、非目标、本地文件命名和 H/I/J/K 分工;QA agent 输出 `harness/manual-runbook-checklist.md`,覆盖准备、执行、收尾和常见失败修复;开发 agent 新增 `scripts/prepare-manual-test-run.mjs`,从 example 生成 ignored local 结果文件、创建 ignored 附件目录、运行 readiness/manual evidence/evidence hygiene 三层门禁并打印下一步;主线程补充输出路径 guard,只允许 `harness/manual-test-results.local*.json` -- 运行过的验证:`node --check scripts/prepare-manual-test-run.mjs`;`node scripts/prepare-manual-test-run.mjs --out harness/manual-test-results.local-smoke.json`;`node scripts/prepare-manual-test-run.mjs --out harness/not-local-test-results.json` 验证非 ignored 路径会失败;重复运行 local smoke 验证默认拒绝覆盖;解析生成的 local smoke JSON,确认 `branch=codex/iter-manual-runbook`、`summary.overallStatus=not_covered`、`passed` journey 数量为 0;清理临时 ignored 产物 -- 已记录证据:helper 正向运行输出 readiness、manual evidence、evidence hygiene 三层门禁均通过并输出 `Manual test run prepared.`;非 ignored 输出路径失败并提示必须匹配 `harness/manual-test-results.local*.json`;重复运行失败并提示 `Refusing to overwrite existing output file`;local smoke 解析输出 `local smoke result prepared for codex/iter-manual-runbook@4aff484; passed journeys: 0` -- 更新过的文件或工件:`harness/manual-runbook-product-brief.md`,`harness/manual-runbook-checklist.md`,`scripts/prepare-manual-test-run.mjs` -- 已知风险或未解决问题:K 组仍不代表真实 DevTools/真机手测已经完成;helper 只准备本地结果文件和命令入口,不收集真实截图/录屏/日志,也不替执行者判断 passed;后续仍需真实手测、填写 local JSON,并通过 I/J 门禁和人工脱敏复核 -- 下一步最佳动作:运行完整候选验证并提交 K 组;随后启动用户评测 agent,评估 K 组是否进一步降低“准备手测时漏跑门禁或误改 example”的风险 - -### Session 023L - -- 日期:2026-06-14 -- 分支:`codex/iter-sanitized-summary` -- 本轮目标:第十二组手测脱敏摘要实验,为 K 组 local 手测结果补充 Markdown 摘要草稿生成器,方便评测/评审阅读而不提交原始证据 -- 已完成:产品 agent 输出 `harness/sanitized-summary-product-brief.md`,明确摘要草稿价值、非真实手测边界和不提交原始 evidence 的口径;QA agent 输出 `harness/sanitized-summary-checklist.md`,定义摘要字段白名单、敏感内容黑名单、生成前后检查和失败处理;开发 agent 新增 `scripts/create-manual-summary.mjs`,只允许读取 `harness/manual-test-results.local*.json` 并写入 ignored 的 `harness/manual-test-summary.local*.md`,生成前串联 manual evidence 与 evidence hygiene 门禁,生成后扫描敏感模式且不输出原始 `evidence` 字符串;主线程更新 `.gitignore` 和 `scripts/check-evidence-hygiene.mjs`,把 local summary 草稿和 L 组文档纳入证据卫生边界 -- 运行过的验证:`node --check scripts/create-manual-summary.mjs`;`node --check scripts/check-evidence-hygiene.mjs`;`node scripts/check-evidence-hygiene.mjs`;`node scripts/check-manual-evidence.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node scripts/prepare-manual-test-run.mjs --out harness/manual-test-results.local-l-smoke.json --force`;`node scripts/create-manual-summary.mjs --input harness/manual-test-results.local-l-smoke.json --out harness/manual-test-summary.local-l-smoke.md`;`node scripts/create-manual-summary.mjs --input harness/manual-test-results.example.json --out harness/manual-test-summary.local-example.md` 验证 example 输入会失败;`node scripts/create-manual-summary.mjs --input harness/manual-test-results.local-l-smoke.json --out harness/manual-test-summary.md` 验证非 ignored 输出路径会失败;临时 bad local JSON 中写入 `/Users/...` 验证生成内容敏感扫描会失败;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`bash harness/init.sh`;`git diff --check`;微信开发者工具内置 `wcc` 全量编译 WXML;微信开发者工具内置 `wcsc -lc` 全量编译 WXSS;清理临时 ignored 产物 -- 已记录证据:正向 smoke 输出 readiness、manual evidence、evidence hygiene 三层门禁均通过并输出 `Manual summary draft created.`;example 输入失败并提示 `Input must match`;非 local summary 输出失败并提示 `Output must match`;敏感路径样例失败并提示 `prohibited macOS user path`;`node scripts/check-evidence-hygiene.mjs` 输出 `Evidence hygiene checks passed.`;`node scripts/check-manual-evidence.mjs` 输出 `Manual evidence checks passed.`;readiness 输出 `Publish flow checks passed.`、`Trust insight checks passed.`、`Candidate flow checks passed.`、`DevTools readiness checks passed.`;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`bash harness/init.sh` 完整跑通;`git diff --check` 通过;`wcc`/`wcsc -lc` 全量编译退出码为 0,输出 `WXML/WXSS compile checks passed.` -- 更新过的文件或工件:`.gitignore`,`harness/sanitized-summary-product-brief.md`,`harness/sanitized-summary-checklist.md`,`scripts/check-evidence-hygiene.mjs`,`scripts/create-manual-summary.mjs` -- 已知风险或未解决问题:L 组仍不代表真实 DevTools/真机手测已经完成;摘要草稿来自本地 JSON,不能把 example、mock、占位或自动脚本结果包装成真实 evidence;local Markdown 草稿保持 ignored,若要写入可提交报告仍需人工脱敏复核 -- 下一步最佳动作:运行完整候选验证并提交 L 组;随后启动用户评测 agent,评估 L 组是否进一步降低“本地证据可读性不足或摘要误泄漏”的风险 - -### Session 024M - -- 日期:2026-06-14 -- 分支:`codex/iter-devtools-smoke-unblock` -- 本轮目标:第十三组 DevTools smoke access unblock 实验,把真实 WeChat DevTools smoke 的 CLI/服务端口阻塞变成可重复诊断和明确 blocked 记录,避免继续把 harness 完整度误读成真实用户旅程通过 -- 已完成:产品 agent 输出 `harness/devtools-smoke-product-brief.md`,明确 M 组从扩展 harness 转向真实 smoke access 排查;QA agent 输出 `harness/devtools-smoke-checklist.md`,覆盖端口/进程/CLI/blocked 字段和端口恢复后的最小 smoke 旅程;开发 agent 新增 `scripts/check-devtools-smoke-access.mjs`,默认无副作用检查项目、DevTools CLI、服务端口监听和 `ide-http-port` 进程声明,可选 `--attempt-open` 捕获 CLI open 结果,默认 blocked exit 0,`--strict` 时 blocked exit 1,并对输出中的本机路径和凭证模式做脱敏 -- 运行过的验证:`node --check scripts/check-devtools-smoke-access.mjs`;`node scripts/check-devtools-smoke-access.mjs --project /tmp/street-tasks-iter-worktrees/devtools-smoke --port 9420`;`node scripts/check-devtools-smoke-access.mjs --project /tmp/street-tasks-iter-worktrees/devtools-smoke --port 9420 --strict` 验证 blocked 会 exit 1;`node scripts/check-devtools-smoke-access.mjs --project /tmp/street-tasks-iter-worktrees/devtools-smoke --port 9420 --attempt-open --timeout-ms 12000`;对 attempt-open 输出运行路径泄漏扫描,确认没有 `/Users/`、`/private/tmp` 或 `/tmp/street-tasks`;手动复现 `curl http://127.0.0.1:9420/` connection refused、`cli open --project ... --port 9420` 12s timeout、进程列表中存在 1 个声明 `--ide-http-port 9420` 的 DevTools-like 进程 -- 已记录证据:默认诊断报告输出 `status: blocked`,项目和 CLI 可用,但 `service port 9420: no (127.0.0.1: ECONNREFUSED; ::1: ECONNREFUSED)`,`ide-http-port process: yes (1 matching declaration(s), 1 DevTools-like)`;strict 模式输出同样 blocked 报告且 exit 1;attempt-open 输出 `attempt-open: no (timed out)`、`signal: SIGTERM`、stderr 摘要包含 `IDE may already started at port 9420, trying to connect`;脱敏检查输出 `DevTools smoke report redaction check passed.` -- 更新过的文件或工件:`harness/devtools-smoke-product-brief.md`,`harness/devtools-smoke-checklist.md`,`scripts/check-devtools-smoke-access.mjs` -- 已知风险或未解决问题:M 组仍未完成真实 DevTools/真机 smoke;当前环境显示 DevTools 进程声明 9420 但端口未监听,CLI open 只能进入 timeout;下一步需要有 UI 权限的执行者确认 DevTools 安全设置服务端口、正常退出重启 IDE 或换端口/换机,再执行 L/K 的真实 smoke runbook -- 下一步最佳动作:运行完整候选验证并提交 M 组;随后启动用户评测 agent,评估 M 组是否比 L 更接近真实手测入口恢复,若端口仍 blocked,下轮应优先进行人工 DevTools UI 服务端口恢复而不是继续新增文档 - -### Session 025N - -- 日期:2026-06-14 -- 分支:`codex/iter-devtools-service-recovery` -- 本轮目标:第十四组 DevTools service port 受控恢复实验,在 M 组诊断基础上尝试显式 quit/reopen 恢复 9420 服务端口,并记录恢复成功或继续 blocked 的证据 -- 已完成:产品 agent 输出 `harness/devtools-service-recovery-product-brief.md`,定义受控恢复边界和避免误判口径;QA agent 输出 `harness/devtools-service-recovery-checklist.md`,覆盖恢复前检查、退出/重启风险、恢复命令、ready/blocked 判定和最小 smoke;开发 agent 新增 `scripts/recover-devtools-service-port.mjs`,默认 dry-run,只在显式 `--quit-reopen` 且非 `--dry-run` 时执行 DevTools CLI `quit`、等待、`open --project --port 9420 --disable-gpu`,并用 M 组 access 检查做 before/after 对比 -- 运行过的验证:`node --check scripts/recover-devtools-service-port.mjs`;默认 dry-run 报告 before/after 均为 blocked 且跳过 quit/open;`--dry-run --quit-reopen` 确认仍跳过 quit/open;`--strict` blocked 路径 exit 1;执行 `node scripts/recover-devtools-service-port.mjs --project /tmp/street-tasks-iter-worktrees/devtools-recovery --port 9420 --timeout-ms 20000 --quit-reopen` 做受控恢复尝试;恢复后运行 `node scripts/check-devtools-smoke-access.mjs --project /tmp/street-tasks-iter-worktrees/devtools-recovery --port 9420 --strict`;恢复报告路径脱敏扫描确认没有 `/Users/`、`/private/tmp` 或 `/tmp/street-tasks`;检查 9420 仍无监听,进程列表仍有 1 个声明 `--ide-http-port 9420` 的 DevTools-like 进程;补跑 `node scripts/check-json.mjs`、`node harness/check-harness.mjs`、`bash harness/init.sh`、`git diff --check`、`node --no-warnings scripts/check-devtools-readiness.mjs`、`node scripts/check-manual-evidence.mjs`、`node scripts/check-evidence-hygiene.mjs`、`node --check scripts/check-devtools-smoke-access.mjs`、微信开发者工具内置 `wcc` 全量编译 WXML、微信开发者工具内置 `wcsc -lc` 全量编译 WXSS -- 已记录证据:受控恢复报告显示 before 为 blocked;`DevTools quit` timed out,`signal: SIGTERM`,stderr 摘要包含 `IDE may already started at port 9420, trying to connect`;等待 1200ms 后 `DevTools open` 同样 timed out;after 仍为 blocked,`service port 9420: no (127.0.0.1: ECONNREFUSED; ::1: ECONNREFUSED)`;strict after check exit 1 并输出 `After recovery still blocked as expected.`;恢复报告脱敏检查输出 `Recovery report redaction check passed.`;JSON 检查输出 `Checked 11 JSON files.`;harness 自检输出 `Harness OK: 6 features checked.`;`bash harness/init.sh` 完整跑通;readiness 检查输出 `Publish flow checks passed.`、`Trust insight checks passed.`、`Candidate flow checks passed.`、`DevTools readiness checks passed.`;manual evidence 和 evidence hygiene 分别输出通过;`git diff --check` 通过;`wcc`/`wcsc -lc` 编译退出码为 0 -- 更新过的文件或工件:`harness/devtools-service-recovery-product-brief.md`,`harness/devtools-service-recovery-checklist.md`,`scripts/recover-devtools-service-port.mjs` -- 已知风险或未解决问题:N 组未恢复 9420 服务端口,真实 DevTools/真机 smoke 仍未执行;CLI quit/open 均因 IDE 端口连接问题超时,下一步需要人工操作 DevTools UI 安全设置、正常退出重启或换机/换端口验证 -- 下一步最佳动作:提交 N 组并启动用户评测 agent,评估 N 组是否推动了阻塞收敛。若继续推进,应由有 UI 权限的执行者手动恢复 DevTools 服务端口,而不是继续用 CLI 反复 quit/open - -### Session 026O - -- 日期:2026-06-14 -- 分支:`codex/iter-devtools-port-forensics` -- 本轮目标:第十五组 DevTools port forensics 只读排查实验,在 M/N 组 9420 blocked 结论之后固化只读观察项、脱敏字段、ready/blocked/unknown 判定、人工 UI 恢复建议和更细的端口状态诊断 -- 已完成:产品 agent 新增 `harness/devtools-port-forensics-product-brief.md`,明确 O 组不是产品功能、真实 smoke 或端口恢复,只解释 DevTools 环境 blocker;QA/设计 agent 新增 `harness/devtools-port-forensics-checklist.md`,覆盖准备/基线、只读进程与端口检查、DevTools app/CLI 版本与路径摘要、多实例识别、声明 `ide-http-port` 但无监听的记录口径、blocked 字段、状态判定、脱敏规则和后续人工 UI 恢复建议;开发 agent 新增 `scripts/inspect-devtools-port-state.mjs`,只读汇总 project config、DevTools CLI、app bundle Info.plist、`lsof`、socket connect 和进程声明,不执行 quit/open/kill/清缓存/写配置 -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check scripts/inspect-devtools-port-state.mjs`;`node scripts/inspect-devtools-port-state.mjs --project /tmp/street-tasks-iter-worktrees/devtools-forensics --port 9420`;`node scripts/inspect-devtools-port-state.mjs --project /tmp/street-tasks-iter-worktrees/devtools-forensics --port 9420 --strict`;`node scripts/inspect-devtools-port-state.mjs --project /tmp/street-tasks-iter-worktrees/devtools-forensics --port 1 --strict`;对默认报告运行路径和 token 脱敏扫描 -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/devtools-forensics`,对应约定 `/tmp/street-tasks-iter-worktrees/devtools-forensics`;当前分支为 `codex/iter-devtools-port-forensics`;`bash harness/init.sh` 完整跑通,依赖 up to date,`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`,`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;默认 forensics 报告输出 `status: blocked`、`diagnosis: declared_without_listener, connect_refused`,DevTools CLI 和 app bundle 可用,版本为 `2.01.2510290` / `4240.111`,`lsof` 无监听,IPv4/IPv6 均 `ECONNREFUSED`,22 个 DevTools-like 相关进程中 1 个声明 requested port;strict 模式对 9420 blocked exit 1;`--port 1 --strict` 输出 `status: unknown` 并 exit 1;脱敏扫描未发现 `/Users/`、`/private/tmp`、`/tmp/street-tasks`、Bearer、cookie 或 token 片段 -- 更新过的文件或工件:`harness/devtools-port-forensics-product-brief.md`,`harness/devtools-port-forensics-checklist.md`,`scripts/inspect-devtools-port-state.mjs`,`harness/claude-progress.md` -- 已知风险或未解决问题:O 组没有退出 DevTools、打开/重启项目、杀进程、清缓存、写用户配置或执行真实 DevTools/真机 smoke;本轮也不证明 9420 已恢复。真实端口恢复和产品旅程仍需有 UI 权限的执行者后续操作并记录脱敏证据 -- 下一步最佳动作:由有本机 UI 权限的执行者按清单先做只读端口观察;若端口仍 blocked,走人工 UI 安全设置、正常退出重开、换端口或换机;若端口 access ready,再按已有手测 runbook 执行真实 DevTools UI/真机 smoke - -### Session 027P - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-resilience` -- 本轮目标:第十六组地图列表 UX resilience 静态防回归实验,在 DevTools 端口 blocked 的情况下先守住地图列表卡片结构和关键 WXSS 约束,降低长标题、长正文、带图和底部统计挤压风险 -- 已完成:产品 agent 新增 `harness/map-list-resilience-product-brief.md`,定义地图列表抽屉和任务卡的静态防回归价值、非目标、关键 UX 风险、成功标准和真机/DevTools 未验证边界;设计/QA agent 新增 `harness/map-list-resilience-checklist.md`,区分可脚本检查项和必须人工确认项,覆盖长标题、长正文、长地点、带图/无图、过期/已解决、多标签、底部统计和详情入口;开发 agent 新增 `scripts/check-map-list-resilience.mjs`,检查 `pages/map/map.wxml` 和 `pages/map/map.wxss` 中抽屉、列表、卡片、标题/标签、正文、footer、图片和详情按钮的结构/样式 guard -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check scripts/check-map-list-resilience.mjs`;`node scripts/check-map-list-resilience.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;微信开发者工具内置 `wcc` 全量编译 WXML;微信开发者工具内置 `wcsc -lc` 全量编译 WXSS -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/map-list-resilience`,对应约定 `/tmp/street-tasks-iter-worktrees/map-list-resilience`;当前分支为 `codex/iter-map-list-resilience`;`bash harness/init.sh` 完整跑通,依赖 up to date,`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`,`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;map list resilience 脚本输出 `Map list resilience checks passed.`;`git diff --check` 通过;`wcc`/`wcsc -lc` 编译退出码为 0 -- 更新过的文件或工件:`harness/map-list-resilience-product-brief.md`,`harness/map-list-resilience-checklist.md`,`scripts/check-map-list-resilience.mjs`,`harness/claude-progress.md` -- 已知风险或未解决问题:P 组不代表地图列表视觉已通过;它只证明当前 WXML/WXSS 保留了关键结构和防挤压约束。DevTools service port 9420 仍 blocked,真机/DevTools 中长标题、长正文、图片加载、safe area、地图原生层和详情点击仍未验证 -- 下一步最佳动作:提交 P 组并启动用户评测 agent;端口恢复后按 P 组 checklist 做地图列表真实视觉 smoke,补充窄屏/常见宽度/真机截图或录屏证据 - -### Session 028Q - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-preflight` -- 本轮目标:第十七组地图列表 preflight 集成实验,把 P 组新增的地图列表静态韧性检查接入 DevTools readiness 聚合门禁,降低后续候选版漏跑该检查的风险 -- 已完成:产品 agent 新增 `harness/map-list-preflight-product-brief.md`,定义 Q 组用户价值、非目标、preflight blocker 口径和 9420 blocked 下的边界;QA/设计 agent 新增 `harness/map-list-preflight-checklist.md`,覆盖自动命令、失败提示、长标题/标签/缩略图/footer/详情入口风险、人工验证项和降级证据字段;开发 agent 更新 `scripts/check-devtools-readiness.mjs`,把 `scripts/check-map-list-resilience.mjs` 作为必需文件和默认运行项,并在输出中明确静态 WXML/WXSS guard 不代表 DevTools 或真机视觉验收通过;主线程同步更新 `harness/feature_list.json` 的 map-feed evidence 和 notes -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check scripts/check-devtools-readiness.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node --no-warnings scripts/check-map-list-resilience.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check` -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/map-list-preflight`,对应约定 `/tmp/street-tasks-iter-worktrees/map-list-preflight`;当前分支为 `codex/iter-map-list-preflight`;`bash harness/init.sh` 完整跑通,依赖 up to date,`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`,`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;readiness 聚合输出 `Publish flow checks passed.`、`Trust insight checks passed.`、`Candidate flow checks passed.`、`Running map list static layout regression guard...`、`Map list resilience checks passed.`、`Map list static layout regression guard passed; DevTools and real-device visual acceptance are still required.`、`DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`;独立地图列表检查输出 `Map list resilience checks passed.`;`git diff --check` 通过 -- 更新过的文件或工件:`harness/map-list-preflight-product-brief.md`,`harness/map-list-preflight-checklist.md`,`scripts/check-devtools-readiness.mjs`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:Q 组没有修改页面 UI,也没有恢复 DevTools 9420 服务端口;它只保证 readiness/preflight 默认串联地图列表静态 guard。地图列表真实渲染、长内容视觉、图片加载、safe area、地图原生层和详情点击仍需 DevTools 或真机证据 -- 下一步最佳动作:提交 Q 组并启动用户评测 agent,评估 Q 相比 P 是否减少“新增检查但未来候选漏跑”的风险;若继续推进,优先做能在端口 blocked 状态下提升真实 UI 证据准备度的下一组实验,或等待人工恢复 DevTools 服务端口后执行地图列表视觉 smoke - -### Session 029R - -- 日期:2026-06-14 -- 分支:`codex/iter-manual-preflight-alignment` -- 本轮目标:第十八组手测准备入口 preflight 对齐实验,在 Q 组 readiness 已串联地图列表 static guard 后,让真实手测准备 helper 显式提示它会先运行 readiness preflight,并继续强调这不代表 DevTools 或真机视觉通过 -- 已完成:产品 agent 新增 `harness/manual-preflight-alignment-product-brief.md`,定义 R 组价值、非目标、与 P/Q/K/L 的关系和 9420 blocked 边界;QA/设计 agent 新增 `harness/manual-preflight-alignment-checklist.md`,覆盖 helper exact command、预期输出、ignored local JSON、manual evidence/evidence hygiene/summary 收尾和 blocked 口径;开发 agent 更新 `scripts/prepare-manual-test-run.mjs`,在 readiness gate 前输出 `Running readiness preflight before manual UI testing.`、点名 `scripts/check-devtools-readiness.mjs` 与 map list static guard,并把 Next steps 第一步改为先阅读 readiness preflight 输出;主线程同步更新 `harness/feature_list.json` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check scripts/prepare-manual-test-run.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node scripts/prepare-manual-test-run.mjs --out harness/manual-test-results.local-r-smoke.json --force`;`node scripts/check-manual-evidence.mjs harness/manual-test-results.local-r-smoke.json`;`node scripts/check-evidence-hygiene.mjs`;解析 local JSON 确认 `summary.overallStatus=not_covered` 且 passed journey 数量为 0;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;检查未写入错误日期;清理 local smoke 文件 -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/manual-preflight-alignment`,对应约定 `/tmp/street-tasks-iter-worktrees/manual-preflight-alignment`;当前分支为 `codex/iter-manual-preflight-alignment`;`bash harness/init.sh` 完整跑通,依赖 up to date,`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`,`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;helper smoke 输出 `Running readiness preflight before manual UI testing.`、`This includes scripts/check-devtools-readiness.mjs and the map list static guard.`、`Map list resilience checks passed.`、`Manual evidence checks passed.`、`Evidence hygiene checks passed.`、`Manual test run prepared.`;local JSON 解析输出 `codex/iter-manual-preflight-alignment 4d49eb1 overall=not_covered passed=0`;`git diff --check` 通过 -- 更新过的文件或工件:`harness/manual-preflight-alignment-product-brief.md`,`harness/manual-preflight-alignment-checklist.md`,`scripts/prepare-manual-test-run.mjs`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:R 组没有修改页面 UI,也没有恢复 DevTools 9420 服务端口;它只让手测准备入口更清楚地运行并展示 Q readiness。地图列表真实渲染、长内容视觉、图片加载、safe area、地图原生层、列表滚动和详情入口点击仍需 DevTools 或真机证据 -- 下一步最佳动作:提交 R 组并启动用户评测 agent,评估 R 相比 Q 是否更贴近真实手测执行入口;若继续推进,优先围绕 9420 blocked 的人工 UI 恢复或真实视觉 smoke 证据闭环,而不是继续把 static gate 当作 UI 通过 - -### Session 030S - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-visual-evidence` -- 本轮目标:第十九组地图列表真实视觉证据结构实验,在 P/Q/R 已经补齐 static guard、readiness 集成和手测准备提示后,为地图列表真实视觉 smoke 增加固定记录入口 -- 已完成:产品 agent 新增 `harness/map-list-visual-evidence-product-brief.md`,定义长标题、长正文、带图/无图、安全区、原生地图层、列表滚动和详情链路的证据槽位与通过/阻塞口径;QA agent 新增 `harness/map-list-visual-evidence-checklist.md`,覆盖环境记录、视觉项、交互项、数据变体、失败/blocked 记录、证据卫生和收尾验证;开发 agent 在 `harness/manual-test-results.example.json` 新增 `map-list-visual-smoke` journey,并保持 `status: not_covered` 和空 evidence,避免把未执行的 DevTools/真机观察写成通过;主线程同步更新 `harness/feature_list.json` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node scripts/check-json.mjs`;`node scripts/check-manual-evidence.mjs`;`node scripts/prepare-manual-test-run.mjs --out harness/manual-test-results.local-s-smoke.json --force`;`node scripts/check-manual-evidence.mjs harness/manual-test-results.local-s-smoke.json`;`node scripts/check-evidence-hygiene.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node harness/check-harness.mjs`;`git diff --check`;检查未写入错误日期;清理 local smoke 文件 -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/map-list-visual-evidence`,对应约定 `/tmp/street-tasks-iter-worktrees/map-list-visual-evidence`;当前分支为 `codex/iter-map-list-visual-evidence`;`bash harness/init.sh` 完整跑通,依赖 up to date,`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`,`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;manual evidence 检查输出 `Manual evidence checks passed.`;helper smoke 输出 readiness、map list static guard、manual evidence 和 evidence hygiene 均通过,并生成只含未覆盖/占位结果的 ignored local JSON;`map-list-visual-smoke` journey 在 example 和 local smoke 中保持 `not_covered` 且没有 passed evidence;`git diff --check` 通过 -- 更新过的文件或工件:`harness/map-list-visual-evidence-product-brief.md`,`harness/map-list-visual-evidence-checklist.md`,`harness/manual-test-results.example.json`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:S 组不修改页面 UI,也没有恢复 DevTools 9420 服务端口;它只让真实视觉 smoke 有固定证据结构。地图列表长标题、长正文、图片加载、safe area、原生地图层、列表滚动、marker/list/detail 链路仍未在 DevTools 或真机中验证 -- 下一步最佳动作:提交 S 组并启动用户评测 agent,评估 S 相比 R 是否更接近真实视觉证据闭环;若继续推进,优先执行或恢复真实 DevTools UI/真机 smoke,而不是把模板和清单当作视觉通过 - -### Session 031T - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-evidence-gate` -- 本轮目标:第二十组地图列表视觉证据必备 journey 门禁实验,在 S 组已新增 `map-list-visual-smoke` 后,防止该证据槽位未来被删除或重复而仍通过手测证据校验 -- 已完成:产品 agent 新增 `harness/map-list-evidence-gate-product-brief.md`,定义 `map-list-visual-smoke` 从文档约定升级为必备 journey gate 的价值和边界;QA agent 新增 `harness/map-list-evidence-gate-checklist.md`,覆盖正向模板、缺 journey、重复 journey、关键字段、错误状态、blocked/passed 边界、local helper 和证据卫生;开发 agent 更新 `scripts/check-manual-evidence.mjs`,要求 manual evidence JSON 中恰好包含一条 `map-list-visual-smoke` journey;主线程同步更新 `harness/feature_list.json` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check scripts/check-manual-evidence.mjs`;`node scripts/check-manual-evidence.mjs`;缺失 `map-list-visual-smoke` 的 `/tmp` 坏样例检查应失败;重复 `map-list-visual-smoke` 的 `/tmp` 坏样例检查应失败;`node scripts/prepare-manual-test-run.mjs --out harness/manual-test-results.local-t-smoke.json --force`;`node scripts/check-manual-evidence.mjs harness/manual-test-results.local-t-smoke.json`;解析 local JSON 确认 `map-list-visual-smoke=not_covered`、`passed=0`、`evidence=0`;`node scripts/check-evidence-hygiene.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`git diff --check`;检查未写入错误日期;清理 local smoke 和 `/tmp` 坏样例 -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/map-list-evidence-gate`,对应约定 `/tmp/street-tasks-iter-worktrees/map-list-evidence-gate`;当前分支为 `codex/iter-map-list-evidence-gate`;`bash harness/init.sh` 完整跑通,依赖 up to date,`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`,`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;manual evidence 正向检查输出 `Manual evidence checks passed.`;缺失坏样例输出 `Missing required journey id: map-list-visual-smoke`;重复坏样例输出 `Expected exactly one required journey id: map-list-visual-smoke; found 2.`;helper local JSON 解析保持 `map-list-visual-smoke=not_covered passed=0 evidence=0`;evidence hygiene、readiness 和 `git diff --check` 均通过 -- 更新过的文件或工件:`harness/map-list-evidence-gate-product-brief.md`,`harness/map-list-evidence-gate-checklist.md`,`scripts/check-manual-evidence.mjs`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:T 组不修改页面 UI,也没有恢复 DevTools 9420 服务端口;它只守护 S 组新增的手测证据槽位。地图列表真实视觉 smoke 仍未执行,不能写 UI passed、DevTools passed 或真机 passed -- 下一步最佳动作:提交 T 组并启动用户评测 agent,评估 T 相比 S 是否降低“证据结构未来被误删”的风险;若继续推进,优先执行真实 DevTools UI/真机 smoke 或将 blocked 结果按 T/S 结构写入 local evidence - -### Session 032U - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-blocked-evidence` -- 本轮目标:第二十一组地图列表视觉 smoke blocked evidence 演练,在 S/T 已经提供并守护 `map-list-visual-smoke` journey 后,让 DevTools 或真机不可用时也能生成合规的 ignored blocked local JSON 草稿 -- 已完成:产品 agent 新增 `harness/map-list-blocked-evidence-product-brief.md`,定义 blocked evidence 只说明环境阻塞被合规记录,不代表产品失败或 UI passed;QA agent 新增 `harness/map-list-blocked-evidence-checklist.md`,覆盖生成 local JSON、将 `map-list-visual-smoke` 写成 blocked/not_covered、坏样例、证据卫生、ignored 文件和清理;开发 agent 新增 `scripts/prepare-map-list-blocked-evidence.mjs`,从 example 生成 ignored local JSON,设置当前分支/commit/testedAt,将 `map-list-visual-smoke` 改为 blocked,并自动运行 manual evidence 与 evidence hygiene gate;主线程同步更新 `harness/feature_list.json` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check scripts/prepare-map-list-blocked-evidence.mjs`;`node scripts/prepare-map-list-blocked-evidence.mjs --out harness/manual-test-results.local-u-blocked.json --reason \"DevTools service port 9420 blocked\" --force`;`node scripts/check-manual-evidence.mjs harness/manual-test-results.local-u-blocked.json`;`node scripts/check-evidence-hygiene.mjs`;解析 local JSON 确认 `branch=codex/iter-map-list-blocked-evidence`、`summary.overallStatus=blocked`、`map-list-visual-smoke=blocked`、`passed=0`、`risks/followUp` 非空;非 ignored 输出路径被拒绝;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`git diff --check`;检查未写入错误日期;清理 local blocked 文件 -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/map-list-blocked-evidence`,对应约定 `/tmp/street-tasks-iter-worktrees/map-list-blocked-evidence`;当前分支为 `codex/iter-map-list-blocked-evidence`;helper 正向输出 `Map-list blocked evidence draft created.` 和 `Blocked evidence is not UI passed or failed evidence; it only records the blocker.`;manual evidence 与 evidence hygiene gate 均通过;local JSON 解析输出 `overall=blocked mapList=blocked passed=0 evidence=0`;非 ignored 输出路径失败并提示必须匹配 `harness/manual-test-results.local*.json`;`bash harness/init.sh` 完整跑通,`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`,`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;readiness 与 `git diff --check` 均通过 -- 更新过的文件或工件:`harness/map-list-blocked-evidence-product-brief.md`,`harness/map-list-blocked-evidence-checklist.md`,`scripts/prepare-map-list-blocked-evidence.mjs`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:U 组不修改页面 UI,也没有恢复 DevTools 9420 服务端口;它只生成可校验的 blocked local 草稿并清理该本地产物。地图列表真实视觉 smoke 仍未执行,不能写 UI passed、DevTools passed 或真机 passed -- 下一步最佳动作:提交 U 组并启动用户评测 agent,评估 U 相比 T 是否更接近真实证据闭环;若继续推进,优先实际恢复 DevTools UI/真机入口或用 U 组 helper 生成 blocked local JSON 后制作脱敏摘要草稿 - -### Session 033V - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-blocked-summary` -- 本轮目标:第二十二组地图列表 blocked summary 演练,在 U 组能生成 blocked local JSON、L 组能生成脱敏 local summary 后,提供一条 reviewer 可读的 blocked 摘要生成入口 -- 已完成:产品 agent 新增 `harness/map-list-blocked-summary-product-brief.md`,定义 blocked summary 只提升评审理解效率,不改变真实 UI 验收状态;设计/QA agent 新增 `harness/map-list-blocked-summary-checklist.md`,覆盖推荐命令链路、状态不变量、脱敏要求、负向样例、清理与报告口径;开发 agent 新增 `scripts/prepare-map-list-blocked-summary.mjs`,校验 results/summary 输出都必须是 ignored local 路径,先调用 `scripts/prepare-map-list-blocked-evidence.mjs`,再调用 `scripts/create-manual-summary.mjs`;主线程同步更新 `harness/feature_list.json` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check scripts/prepare-map-list-blocked-summary.mjs`;`node scripts/prepare-map-list-blocked-summary.mjs --reason \"DevTools service port 9420 blocked\" --results-out harness/manual-test-results.local-v-blocked.json --summary-out harness/manual-test-summary.local-v-blocked.md --force`;`node scripts/check-manual-evidence.mjs harness/manual-test-results.local-v-blocked.json`;`node scripts/check-evidence-hygiene.mjs`;解析 local JSON 和 summary 确认 `summary.overallStatus=blocked`、`map-list-visual-smoke=blocked`、`passed=0`、`evidence=0`、summary 含 `overallStatus`/`map-list-visual-smoke`/`evidenceCount` 且未把目标 journey 写成 passed;非 ignored results 路径被拒绝;非 ignored summary 路径被拒绝;缺失 `--reason` 被拒绝;带 `/Users/example/secret.png` 的敏感 local JSON 被 summary 生成器拦截且未写出 summary;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`git diff --check`;检查未写入错误日期 -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/map-list-blocked-summary`,对应约定 `/tmp/street-tasks-iter-worktrees/map-list-blocked-summary`;当前分支为 `codex/iter-map-list-blocked-summary`;wrapper 正向输出 `Created blocked result and sanitized summary.`、`Summary is not UI passed evidence...`;local JSON 解析输出 `overall=blocked mapList=blocked passed=0 evidence=0`;summary 生成输出 `Manual summary draft created.`;非 ignored results 路径提示必须匹配 `harness/manual-test-results.local*.json`;非 ignored summary 路径提示必须匹配 `harness/manual-test-summary.local*.md`;敏感路径负向输出 `Generated summary contains prohibited macOS user path on line 43.`;readiness、JSON、harness 和 `git diff --check` 均通过 -- 更新过的文件或工件:`harness/map-list-blocked-summary-product-brief.md`,`harness/map-list-blocked-summary-checklist.md`,`scripts/prepare-map-list-blocked-summary.mjs`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:V 组不修改页面 UI,也没有恢复 DevTools 9420 服务端口;它只让 U 组 blocked local JSON 更容易生成脱敏 local summary。地图列表真实视觉 smoke 仍未执行,不能写 UI passed、DevTools passed 或真机 passed;生成的 local JSON/MD 仍是 ignored 本地产物,不能提交为真实 UI 证据 -- 下一步最佳动作:提交 V 组并启动用户评测 agent,评估 V 相比 U 是否提升 blocked 证据的评审可读性且不制造 UI passed 误读;若继续推进,优先恢复 DevTools UI/真机入口或增加对 local summary 状态不变量的自动守门 - -### Session 034W - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-summary-guard` -- 本轮目标:第二十三组地图列表 blocked summary guard 实验,在 V 组已经生成 blocked local JSON 与脱敏 summary 后,增加自动守门,防止 summary 被改成 passed 或丢失 blocked 语义 -- 已完成:产品 agent 新增 `harness/map-list-summary-guard-product-brief.md`,定义 summary guard 只守 blocked JSON 与 summary 的状态一致性,不代表真实 UI 验收;QA agent 新增 `harness/map-list-summary-guard-checklist.md`,覆盖正向 wrapper 命令、guard 命令、JSON/summary 不变量、坏样例、清理与报告口径;开发 agent 新增 `scripts/check-map-list-blocked-summary.mjs`,限制 results/summary 都必须是 ignored local 路径,先运行 manual evidence 与 evidence hygiene gate,再检查 JSON `overallStatus=blocked`、唯一 `map-list-visual-smoke`、`passed=0`、目标 evidence count 为 0,并检查 summary 中 `overallStatus=blocked`、目标 journey 行 status 为 blocked 且 evidenceCount 为 0;主线程同步更新 `harness/feature_list.json` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check scripts/check-map-list-blocked-summary.mjs`;`node scripts/prepare-map-list-blocked-summary.mjs --reason \"DevTools service port 9420 blocked\" --results-out harness/manual-test-results.local-w-blocked.json --summary-out harness/manual-test-summary.local-w-blocked.md --force`;`node scripts/check-map-list-blocked-summary.mjs --results harness/manual-test-results.local-w-blocked.json --summary harness/manual-test-summary.local-w-blocked.md`;summary 目标行改成 `passed` 的坏样例应失败;summary 目标行 `evidenceCount` 改成 `1` 的坏样例应失败;JSON 目标 journey 改成 `passed` 的坏样例应失败;summary 删除 `map-list-visual-smoke` 行的坏样例应失败;非 local results 路径应失败;非 local summary 路径应失败;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`git diff --check`;检查未写入错误日期;清理 local JSON/MD 和 `/tmp` 临时输出 -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/map-list-summary-guard`,对应约定 `/tmp/street-tasks-iter-worktrees/map-list-summary-guard`;当前分支为 `codex/iter-map-list-summary-guard`;正向 guard 输出 `Map-list blocked summary checks passed.`;summary 改 passed 坏样例输出 `map-list-visual-smoke summary row must not be passed`;summary evidenceCount 改 1 坏样例输出 `summary row evidenceCount must be 0`;JSON 改 passed 坏样例先被 manual evidence gate 拦截,输出 `journey map-list-visual-smoke is passed but evidence is empty`;summary 删除目标行坏样例输出 `Summary Markdown must contain exactly one map-list-visual-smoke row; found 0.`;非 local results/summary 路径均被拒绝;readiness、JSON、harness 和 `git diff --check` 均通过 -- 更新过的文件或工件:`harness/map-list-summary-guard-product-brief.md`,`harness/map-list-summary-guard-checklist.md`,`scripts/check-map-list-blocked-summary.mjs`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:W 组不修改页面 UI,也没有恢复 DevTools 9420 服务端口;它只验证 ignored local JSON 与 ignored local summary 的 blocked 状态一致性。地图列表真实视觉 smoke 仍未执行,不能写 UI passed、DevTools passed 或真机 passed;guard 通过也不能替代真实 UI 证据 -- 下一步最佳动作:提交 W 组并启动用户评测 agent,评估 W 相比 V 是否降低 summary 被误改成 passed 的风险;若继续推进,优先恢复 DevTools UI/真机入口,或将 W guard 串入 V wrapper 输出后的推荐流程 - -### Session 035X - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-summary-integrity` -- 本轮目标:第二十四组地图列表 blocked summary 同源完整性实验,在 W 组已守住 blocked 状态不变量后,进一步确认 summary 的 branch、commit、actual、followUp 和 blocker/risk 内容仍来自同一份 ignored blocked JSON -- 已完成:产品 agent 新增 `harness/map-list-summary-integrity-product-brief.md`,定义同源完整性只证明 summary 是 blocked JSON 的可信转述,不代表真实 UI 验收;QA agent 新增 `harness/map-list-summary-integrity-checklist.md`,覆盖正向 wrapper/guard 命令、W 组不变量、新增 branch/commit/actual/followUp/blocker 不变量、坏样例、清理与报告口径;开发 agent 增强 `scripts/check-map-list-blocked-summary.mjs`,在原有 status/evidenceCount 检查上增加 Run branch/commit 与 JSON 比对、目标 journey actual/followUp 关键片段比对、目标 blocker 非空且包含 JSON risks 关键片段,并处理 `
`、空白压缩与 escaped pipe;主线程同步更新 `harness/feature_list.json` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check scripts/check-map-list-blocked-summary.mjs`;`node scripts/prepare-map-list-blocked-summary.mjs --reason \"DevTools service port 9420 blocked\" --results-out harness/manual-test-results.local-x-blocked.json --summary-out harness/manual-test-summary.local-x-blocked.md --force`;`node scripts/check-map-list-blocked-summary.mjs --results harness/manual-test-results.local-x-blocked.json --summary harness/manual-test-summary.local-x-blocked.md`;summary branch 改错坏样例应失败;summary commit 改错坏样例应失败;summary actual 替换为 unrelated text 坏样例应失败;summary followUp 替换为 unrelated text 坏样例应失败;summary blocker 替换为 unrelated text 坏样例应失败;非 local results 路径应失败;非 local summary 路径应失败;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`git diff --check`;检查未写入错误日期;清理 local JSON/MD 和 `/tmp` 临时输出 -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/map-list-summary-integrity`,对应约定 `/tmp/street-tasks-iter-worktrees/map-list-summary-integrity`;当前分支为 `codex/iter-map-list-summary-integrity`;正向 guard 输出 `Map-list blocked summary checks passed.`;branch 坏样例输出 `Run branch must match blocked results JSON branch`;commit 坏样例输出 `Run commit must match blocked results JSON commit`;actual 坏样例输出 `summary row actual must include the matching JSON actual fragment`;followUp 坏样例输出 `summary row followUp must include the matching JSON followUp fragment`;blocker 坏样例输出 `summary row blocker must include at least one JSON risk phrase`;非 local results/summary 路径均被拒绝;readiness、JSON、harness 和 `git diff --check` 均通过 -- 更新过的文件或工件:`harness/map-list-summary-integrity-product-brief.md`,`harness/map-list-summary-integrity-checklist.md`,`scripts/check-map-list-blocked-summary.mjs`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:X 组不修改页面 UI,也没有恢复 DevTools 9420 服务端口;它只验证 ignored local JSON 与 ignored local summary 的同源摘要完整性。地图列表真实视觉 smoke 仍未执行,不能写 UI passed、DevTools passed 或真机 passed;同源 guard 通过也不能替代真实 UI 证据 -- 下一步最佳动作:提交 X 组并启动用户评测 agent,评估 X 相比 W 是否降低 summary 跨 run/跨 blocker 拼接的风险;若继续推进,优先恢复 DevTools UI/真机入口,或将增强后的 summary guard 串入 blocked summary wrapper 的推荐输出 - -### Session 036Y - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-summary-wrapper-guarded` -- 本轮目标:第二十五组地图列表 blocked summary wrapper guard 实验,在 X 组已增强 summary 同源 guard 后,把该 guard 接入 blocked summary wrapper,避免生成 JSON/summary 后漏跑守门检查 -- 已完成:产品 agent 新增 `harness/map-list-summary-wrapper-guarded-product-brief.md`,定义 wrapper 默认串 guard 的价值和边界;QA agent 新增 `harness/map-list-summary-wrapper-guarded-checklist.md`,覆盖正向 wrapper+guard 输出、ignored local 路径、非 local summary 路径失败、损坏 summary 回归、清理与报告口径;开发 agent 更新 `scripts/prepare-map-list-blocked-summary.mjs`,在 `scripts/create-manual-summary.mjs` 成功后立即调用 `scripts/check-map-list-blocked-summary.mjs --results --summary `,并输出 `Blocked summary guard passed.`;主线程同步更新 `harness/feature_list.json` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check scripts/prepare-map-list-blocked-summary.mjs`;`node --check scripts/check-map-list-blocked-summary.mjs`;`node scripts/prepare-map-list-blocked-summary.mjs --reason \"DevTools service port 9420 blocked\" --results-out harness/manual-test-results.local-y-blocked.json --summary-out harness/manual-test-summary.local-y-blocked.md --force`;`node scripts/check-map-list-blocked-summary.mjs --results harness/manual-test-results.local-y-blocked.json --summary harness/manual-test-summary.local-y-blocked.md`;非 local summary 输出路径应失败且不生成 results;summary commit 改错坏样例应失败;summary 目标行改成 passed 坏样例应失败;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`git diff --check`;检查未写入错误日期;清理 local JSON/MD 和 `/tmp` 临时输出 -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/map-list-summary-wrapper-guarded`,对应约定 `/tmp/street-tasks-iter-worktrees/map-list-summary-wrapper-guarded`;当前分支为 `codex/iter-map-list-summary-wrapper-guarded`;wrapper 正向输出 `Map-list blocked evidence draft created.`、`Manual summary draft created.`、`Map-list blocked summary checks passed.`、`Blocked summary guard passed.` 和 `Summary is not UI passed evidence...`;单独 guard 正向输出 `Map-list blocked summary checks passed.`;非 local summary 路径提示必须匹配 `harness/manual-test-summary.local*.md` 且未生成 bad results;commit 坏样例输出 `Run commit must match blocked results JSON commit`;passed 坏样例输出 `map-list-visual-smoke summary row must not be passed`;readiness、JSON、harness 和 `git diff --check` 均通过 -- 更新过的文件或工件:`harness/map-list-summary-wrapper-guarded-product-brief.md`,`harness/map-list-summary-wrapper-guarded-checklist.md`,`scripts/prepare-map-list-blocked-summary.mjs`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:Y 组不修改页面 UI,也没有恢复 DevTools 9420 服务端口;它只确保 wrapper 生成 blocked local JSON 和 summary 后默认跑同源 guard。地图列表真实视觉 smoke 仍未执行,不能写 UI passed、DevTools passed 或真机 passed;wrapper+guard 成功也不能替代真实 UI 证据 -- 下一步最佳动作:提交 Y 组并启动用户评测 agent,评估 Y 相比 X 是否降低执行者漏跑 guard 的风险;若继续推进,优先恢复 DevTools UI/真机入口,或给 wrapper 输出增加更明确的下一步真实 UI smoke 指引 - -### Session 037Z - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-summary-postedit-guard` -- 本轮目标:第二十六组地图列表 blocked summary 后编辑 guard 提示实验,在 Y 组 wrapper 已默认运行同源 guard 后,进一步降低生成后手工编辑 JSON/Markdown 却沿用旧 guard 结果的误用风险 -- 已完成:产品 agent 新增 `harness/map-list-summary-postedit-guard-product-brief.md`,定义 wrapper 自动 guard 的可信边界和生成后编辑必须复跑 guard 的产品口径;设计/QA agent 新增 `harness/map-list-summary-postedit-guard-checklist.md`,覆盖正向生成、后编辑篡改失败、修复路径、不能证明 UI 通过和未验证项;开发 agent 更新 `scripts/prepare-map-list-blocked-summary.mjs`,在成功输出中打印 `Post-edit rerun guard` 以及当前 JSON/MD 对应的 `node scripts/check-map-list-blocked-summary.mjs --results ... --summary ...` 命令;主线程同步更新 `harness/feature_list.json` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check scripts/prepare-map-list-blocked-summary.mjs`;`node --check scripts/check-map-list-blocked-summary.mjs`;`node scripts/prepare-map-list-blocked-summary.mjs --reason "DevTools service port 9420 blocked" --results-out harness/manual-test-results.local-z-postedit.json --summary-out harness/manual-test-summary.local-z-postedit.md --force`;`node scripts/check-map-list-blocked-summary.mjs --results harness/manual-test-results.local-z-postedit.json --summary harness/manual-test-summary.local-z-postedit.md`;summary 目标行改成 `passed` 的坏样例应失败;summary commit 改错坏样例应失败;`git diff --check`;清理 local JSON/MD -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/map-list-summary-postedit-guard`,对应约定 `/tmp/street-tasks-iter-worktrees/map-list-summary-postedit-guard`;当前分支为 `codex/iter-map-list-summary-postedit-guard`;wrapper 正向输出 `Map-list blocked evidence draft created.`、`Manual summary draft created.`、`Map-list blocked summary checks passed.`、`Blocked summary guard passed.`、`Post-edit rerun guard`、`node scripts/check-map-list-blocked-summary.mjs --results harness/manual-test-results.local-z-postedit.json --summary harness/manual-test-summary.local-z-postedit.md` 和 `Summary is not UI passed evidence...`;单独 guard 正向输出 `Map-list blocked summary checks passed.`;passed 坏样例输出 `map-list-visual-smoke summary row must not be passed`;commit 坏样例输出 `Summary Markdown Run commit must match blocked results JSON commit`;`git diff --check` 通过 -- 更新过的文件或工件:`harness/map-list-summary-postedit-guard-product-brief.md`,`harness/map-list-summary-postedit-guard-checklist.md`,`scripts/prepare-map-list-blocked-summary.mjs`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:Z 组不修改页面 UI,也没有恢复 DevTools 9420 服务端口;它只让 wrapper 成功输出更明确地要求生成后编辑必须复跑 guard。地图列表真实视觉 smoke 仍未执行,不能写 UI passed、DevTools passed 或真机 passed;post-edit guard 通过也不能替代真实 UI 证据 -- 下一步最佳动作:提交 Z 组并启动用户评测 agent,评估 Z 相比 Y 是否降低“生成后手工编辑但不复跑 guard”的误用风险;若继续推进,优先恢复 DevTools UI/真机入口,或把当前 blocked evidence 链路整理成更高层的人工验收准入说明 - -### Session 038AA - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-summary-preflight` -- 本轮目标:第二十七组地图列表 blocked summary 评审前 preflight 实验,在 Z 组已打印单对 post-edit guard 命令后,提供一键扫描 ignored local blocked summary/result 对并逐对复跑 guard 的入口 -- 已完成:产品 agent 新增 `harness/map-list-summary-preflight-product-brief.md`,定义一键 preflight 的评审价值、验收标准和非 UI passed 边界;设计/QA agent 新增 `harness/map-list-summary-preflight-checklist.md`,覆盖无 local summary、正向扫描一对、缺 results JSON、summary 被改 passed、清理和报告口径;开发 agent 新增 `scripts/check-map-list-blocked-summary-preflight.mjs`,扫描 `harness/manual-test-summary.local*.md`,派生对应 `harness/manual-test-results.local*.json`,并对每对调用 `scripts/check-map-list-blocked-summary.mjs`;开发 agent同时更新 `scripts/prepare-map-list-blocked-summary.mjs`,在成功输出中提示评审前可运行 `node scripts/check-map-list-blocked-summary-preflight.mjs`;主线程补充 preflight 输出,明确它不是 UI passed evidence;主线程同步更新 `harness/feature_list.json` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check scripts/check-map-list-blocked-summary-preflight.mjs`;`node --check scripts/prepare-map-list-blocked-summary.mjs`;无 local summary 时运行 `node scripts/check-map-list-blocked-summary-preflight.mjs` 应通过;`node scripts/prepare-map-list-blocked-summary.mjs --reason "DevTools service port blocked; map-list visual smoke was not executed." --results-out harness/manual-test-results.local-aa-preflight.json --summary-out harness/manual-test-summary.local-aa-preflight.md --force`;正向运行 `node scripts/check-map-list-blocked-summary-preflight.mjs` 应通过;删除对应 results JSON 后 preflight 应失败;重新生成 pair 后把 summary 目标行改成 `passed`,preflight 应失败;清理 local JSON/MD 和 `/tmp` 临时文件 -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/map-list-summary-preflight`,对应约定 `/tmp/street-tasks-iter-worktrees/map-list-summary-preflight`;当前分支为 `codex/iter-map-list-summary-preflight`;无 local summary 输出 `No local blocked summary files found; nothing checked.` 和 `Preflight is not UI passed evidence...`;wrapper 输出 `Review preflight: before citing local blocked summaries, run:` 以及 `node scripts/check-map-list-blocked-summary-preflight.mjs`;正向 preflight 输出 `Checking harness/manual-test-summary.local-aa-preflight.md with harness/manual-test-results.local-aa-preflight.json.`、`Map-list blocked summary checks passed.`、`Map-list blocked summary preflight passed. Checked 1 pair(s).` 和 `Preflight is not UI passed evidence...`;缺 results 负向输出 `Missing results JSON for harness/manual-test-summary.local-aa-preflight.md; expected harness/manual-test-results.local-aa-preflight.json.`;passed 篡改负向输出 `map-list-visual-smoke summary row must not be passed` 和 `Map-list blocked summary preflight failed for 1 item(s).` -- 更新过的文件或工件:`harness/map-list-summary-preflight-product-brief.md`,`harness/map-list-summary-preflight-checklist.md`,`scripts/check-map-list-blocked-summary-preflight.mjs`,`scripts/prepare-map-list-blocked-summary.mjs`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:AA 组不修改页面 UI,也没有恢复 DevTools 9420 服务端口;它只让评审前更容易统一复跑 ignored local blocked summary guard。地图列表真实视觉 smoke 仍未执行,不能写 UI passed、DevTools passed 或真机 passed;preflight 通过也不能替代真实 UI 证据 -- 下一步最佳动作:提交 AA 组并启动用户评测 agent,评估 AA 相比 Z 是否降低 reviewer 漏跑单对 guard 的风险;若继续推进,优先恢复 DevTools UI/真机入口,或把 blocked evidence/preflight 流程接入更靠近提交前的 harness/readiness 检查但仍保持不提交 ignored local 证据 - -### Session 039AB - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-summary-readiness-preflight` -- 本轮目标:第二十八组地图列表 blocked summary 默认入口 preflight 实验,在 AA 组已有一键 preflight 后,把它接入常规启动和 DevTools readiness 检查,降低评审前忘记运行 preflight 的风险 -- 已完成:产品 agent 新增 `harness/map-list-summary-readiness-preflight-product-brief.md`,定义默认入口运行 blocked summary preflight 的价值、验收和非 UI passed 边界;设计/QA agent 新增 `harness/map-list-summary-readiness-preflight-checklist.md`,覆盖无 local summary、正向 pair、缺 results JSON、summary 改 passed、清理和报告口径;开发 agent 更新 `harness/init.sh`,在 JSON/harness 检查后运行 `node scripts/check-map-list-blocked-summary-preflight.mjs`;开发 agent 更新 `scripts/check-devtools-readiness.mjs`,把 blocked summary preflight 接入 readiness,并输出它不证明 DevTools 或真机 UI passed;主线程同步更新 `harness/feature_list.json` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check scripts/check-map-list-blocked-summary-preflight.mjs`;`node --check scripts/check-devtools-readiness.mjs`;无 local summary 时运行 `bash harness/init.sh` 应通过;无 local summary 时运行 `node --no-warnings scripts/check-devtools-readiness.mjs` 应通过;生成 `harness/manual-test-results.local-ab-readiness.json` 和 `harness/manual-test-summary.local-ab-readiness.md` 后,`bash harness/init.sh` 和 readiness 均应通过;删除对应 results JSON 后 `bash harness/init.sh` 应失败;重新生成 pair 并把 summary 目标行改成 `passed` 后 readiness 应失败;清理 local JSON/MD -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/map-list-summary-readiness-preflight`,对应约定 `/tmp/street-tasks-iter-worktrees/map-list-summary-readiness-preflight`;当前分支为 `codex/iter-map-list-summary-readiness-preflight`;无 local summary 的 init 输出 `+ node scripts/check-map-list-blocked-summary-preflight.mjs`、`No local blocked summary files found; nothing checked.` 和 `Preflight is not UI passed evidence...`;无 local summary 的 readiness 输出 `Running blocked summary preflight. This preflight does not prove DevTools or real-device UI passed.`;正向 pair 的 init/readiness 输出 `Checking harness/manual-test-summary.local-ab-readiness.md with harness/manual-test-results.local-ab-readiness.json.`、`Map-list blocked summary checks passed.` 和 `Map-list blocked summary preflight passed. Checked 1 pair(s).`;缺 results 负向输出 `Missing results JSON for harness/manual-test-summary.local-ab-readiness.md; expected harness/manual-test-results.local-ab-readiness.json.`;passed 篡改负向输出 `map-list-visual-smoke summary row must not be passed` 且 readiness gate failed -- 更新过的文件或工件:`harness/map-list-summary-readiness-preflight-product-brief.md`,`harness/map-list-summary-readiness-preflight-checklist.md`,`harness/init.sh`,`scripts/check-devtools-readiness.mjs`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:AB 组不修改页面 UI,也没有恢复 DevTools 9420 服务端口;它只让常规启动和 readiness 默认复跑 ignored local blocked summary guard。地图列表真实视觉 smoke 仍未执行,不能写 UI passed、DevTools passed 或真机 passed;默认入口 preflight 通过也不能替代真实 UI 证据 -- 下一步最佳动作:提交 AB 组并启动用户评测 agent,评估 AB 相比 AA 是否降低人工忘记运行 preflight 的风险;若继续推进,优先恢复 DevTools UI/真机入口,或改善 failed readiness 输出可读性但不改变 blocked evidence 语义 - -### Session 040AC - -- 日期:2026-06-14 -- 分支:`codex/iter-package-readiness-gate` -- 本轮目标:第二十九组 npm 级 readiness gate 实验,在 AB 组已将 blocked summary preflight 接入默认入口后,把 JSON、harness、readiness/default preflight 暴露为更常见的 npm 检查命令,方便人工和自动化统一调用 -- 已完成:产品 agent 新增 `harness/package-readiness-gate-product-brief.md`,定义 npm 级检查入口的价值、验收和非 UI passed 边界;设计/QA agent 新增 `harness/package-readiness-gate-checklist.md`,覆盖各 npm script、正向 local pair、缺 results JSON、summary 改 passed、清理和报告口径;开发 agent 更新 `package.json`,新增 `check`、`check:harness`、`check:blocked-summary`、`check:readiness`,并保留 `check:json`;主线程同步更新 `harness/feature_list.json` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`npm run check:json`;`npm run check:harness`;`npm run check:blocked-summary`;`npm run check:readiness`;`npm run check`;生成 `harness/manual-test-results.local-ac-package.json` 和 `harness/manual-test-summary.local-ac-package.md` 后 `npm run check` 应通过;删除对应 results JSON 后 `npm run check` 应失败;重新生成 pair 并把 summary 目标行改成 `passed` 后 `npm run check:readiness` 应失败;清理 local JSON/MD -- 已记录证据:`npm run check:json` 输出 `Checked 11 JSON files.`;`npm run check:harness` 输出 `Harness OK: 6 features checked.`;无 local summary 的 `npm run check:blocked-summary` 输出 `No local blocked summary files found; nothing checked.` 和 `Preflight is not UI passed evidence...`;无 local summary 的 `npm run check:readiness` 输出 blocked summary preflight 口径且通过;`npm run check` 串联 `check:json`、`check:harness`、`check:readiness` 并通过;正向 local pair 的 `npm run check` 输出 `Checking harness/manual-test-summary.local-ac-package.md with harness/manual-test-results.local-ac-package.json.`、`Map-list blocked summary checks passed.` 和 `Map-list blocked summary preflight passed. Checked 1 pair(s).`;缺 results 负向输出 `Missing results JSON for harness/manual-test-summary.local-ac-package.md; expected harness/manual-test-results.local-ac-package.json.`;passed 篡改负向输出 `map-list-visual-smoke summary row must not be passed` -- 更新过的文件或工件:`harness/package-readiness-gate-product-brief.md`,`harness/package-readiness-gate-checklist.md`,`package.json`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:AC 组不修改页面 UI,也没有恢复 DevTools 9420 服务端口;它只让本地和未来自动化更容易调用现有静态/readiness/preflight 门禁。地图列表真实视觉 smoke 仍未执行,不能写 UI passed、DevTools passed 或真机 passed;`npm run check` 通过也不能替代真实 UI 证据 -- 下一步最佳动作:提交 AC 组并启动用户评测 agent,评估 AC 相比 AB 是否降低“默认入口存在但调用不统一”的风险;若继续推进,优先恢复 DevTools UI/真机入口,或设计不强制安装的可选 git hook/CI 文档,但避免把本地 blocked evidence 当发布准入 - -### Session 041AD - -- 日期:2026-06-14 -- 分支:`codex/iter-ci-readiness-gate` -- 本轮目标:第三十组 CI readiness gate 实验,在 AC 组已有 `npm run check` 后,新增最小 GitHub Actions workflow,让 push 和 pull_request 自动运行 JSON、harness、readiness 和 blocked summary preflight 门禁 -- 已完成:产品 agent 新增 `harness/ci-readiness-gate-product-brief.md`,定义 CI 门禁价值、验收和非 UI passed 边界;设计/QA agent 新增 `harness/ci-readiness-gate-checklist.md`,覆盖 workflow 结构、npm check 正向、缺 results/summary passed 负向、清理和报告口径;开发 agent 新增 `.github/workflows/readiness.yml`,在 push/pull_request 上用 Ubuntu、Node 20、`npm ci --ignore-scripts` 和 `npm run check` 执行门禁,并设置 `contents: read` 权限;主线程同步更新 `harness/feature_list.json` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`ruby -e 'require "yaml"; YAML.load_file(".github/workflows/readiness.yml"); puts "workflow yaml ok"'`;workflow 结构检查;workflow 单独扫描确认不含 secrets、artifact、local evidence 或 UI passed 口径;`npm run check`;生成 `harness/manual-test-results.local-ad-ci.json` 和 `harness/manual-test-summary.local-ad-ci.md` 后 `npm run check` 应通过;删除对应 results JSON 后 `npm run check` 应失败;重新生成 pair 并把 summary 目标行改成 `passed` 后 `npm run check` 应失败;清理 local JSON/MD -- 已记录证据:workflow YAML 解析输出 `workflow yaml ok`,结构检查输出 `workflow structure ok`;workflow 中包含 `on: push`、`pull_request`、`permissions: contents: read`、`actions/checkout@v4`、`actions/setup-node@v4`、`npm ci --ignore-scripts` 和 `npm run check`;workflow 单独敏感词扫描无命中;无 local summary 的 `npm run check` 通过并输出 `Preflight is not UI passed evidence...`;正向 local pair 的 `npm run check` 输出 `Checking harness/manual-test-summary.local-ad-ci.md with harness/manual-test-results.local-ad-ci.json.`、`Map-list blocked summary checks passed.` 和 `Map-list blocked summary preflight passed. Checked 1 pair(s).`;缺 results 负向输出 `Missing results JSON for harness/manual-test-summary.local-ad-ci.md; expected harness/manual-test-results.local-ad-ci.json.`;passed 篡改负向输出 `map-list-visual-smoke summary row must not be passed` -- 更新过的文件或工件:`.github/workflows/readiness.yml`,`harness/ci-readiness-gate-product-brief.md`,`harness/ci-readiness-gate-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:AD 组不修改页面 UI,也没有恢复 DevTools 9420 服务端口;它只让 `npm run check` 具备未来 push/PR 自动化入口。地图列表真实视觉 smoke 仍未执行,不能写 UI passed、DevTools passed 或真机 passed;本地未验证 GitHub Actions 在远端仓库真实触发、分支保护或 PR required check 配置 -- 下一步最佳动作:提交 AD 组并启动用户评测 agent,评估 AD 相比 AC 是否降低“本地命令存在但没有自动化门禁”的风险;若继续推进,优先恢复 DevTools UI/真机入口,或改善 readiness 失败输出可读性并避免断言真实 UI 通过 - -### Session 042AE - -- 日期:2026-06-14 -- 分支:`codex/iter-ci-required-check-runbook` -- 本轮目标:第三十一组 CI required-check runbook 实验,在 AD 组已有 GitHub Actions workflow 后,补充远端 workflow 触发验证、required check 名称确认和 branch protection 配置步骤,同时避免声称远端已通过或分支保护已配置 -- 已完成:产品 agent 新增 `harness/ci-required-check-runbook-product-brief.md`,定义 runbook 的目标、用户价值、验收和非 UI passed 边界;设计/QA agent 新增 `harness/ci-required-check-runbook-checklist.md`,覆盖本地 `npm run check`、workflow YAML、远端 Actions 验证、required check 名称、branch protection 配置和未验证口径;开发 agent 更新 `.github/workflows/readiness.yml`,为 `readiness` job 添加稳定显示名 `readiness / npm run check`;开发 agent 新增 `harness/ci-required-check-runbook.md`,说明如何验证远端 workflow、读取实际 check 名称、配置 required status check、记录证据和报告未验证状态;主线程同步更新 `harness/feature_list.json` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`npm run check`;`ruby -e 'require "yaml"; y=YAML.load_file(".github/workflows/readiness.yml"); puts "workflow=#{y["name"]}; job=#{y.dig("jobs", "readiness", "name")}"'`;`git diff --check`;`rg` 检查 runbook 包含 remote Actions、required status checks、branch protection、unverified、workflow run URL 等关键口径;workflow/runbook 扫描确认没有 secrets、artifact、local evidence 引用或虚假 UI passed 声称 -- 已记录证据:`npm run check` 通过并继续输出 static/readiness/preflight 非 UI passed 口径;workflow YAML 解析输出 `workflow=Readiness checks; job=readiness / npm run check`;runbook 明确 `Do not record the workflow as verified until a remote run exists and its final status has been checked in GitHub.`;runbook 明确 `Do not mark branch protection as configured until the repository settings show the readiness check as required and a pull request displays it as a required check.`;runbook 证据模板包含 `Workflow run URL`、`Observed check name` 和 `Required check configured` -- 更新过的文件或工件:`.github/workflows/readiness.yml`,`harness/ci-required-check-runbook.md`,`harness/ci-required-check-runbook-product-brief.md`,`harness/ci-required-check-runbook-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:AE 组不修改页面 UI,也没有恢复 DevTools 9420 服务端口;它只把远端 Actions 和 branch protection 验证步骤文档化并稳定 job display name。地图列表真实视觉 smoke 仍未执行,不能写 UI passed、DevTools passed 或真机 passed;本地仍未实际验证远端 GitHub Actions 触发、分支保护 required check 生效或 PR 合并阻断 -- 下一步最佳动作:提交 AE 组并启动用户评测 agent,评估 AE 相比 AD 是否降低“有 workflow 但远端触发和 required check 配置不可验证”的风险;若继续推进,优先恢复 DevTools UI/真机入口,或在有远端权限时实际执行 runbook 并记录真实 workflow run URL 与 branch protection 结果 - -### Session 043AF - -- 日期:2026-06-14 -- 分支:`codex/iter-devtools-smoke-command` -- 本轮目标:第三十二组 DevTools smoke 手动命令入口实验,在 AE 已补充远端 CI runbook 但本机 DevTools 9420 端口仍 blocked 后,把真实 UI smoke 的端口诊断和 strict smoke 入口暴露为明确 npm 命令,同时不把本机 GUI 依赖加入默认 `npm run check` 或 CI -- 已完成:产品 agent 新增 `harness/devtools-smoke-command-product-brief.md`,定义 AF 的价值、非目标、当前 blocked 预期和“不声称 UI smoke 通过”的边界;设计 agent 新增 `harness/devtools-smoke-command-design-note.md`,统一 `blocked`、`unverified`、`passing` 口径和 `inspect:*` / `check:*` 命名体验;QA agent 新增 `harness/devtools-smoke-command-checklist.md`,覆盖 package scripts、当前 blocked 记录、恢复 Service Port 后复测和证据字段;开发 agent 更新 `package.json`,新增 `inspect:devtools-port` 与 `check:devtools-smoke`,并保持默认 `check` 不运行 strict DevTools smoke;主线程同步更新 `harness/feature_list.json` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node scripts/check-json.mjs`;`npm run inspect:devtools-port`;`npm run check:devtools-smoke`;`npm run check`;`git diff --check`;检查未写入错误日期 -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/devtools-smoke-command`,对应约定 `/tmp/street-tasks-iter-worktrees/devtools-smoke-command`;当前分支为 `codex/iter-devtools-smoke-command`;`bash harness/init.sh` 完整跑通,`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`,`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`npm run inspect:devtools-port` 退出 0 并输出 `status: blocked`、`diagnosis: declared_without_listener, connect_refused`、`lsof listener: no (no listener rows)`、`socket connect: no (127.0.0.1: ECONNREFUSED; ::1: ECONNREFUSED)`、`process scan: yes (21 related process(es), 1 declaring requested port)`;`npm run check:devtools-smoke` 按预期以 strict 模式非零退出并输出 `status: blocked`、`service port 9420: no`、`ide-http-port process: yes (1 matching declaration(s), 1 DevTools-like)`、`requested DevTools service port is not listening`;`npm run check` 仍只串联 JSON、harness 和 readiness/default preflight 并通过;`git diff --check` 通过 -- 更新过的文件或工件:`package.json`,`harness/devtools-smoke-command-product-brief.md`,`harness/devtools-smoke-command-design-note.md`,`harness/devtools-smoke-command-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:AF 组不修改页面 UI,也没有恢复 DevTools 9420 服务端口;它只把端口诊断和 strict smoke 入口变成显式手动命令。`check:devtools-smoke` 当前 blocked 是环境阻塞证据,不是地图、列表、发布或详情 UI 失败;真实 DevTools UI smoke 仍未执行,不能写 UI passed、DevTools passed 或真机 passed -- 下一步最佳动作:提交 AF 组并启动用户评测 agent,评估 AF 相比 AE 是否更直接暴露当前真实 DevTools blocker;若继续推进,优先在有用户操作配合时启用 WeChat DevTools Service Port 并复跑 strict smoke,或围绕 blocked/ready 转换补充更高层恢复准入说明 - -### Session 044AG - -- 日期:2026-06-14 -- 分支:`codex/iter-devtools-recovery-command` -- 本轮目标:第三十三组 DevTools recovery dry-run 手动入口实验,在 AF 已能明确诊断和 strict smoke blocked 后,把已有 recovery helper 的无副作用干跑模式暴露为 npm 命令,帮助执行者看到 before/actions/after/next steps,但不默认退出或重新打开 DevTools -- 已完成:产品 agent 新增 `harness/devtools-recovery-command-product-brief.md`,定义 recovery dry-run 的用户价值、非目标、使用场景和当前 blocked 预期;设计 agent 新增 `harness/devtools-recovery-command-design-note.md`,定义 before status、actions attempted/skipped、after status、next steps 四段报告结构和 side-effect 文案边界;QA agent 新增 `harness/devtools-recovery-command-checklist.md`,覆盖 package script、dry-run 输出、显式 side-effect 恢复复测和证据格式;开发 agent 更新 `package.json`,新增 `inspect:devtools-recovery` 并保持默认 `check` 不运行 recovery dry-run 或 `--quit-reopen`;主线程同步更新 `harness/feature_list.json` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node scripts/check-json.mjs`;`npm run inspect:devtools-recovery`;`npm run check`;`git diff --check`;检查未写入错误日期 -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/devtools-recovery-command`,对应约定 `/tmp/street-tasks-iter-worktrees/devtools-recovery-command`;当前分支为 `codex/iter-devtools-recovery-command`;`bash harness/init.sh` 完整跑通,`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`,`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`npm run inspect:devtools-recovery` 退出 0 并输出 `WeChat DevTools service port recovery report`、`mode: dry-run diagnostics`、`Before status: status: blocked`、`Actions attempted/skipped` 中 DevTools quit、reopen wait、DevTools open 均为 `skipped because --dry-run was requested`、`After status: status: blocked`、`Next steps` 提示如需恢复须显式 `--quit-reopen` 且实际 UI journey 需另行手测;`npm run check` 仍只串联 JSON、harness 和 readiness/default preflight 并通过;`git diff --check` 通过 -- 更新过的文件或工件:`package.json`,`harness/devtools-recovery-command-product-brief.md`,`harness/devtools-recovery-command-design-note.md`,`harness/devtools-recovery-command-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:AG 组不修改页面 UI,也没有恢复 DevTools 9420 服务端口;它只提供无副作用 recovery dry-run 报告入口。dry-run before/after blocked 是环境仍阻塞且恢复未执行的证据,不是恢复失败的产品 bug,也不是地图、列表、发布或详情 UI 失败。真实 DevTools UI smoke 仍未执行,不能写 UI passed、DevTools passed 或真机 passed -- 下一步最佳动作:提交 AG 组并启动用户评测 agent,评估 AG 相比 AF 是否降低“诊断 blocked 后不知道恢复动作边界”的风险;若继续推进,优先由用户在 WeChat DevTools UI 中启用 Service Port 后复跑 AF/AG 命令,或在明确接受 side effect 时直接运行带 `--quit-reopen` 的 node 命令并记录 side effects - -### Session 045AH - -- 日期:2026-06-15 -- 分支:`codex/iter-devtools-recovery-report` -- 本轮目标:第三十四组 DevTools recovery dry-run local report 实验,在 AG 已有无副作用 recovery dry-run 命令后,把该输出保存为 ignored local Markdown 草稿,并用 guard 防止交接报告被误写成恢复成功或 UI smoke passed -- 已完成:产品 agent 新增 `harness/devtools-recovery-report-product-brief.md`,定义 ignored local report 的交接价值、非目标、报告字段和 guard 要求;设计 agent 新增 `harness/devtools-recovery-report-design-note.md`,定义 run metadata、guard status、raw dry-run report、next action 的信息层级和避免写法;QA agent 新增 `harness/devtools-recovery-report-checklist.md`,覆盖 ignored 路径、package scripts、正向生成、负向篡改和清理;开发 agent 新增 `scripts/prepare-devtools-recovery-report.mjs` 与 `scripts/check-devtools-recovery-report.mjs`,更新 `.gitignore` 和 `package.json`,并保持默认 `npm run check` 不运行 local report 工具;主线程同步更新 `harness/feature_list.json` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check scripts/prepare-devtools-recovery-report.mjs`;`node --check scripts/check-devtools-recovery-report.mjs`;`node scripts/check-json.mjs`;`npm run prepare:devtools-recovery-report -- --out harness/devtools-recovery-report.local-ah.md --force`;`npm run check:devtools-recovery-report -- --report harness/devtools-recovery-report.local-ah.md`;非 ignored output 路径负向;已有报告无 `--force` 负向;`UI smoke passed`、`DevTools recovered`、`恢复成功` 三类篡改负向;`npm run check`;`git diff --check`;清理 local report 文件 -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/devtools-recovery-report`,对应约定 `/tmp/street-tasks-iter-worktrees/devtools-recovery-report`;当前分支为 `codex/iter-devtools-recovery-report`;正向 prepare 输出 `DevTools recovery report checks passed.`、`Report: harness/devtools-recovery-report.local-ah.md`、`DevTools recovery report guard passed.`、`This report is not UI passed evidence.`;生成的 local report 包含 branch `codex/iter-devtools-recovery-report`、commit `e6a5cf2`、command `node scripts/recover-devtools-service-port.mjs --dry-run`、exitCode `0`、`mode: dry-run diagnostics`、before/after `status: blocked`、DevTools quit/reopen wait/DevTools open 均 `skipped because --dry-run was requested`;单独 guard 输出 `DevTools recovery report checks passed.`;非 ignored output 路径被拒绝;已有报告无 `--force` 被拒绝;三类篡改分别输出 `Report must not claim unverified success: UI smoke passed`、`DevTools recovered`、`恢复成功`;`npm run check` 仍通过且不调用 local report 工具;local report 文件已清理 -- 更新过的文件或工件:`.gitignore`,`package.json`,`scripts/prepare-devtools-recovery-report.mjs`,`scripts/check-devtools-recovery-report.mjs`,`harness/devtools-recovery-report-product-brief.md`,`harness/devtools-recovery-report-design-note.md`,`harness/devtools-recovery-report-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:AH 组不修改页面 UI,也没有恢复 DevTools 9420 服务端口;它只生成并校验 ignored local recovery dry-run 草稿。报告 guard 通过只证明草稿仍是 dry-run/actions skipped/非通过声明,不能写 UI passed、DevTools recovered、真机 passed 或地图列表视觉通过。真实 DevTools UI smoke 仍未执行 -- 下一步最佳动作:提交 AH 组并启动用户评测 agent,评估 AH 相比 AG 是否降低“控制台输出难交接或 local 草稿被误写成恢复成功”的风险;若继续推进,优先在用户可操作 DevTools 时启用 Service Port 后复跑 AF/AG/AH 命令,或把 recovery report guard 接入更高层的手测交接 preflight 但继续避免默认 CI/`npm run check` 依赖本机 GUI - -### Session 046AI - -- 日期:2026-06-15 -- 分支:`codex/iter-devtools-recovery-report-preflight` -- 本轮目标:第三十五组 AI DevTools recovery report preflight 实验,在 AH 已能生成并 guard 单份 ignored local recovery dry-run report 后,新增交接前手动 preflight 扫描当前 worktree 的所有 local reports 并逐份复跑 guard -- 已完成:产品 agent 新增 `harness/devtools-recovery-report-preflight-product-brief.md`,定义批量 preflight 的用户价值、非目标、使用场景和验收口径;设计 agent 新增 `harness/devtools-recovery-report-preflight-design-note.md`,定义 scan scope、per-report result、aggregate status 和非 UI evidence 警告;QA agent 新增 `harness/devtools-recovery-report-preflight-checklist.md`,覆盖默认 check 不接入、无 report、单份/多份 report、负向篡改和清理;开发 agent 新增 `scripts/check-devtools-recovery-report-preflight.mjs` 并在 `package.json` 暴露 `check:devtools-recovery-report-preflight`,保持默认 `npm run check` 不运行本地 recovery report preflight;主线程同步更新 `harness/feature_list.json` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check scripts/check-devtools-recovery-report-preflight.mjs`;无 local report 的 `npm run check:devtools-recovery-report-preflight`;生成两份有效 local report 后的 `npm run check:devtools-recovery-report-preflight`;追加 `DevTools recovered` 的负向 preflight;追加 `UI smoke passed` 的负向 preflight;清理 `harness/devtools-recovery-report.local*.md`;`npm run check`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;检查新文件未写入旧日期 -- 已记录证据:无 local report 时 preflight 输出 `No local DevTools recovery reports found; nothing checked.` 和非 UI passed 警告;两份有效 local report 时 preflight 逐份输出 `Checking harness/devtools-recovery-report.local-ai-*.md.`、单份 guard 通过,并输出 `DevTools recovery report preflight passed. Checked 2 report(s).`;追加 `DevTools recovered` 或 `UI smoke passed` 后 preflight 均非零退出,单份 guard 输出对应 `Report must not claim unverified success` 原因;`npm run check` 仍只串联 JSON、harness 和 readiness/default preflight,不调用 recovery report preflight;所有 ignored local recovery reports 已清理 -- 更新过的文件或工件:`package.json`,`scripts/check-devtools-recovery-report-preflight.mjs`,`harness/devtools-recovery-report-preflight-product-brief.md`,`harness/devtools-recovery-report-preflight-design-note.md`,`harness/devtools-recovery-report-preflight-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:AI 组不修改页面 UI,也没有恢复 DevTools 9420 服务端口;preflight 通过只证明当前 ignored local recovery dry-run 草稿逐份通过 guard,不代表 UI passed、DevTools recovered、真机 passed、地图列表视觉通过或 service port 已恢复。真实 DevTools UI smoke 仍未执行 -- 下一步最佳动作:按用户要求本轮运行完后生成结论并终止;若未来恢复工作,应优先由有 UI 权限的执行者恢复 WeChat DevTools service port,再复跑 AF/AG/AH/AI 手动命令并单独执行真实 UI smoke - -### Session 047Integration - -- 日期:2026-06-15 -- 分支:`codex/integrate-all-capabilities` -- 本轮目标:按用户要求把已探索完成的产品能力和验证能力集合到主分支候选中 -- 已完成:从 `main` 新建独立集成 worktree,合并 `codex/iter-devtools-recovery-report-preflight`、`codex/iter-map-ux`、`codex/iter-detail-trust`、`codex/iter-admin-risk` 和 `codex/iter-profile-activity`;保留发布准备度、详情 TrustInsight、地图 NearbyPreview、地图列表静态 guard、管理风险处理、个人中心状态面板、manual evidence/readiness/DevTools 诊断/recovery report 等能力;解决 DESIGN_SYSTEM 和 harness 记录冲突 -- 运行过的验证:合并前后运行 `bash harness/init.sh`;地图合并后运行 `node --check pages/map/map.js`、`node --check utils/post-presenter.js`、`node --check utils/geo.js`、`node harness/check-map-feed.mjs`;详情合并后运行 `node --check pages/detail/detail.js`、`node --check utils/format.js`、`node harness/check-trust-insight.mjs`;管理合并后运行 `node --check pages/admin/admin.js`、`node --check pages/admin/admin-review.js`、`node scripts/check-admin-review.mjs`;个人中心合并后运行 `node --check pages/me/me.js`、`node --check pages/me/me-state.js`、`node scripts/check-me-state.mjs`;后续还需跑完整候选验证 -- 已记录证据:各功能 helper 均已通过;`node scripts/check-json.mjs` 和 `node harness/check-harness.mjs` 在冲突解决后通过;项目既有 `MODULE_TYPELESS_PACKAGE_JSON` ESM warning 仍只影响 Node 检查输出,不影响退出码 -- 已知风险或未解决问题:本集成仍未完成真实 WeChat DevTools UI smoke 或真机验证;9420 service port blocker 仍需用户在 DevTools UI 中处理。集成进入 main 后仍不能把 map-feed 或其他用户可见旅程标记为 passing -- 下一步最佳动作:运行完整候选验证,通过后把 `codex/integrate-all-capabilities` 合入 `main`,同时保护 `/Users/bytedance/git/x` 当前未提交的本地文件 - -### Session 048Bugfix - -- 日期:2026-06-15 -- 分支:`main` -- 本轮目标:按用户真实体验反馈,修复地图“附近优先”看不见,以及管理员校验失败时展示 raw cloud.callFunction 堆栈的问题 -- 已完成:地图 `NearbyPreview` 从“未打开列表且未选中任务才显示”改为“未打开列表且有预览任务就显示”;选中任务卡在预览条可见时上移,避免与预览条重叠;管理员校验新增 `formatAdminRoleError`,把 `wx-server-sdk` 缺依赖、getMyRole 未部署/环境不匹配和未知云函数失败映射成短状态与处理步骤;“我的”页管理员入口展示 `处理:` 下一步;readiness 新增 admin auth error formatting guard -- 运行过的验证:`node --check utils/auth.js`;`node --check pages/me/me.js`;`node --check pages/map/map.js`;`node scripts/check-admin-auth-errors.mjs`;`node harness/check-map-feed.mjs`;`npm run check`;微信开发者工具内置 `wcc` 全量编译 WXML;微信开发者工具内置 `wcsc -lc` 全量编译 WXSS -- 已记录证据:`scripts/check-admin-auth-errors.mjs` 输出 `Admin auth error checks passed.`;`harness/check-map-feed.mjs` 输出 `Map feed checks passed.`;`npm run check` 输出 JSON、harness、publish flow、TrustInsight、candidate flow、Admin auth error、map list resilience 和 blocked summary preflight 全部通过;`wcc` 与 `wcsc -lc` 均退出 0 且无错误输出 -- 已知风险或未解决问题:尚未由用户在 WeChat DevTools 里重新编译后肉眼确认地图首屏;管理员真正通过仍取决于云端 `getMyRole` 函数重新上传并选择“云端安装依赖”,以及 `admins` 集合里有当前 openid 的 enabled admin 记录 -- 下一步最佳动作:请用户在 WeChat DevTools 重新编译项目,先回到地图 tab 确认“附近优先”出现;再到“我的”页点管理员校验,若仍提示依赖未安装,则按提示重新上传部署 `cloudfunctions/getMyRole` - -### Session 049Bugfix - -- 日期:2026-06-15 -- 分支:`main` -- 本轮目标:继续修复用户反馈的“底部任务列表入口/抽屉依旧没看到” -- 已完成:确认原实现把 `button/view/scroll-view` 绝对定位在全屏 `map` 原生组件上方,真实 DevTools 中可能被原生 map 层遮挡;将折叠态地图任务入口改为 `cover-view` 底部 dock,文案为“附近优先 / 列表”;地图工具按钮也改为 `cover-view`/`cover-image`;打开列表时为根节点增加 `list-open`,把 native map 高度缩到 `38vh`,并让普通任务卡抽屉从 `top: 38vh` 开始渲染,避免抽屉继续压在原生地图上 -- 运行过的验证:`node --check pages/map/map.js`;`node --check scripts/check-map-list-resilience.mjs`;`node --check harness/check-map-feed.mjs`;`node harness/check-map-feed.mjs`;`node scripts/check-map-list-resilience.mjs`;微信开发者工具内置 `wcc` 全量编译 WXML;微信开发者工具内置 `wcsc -lc` 全量编译 WXSS;`npm run check` -- 已记录证据:`harness/check-map-feed.mjs` 输出 `Map feed checks passed.`;`scripts/check-map-list-resilience.mjs` 输出 `Map list resilience checks passed.`;`npm run check` 输出 JSON、harness、publish flow、TrustInsight、candidate flow、Admin auth error、map list resilience 和 blocked summary preflight 全部通过;`wcc` 与 `wcsc -lc` 均退出 0 且无错误输出 -- 已知风险或未解决问题:仍需用户在 WeChat DevTools 中重新编译后确认底部 cover-view dock 可见,点击后抽屉在下半屏显示并能滚动/点击任务卡;自动检查不能替代真实原生 map 层视觉验收 -- 下一步最佳动作:用户重新编译并打开地图 tab,观察底部“附近优先 / 列表”dock,点击“列表”确认抽屉露出;若仍不显示,优先截图地图页全屏和控制台首条错误 - -### Session 050Bugfix - -- 日期:2026-06-15 -- 分支:`main` -- 本轮目标:修复用户截图反馈的“附近优先 dock 和回到当前位置按钮重叠,找一找按钮不见” -- 已完成:将 `map-tool-row` 从和 `list-dock` 相同的底部位置上移到 `268rpx + safe-area`,确保定位/找一找两个 cover-view 按钮位于 dock 上方;把 cover-view 工具栏从 grid/gap 改为更稳的 flex + margin;把找一找按钮从 gradient 改成 cover-view 更稳定的实色橙色背景;图标改用绝对居中,确保两个按钮都可见 -- 运行过的验证:`bash harness/init.sh`;`node --check scripts/check-map-list-resilience.mjs`;`node --check harness/check-map-feed.mjs`;`node harness/check-map-feed.mjs`;`node scripts/check-map-list-resilience.mjs`;微信开发者工具内置 `wcc` 全量编译 WXML;微信开发者工具内置 `wcsc -lc` 全量编译 WXSS -- 已记录证据:`harness/check-map-feed.mjs` 输出 `Map feed checks passed.`;`scripts/check-map-list-resilience.mjs` 输出 `Map list resilience checks passed.`;`wcc` 与 `wcsc -lc` 均退出 0 且无错误输出 -- 已知风险或未解决问题:仍需用户在 WeChat DevTools 重新编译后确认定位和找一找两个按钮在 dock 上方并排可见,点击找一找仍能切换到附近任务 -- 下一步最佳动作:用户重新编译地图页,检查 dock 上方右侧是否有两个按钮:左边回到当前位置,右边橙色找一找 - -### Session 051Bugfix - -- 日期:2026-06-15 -- 分支:`main` -- 本轮目标:修复用户反馈的“附近优先含义不清、任务卡片和地图 icon 仍重叠” -- 已完成:底部 dock 标题从“附近优先”改为“附近任务”,副文案改成“全部/分类 · N 条任务”,去掉“点开看任务卡”的教学式文案;将定位/找一找工具栏从底部区域移动到地图右上角,彻底避开选中任务卡、详情按钮和底部 dock -- 运行过的验证:`bash harness/init.sh`;`node --check pages/map/map.js`;`node --check scripts/check-map-list-resilience.mjs`;`node --check harness/check-map-feed.mjs`;`node harness/check-map-feed.mjs`;`node scripts/check-map-list-resilience.mjs`;微信开发者工具内置 `wcc` 全量编译 WXML;微信开发者工具内置 `wcsc -lc` 全量编译 WXSS -- 已记录证据:`harness/check-map-feed.mjs` 输出 `Map feed checks passed.`;`scripts/check-map-list-resilience.mjs` 输出 `Map list resilience checks passed.`;`wcc` 与 `wcsc -lc` 均退出 0 且无错误输出 -- 已知风险或未解决问题:仍需用户在 WeChat DevTools 重新编译后确认地图右上角两个工具按钮不再和选中任务卡重叠,底部 dock 文案更易理解 -- 下一步最佳动作:用户重新编译地图页,点击 marker 后确认任务卡内详情按钮无遮挡;观察底部 dock 是否显示“附近任务” - -### Session 052Bugfix - -- 日期:2026-06-15 -- 分支:`main` -- 本轮目标:按用户明确偏好,把地图 UI 收敛为“列表入口右上角,回到当前位置和找一找按钮右下角” -- 已完成:取消底部大 dock,改为右上角 `cover-view` 紧凑“列表 + 数量”入口;定位和找一找两个 `cover-view` 工具按钮放回右下角并保持并排;选中任务卡底部上移到工具行上方,避免遮挡按钮;移除选中卡片上旧的 `with-nearby-preview` 条件类;同步更新 `DESIGN_SYSTEM.md`、`harness/check-map-feed.mjs` 和 `scripts/check-map-list-resilience.mjs`,防止后续回退到底部 dock 或顶部工具行 -- 运行过的验证:`bash harness/init.sh`;`node --check pages/map/map.js`;`node --check scripts/check-map-list-resilience.mjs`;`node --check harness/check-map-feed.mjs`;`node harness/check-map-feed.mjs`;`node scripts/check-map-list-resilience.mjs`;微信开发者工具内置 `wcc` 全量编译 WXML;微信开发者工具内置 `wcsc -lc` 全量编译 WXSS;`node scripts/check-json.mjs && node harness/check-harness.mjs && git diff --check`;`npm run check` -- 已记录证据:`harness/check-map-feed.mjs` 输出 `Map feed checks passed.`;`scripts/check-map-list-resilience.mjs` 输出 `Map list resilience checks passed.`;`wcc` 与 `wcsc -lc` 均退出 0 且无错误输出;`npm run check` 输出 JSON、harness、publish flow、TrustInsight、candidate flow、Admin auth error、map list resilience 和 blocked summary preflight 全部通过 -- 已知风险或未解决问题:自动检查不能替代真实原生地图层视觉验收;仍需用户在 WeChat DevTools 中重新编译后确认右上角“列表 N”入口可见、右下角两个按钮不与选中任务卡重叠、点击“列表”后抽屉仍正常打开 -- 下一步最佳动作:用户在 WeChat DevTools 重新编译地图页,先看右上角“列表 N”,再点击 marker 看任务卡和右下角定位/找一找按钮是否分离 - -### Session 053Bugfix - -- 日期:2026-06-15 -- 分支:`main` -- 本轮目标:修复用户截图反馈的“右上角列表按钮样式有问题” -- 已完成:确认截图中白底“列表 6”更像地图标签而不是操作按钮;将右上角列表入口改为绿色实色 `cover-view` 按钮,只显示“列表”,任务数量改为右上角橙色小角标;同步更新 `DESIGN_SYSTEM.md`、`harness/check-map-feed.mjs` 和 `scripts/check-map-list-resilience.mjs`,要求后续保持绿色按钮和数量角标 -- 运行过的验证:`bash harness/init.sh`;`node --check pages/map/map.js`;`node --check scripts/check-map-list-resilience.mjs`;`node --check harness/check-map-feed.mjs`;`node harness/check-map-feed.mjs`;`node scripts/check-map-list-resilience.mjs`;微信开发者工具内置 `wcc` 全量编译 WXML;微信开发者工具内置 `wcsc -lc` 全量编译 WXSS;`node scripts/check-json.mjs && node harness/check-harness.mjs && git diff --check`;`npm run check` -- 已记录证据:`harness/check-map-feed.mjs` 输出 `Map feed checks passed.`;`scripts/check-map-list-resilience.mjs` 输出 `Map list resilience checks passed.`;`wcc` 与 `wcsc -lc` 均退出 0 且无错误输出;`npm run check` 输出 JSON、harness、publish flow、TrustInsight、candidate flow、Admin auth error、map list resilience 和 blocked summary preflight 全部通过 -- 已知风险或未解决问题:自动检查仍不能替代真实原生地图层视觉验收;需要用户在 WeChat DevTools 中重新编译后确认绿色列表按钮和角标不再像地图标签,并且点击仍打开列表抽屉 -- 下一步最佳动作:用户在 WeChat DevTools 重新编译地图页,观察右上角是否变成绿色“列表”按钮并带小角标 - -### Session 054Bugfix - -- 日期:2026-06-15 -- 分支:`main` -- 本轮目标:按用户澄清修正右上角列表按钮的对齐问题,而不是颜色问题 -- 已完成:撤销上一轮绿色按钮/角标方向,恢复白色右上角 `cover-view` 按钮;把原来分开的“列表”和数量节点改为单个 `list-fab-line`,内容为“列表 {{visiblePosts.length}}”;通过固定 `height: 70rpx`、同等 `line-height: 70rpx` 和 `text-align: center` 保证文本与数字在同一条视觉中线上;同步更新 `DESIGN_SYSTEM.md`、`harness/check-map-feed.mjs` 和 `scripts/check-map-list-resilience.mjs`,防止再次拆成两个基线不一致的节点 -- 运行过的验证:`bash harness/init.sh`;`node --check pages/map/map.js`;`node --check scripts/check-map-list-resilience.mjs`;`node --check harness/check-map-feed.mjs`;`node harness/check-map-feed.mjs`;`node scripts/check-map-list-resilience.mjs`;微信开发者工具内置 `wcc` 全量编译 WXML;微信开发者工具内置 `wcsc -lc` 全量编译 WXSS;`node scripts/check-json.mjs && node harness/check-harness.mjs && git diff --check`;`npm run check` -- 已记录证据:`harness/check-map-feed.mjs` 输出 `Map feed checks passed.`;`scripts/check-map-list-resilience.mjs` 输出 `Map list resilience checks passed.`;`wcc` 与 `wcsc -lc` 均退出 0 且无错误输出;`npm run check` 输出 JSON、harness、publish flow、TrustInsight、candidate flow、Admin auth error、map list resilience 和 blocked summary preflight 全部通过 -- 已知风险或未解决问题:自动检查不能替代真实原生地图层视觉验收;需要用户在 WeChat DevTools 中重新编译后确认“列表 6”同一行居中对齐,并且点击仍打开列表抽屉 -- 下一步最佳动作:用户在 WeChat DevTools 重新编译地图页,观察右上角白色“列表 N”按钮内文字和数字是否齐平 - -### Session 055C - -- 日期:2026-06-16 -- 分支:`codex/iter-viral-publish` -- 本轮目标:C 组产品/设计/开发围绕“发布成功后的扩散闭环”做最小可验证迭代,让发布者在刚发布任务后知道转给谁、想获得什么信号、稍后如何回访 -- 产品假设:发布者刚创建任务时动机最强,如果在 `from=publish` 详情上下文给出清晰扩散对象、确认/线索目标和回访动作,会更愿意转发到附近群或让朋友确认 -- 已完成:新增 `harness/viral-publish-product-brief.md` 和 `harness/viral-publish-design-checklist.md`;新增 `utils/publish-spread.js` 生成分类/意图/图片/评论/状态相关的谨慎扩散计划;详情页仅在 `from=publish` 时把原发布成功卡升级为三步扩散计划;`resolved`、`expired`、`hidden` 不鼓励扩散;分享 path 保留非发布来源参数但移除 `from=publish`;新增 `scripts/check-publish-spread.mjs` 并接入 `scripts/check-devtools-readiness.mjs` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check utils/publish-spread.js`;`node --check pages/detail/detail.js`;`node --check scripts/check-publish-spread.mjs`;`node --check scripts/check-devtools-readiness.mjs`;`node --no-warnings scripts/check-publish-spread.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;微信开发者工具内置 `wcc` 全量编译 WXML;微信开发者工具内置 `wcsc -lc` 全量编译 WXSS;`npm run check` -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/viral-publish`,对应约定 `/tmp/street-tasks-iter-worktrees/viral-publish`;当前分支为 `codex/iter-viral-publish`;新增检查先因缺少 `utils/publish-spread.js` 按预期失败,补实现后输出 `Publish spread checks passed.`;四条 `node --check` 均通过;readiness 输出 `Publish flow checks passed.`、`Publish spread checks passed.`、`Trust insight checks passed.`、`Candidate flow checks passed.`、`Admin auth error checks passed.`、`Map list resilience checks passed.` 和 `DevTools readiness checks passed.`;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`git diff --check` 通过无输出;`bash harness/init.sh` 完整跑通;`wcc` 和 `wcsc -lc` 全量编译退出码为 0 且无输出;`npm run check` 通过 -- 更新过的文件或工件:`utils/publish-spread.js`,`pages/detail/detail.js`,`pages/detail/detail.wxml`,`pages/detail/detail.wxss`,`scripts/check-publish-spread.mjs`,`scripts/check-devtools-readiness.mjs`,`harness/viral-publish-product-brief.md`,`harness/viral-publish-design-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:尚未在 WeChat DevTools 或真机中完成真实发布、发布后详情跳转、open-type share 系统面板、分享接收路径、窄屏扩散计划布局、图片任务渲染和 resolved/expired 非扩散视觉验证;自动检查不能证明转发率提升或真实分享面板可用 -- 下一步最佳动作:在 WeChat DevTools 中用一条带图和一条无图任务走完整发布成功链路,确认扩散计划出现、普通详情入口不出现、转发路径不带 `from=publish`,再用真机观察分享卡片和窄屏布局 - -### Session 055Share - -- 日期:2026-06-16 -- 分支:`codex/iter-viral-share` -- 本轮目标:围绕详情页任务转发做一版能提升用户自发裂变的最小可验证迭代 -- 已完成:新增 `utils/share-message.js`,把详情页分享标题、路径和说明文案统一收敛到单一 helper;新增 `scripts/check-share-message.mjs` 覆盖 active、stale、report、resolved、expired、hidden 和无任务边界;详情页改为展示轻量分享提示层,明确告诉用户转给谁、为什么转、转出去能帮什么,并让 `onShareAppMessage` 直接复用同一 helper 输出动态 title/path -- 运行过的验证:`node --check utils/share-message.js`;`node --check pages/detail/detail.js`;`node --check scripts/check-share-message.mjs`;`node scripts/check-share-message.mjs`;`node --no-warnings scripts/check-share-message.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`bash harness/init.sh` -- 已记录证据:`node --check` 三项均通过;`node scripts/check-share-message.mjs` 首次命中 expired 断言后已按更谨慎的文案调整通过;`node --no-warnings scripts/check-share-message.mjs` 输出 `Share message checks passed.`;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`git diff --check` 通过无输出;`bash harness/init.sh` 完整跑通并再次通过 JSON 和 harness 自检 -- 更新过的文件或工件:`utils/share-message.js`,`scripts/check-share-message.mjs`,`pages/detail/detail.js`,`pages/detail/detail.wxml`,`pages/detail/detail.wxss`,`harness/viral-share-product-brief.md`,`harness/viral-share-design-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:分享模块的实际视觉表现、按钮触发和系统分享菜单仍需在 WeChat DevTools 中手动确认;当前只验证了静态结构和 Node 逻辑 -- 下一步最佳动作:在 WeChat DevTools 中打开一条 active、resolved、expired 和高举报详情,逐个点开分享菜单确认 title/path 与页面提示一致 - -### Session 056ViralCandidate - -- 日期:2026-06-16 -- 分支:`codex/iter-viral-candidate` -- 本轮目标:组合 C 组发布后扩散计划和 A 组普通详情分享提示,形成更接近合入候选的大版本 -- 已完成:从 `codex/iter-viral-publish` 创建候选分支并合入 `codex/iter-viral-share`;详情页现在在 `from=publish` 场景展示发布者专属三步扩散计划,在普通详情入口展示“转给谁 / 为什么转 / 能帮什么”的通用分享提示;`onShareAppMessage` 使用 A 组谨慎标题,发布成功场景继续用 C 组接收侧普通详情 path,避免把发布者专属卡转给接收者 -- 运行过的验证:`node --check pages/detail/detail.js`;`node --check utils/share-message.js`;`node --check utils/publish-spread.js`;`node --check scripts/check-share-message.mjs`;`node --check scripts/check-publish-spread.mjs`;`node --check scripts/check-viral-candidate.mjs`;`node --no-warnings scripts/check-share-message.mjs`;`node --no-warnings scripts/check-publish-spread.mjs`;`node --no-warnings scripts/check-viral-candidate.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`bash harness/init.sh`;`npm run check` -- 已记录证据:`node --no-warnings scripts/check-share-message.mjs` 输出 `Share message checks passed.`;`node --no-warnings scripts/check-publish-spread.mjs` 输出 `Publish spread checks passed.`;`node --no-warnings scripts/check-viral-candidate.mjs` 输出 `Viral candidate checks passed.`;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`bash harness/init.sh` 完整跑通;`npm run check` 输出 JSON、harness、publish flow、publish spread、TrustInsight、candidate flow、Admin auth error、map list resilience 和 blocked summary preflight 全部通过 -- 更新过的文件或工件:`utils/share-message.js`,`utils/publish-spread.js`,`pages/detail/detail.js`,`pages/detail/detail.wxml`,`pages/detail/detail.wxss`,`scripts/check-share-message.mjs`,`scripts/check-publish-spread.mjs`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:尚未在 WeChat DevTools/真机中验证组合后的发布成功页、普通详情页、分享面板、接收路径、窄屏布局和带图任务渲染 -- 下一步最佳动作:提交组合候选,再把该大版本发送给用户评测 agent 追加评分 - -### Session 057Receiver - -- 日期:2026-06-16 -- 分支:`codex/iter-viral-receiver` -- 本轮目标:围绕“分享接收侧转化”做一版最小可验证迭代,让 `from=share` 打开的详情页明确告诉用户为什么收到这条任务、先做什么、以及不在现场怎么帮 -- 产品假设:如果接收侧页面把“为什么转给你”“先做哪一步”“不在现场怎么帮”说清楚,用户更容易继续确认、评论或二次转发,而不是只看一眼就离开 -- 已完成:新增 `harness/viral-receiver-product-brief.md` 和 `harness/viral-receiver-design-checklist.md`;新增 `utils/share-receiver.js` 生成 `from=share` 的接收侧提示;详情页在 `entryQuery.from === 'share'` 且有任务时展示轻量提示条,并继续保留现有信任判断、评论和普通分享提示;新增 `scripts/check-share-receiver.mjs`,并把它接入 `scripts/check-devtools-readiness.mjs` 与 `scripts/check-viral-candidate.mjs` -- 运行过的验证:`node --check pages/detail/detail.js`;`node --check utils/share-receiver.js`;`node --check scripts/check-share-receiver.mjs`;`node scripts/check-share-receiver.mjs`;`node scripts/check-share-message.mjs`;`node scripts/check-publish-spread.mjs`;`node scripts/check-viral-candidate.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`bash harness/init.sh`;`npm run check` -- 已记录证据:四条 `node --check` 均通过;`node scripts/check-share-receiver.mjs` 输出 `Share receiver checks passed.`;`node scripts/check-share-message.mjs` 输出 `Share message checks passed.`;`node scripts/check-publish-spread.mjs` 输出 `Publish spread checks passed.`;`node scripts/check-viral-candidate.mjs` 输出 `Viral candidate checks passed.`;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`git diff --check` 通过无输出;`bash harness/init.sh` 完整跑通;`npm run check` 输出 JSON、harness、publish flow、publish spread、share receiver、TrustInsight、candidate flow、Admin auth error、map list resilience 和 blocked summary preflight 全部通过 -- 更新过的文件或工件:`utils/share-receiver.js`,`pages/detail/detail.js`,`pages/detail/detail.wxml`,`pages/detail/detail.wxss`,`scripts/check-share-receiver.mjs`,`scripts/check-devtools-readiness.mjs`,`scripts/check-viral-candidate.mjs`,`harness/viral-receiver-product-brief.md`,`harness/viral-receiver-design-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:真实 WeChat DevTools/真机仍需手动确认 `from=share` 提示模块的文案、换行、按钮层级以及二次转发行为;当前自动验证只能证明静态结构和 Node 逻辑正确,不能证明实际分享转化提升 -- 下一步最佳动作:在 WeChat DevTools 中打开一条从分享进入的 active、stale、resolved 和 expired 任务,确认接收侧提示、普通分享提示和信任/评论区域没有互相挤压 - -### Session 058F - -- 日期:2026-06-16 -- 分支:`codex/iter-viral-comment-relay` -- 本轮目标:F 组产品/设计/开发围绕“评论成功后的二次接力”做最小可验证迭代,让用户刚补充线索后知道可以把最新线索转给更可能路过的人 -- 产品假设:用户刚在任务详情补充评论/线索时参与意愿最高;如果评论成功后给出轻量接力提示,并在高风险或关闭状态下不鼓励公开扩散,可能把一次评论转化为一次更谨慎的二次传播 -- 已完成:新增 `harness/viral-comment-relay-product-brief.md` 和 `harness/viral-comment-relay-design-checklist.md`;新增 `utils/comment-relay.js` 生成评论成功后的接力提示;详情页新增 `commentRelayPrompt`,页面加载/重新进入默认隐藏,只在评论提交成功后显示轻量 panel;active 低风险任务显示 `open-type="share"` 接力按钮,`stale`、高举报、`resolved`、`expired`、`hidden` 只显示谨慎提醒;新增 `scripts/check-comment-relay.mjs` 并接入 `scripts/check-devtools-readiness.mjs` 与 `scripts/check-viral-candidate.mjs` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --no-warnings scripts/check-comment-relay.mjs` 红灯确认缺少 `utils/comment-relay.js`;`node --check utils/comment-relay.js`;`node --check pages/detail/detail.js`;`node --check scripts/check-comment-relay.mjs`;`node --check scripts/check-devtools-readiness.mjs`;`node --check scripts/check-viral-candidate.mjs`;`node --no-warnings scripts/check-comment-relay.mjs`;`node --no-warnings scripts/check-share-message.mjs`;`node --no-warnings scripts/check-publish-spread.mjs`;`node --no-warnings scripts/check-share-receiver.mjs`;`node --no-warnings scripts/check-viral-candidate.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`npm run check`;`bash harness/init.sh` -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/viral-comment-relay`,对应约定 `/tmp/street-tasks-iter-worktrees/viral-comment-relay`;当前分支为 `codex/iter-viral-comment-relay`;新增检查先因缺少 `utils/comment-relay.js` 按预期失败,补实现和详情接入后输出 `Comment relay checks passed.`;五条 `node --check` 均通过;既有 `Share message checks passed.`、`Publish spread checks passed.`、`Share receiver checks passed.`、`Viral candidate checks passed.` 均通过;readiness 输出包含 `Comment relay checks passed.`,随后 publish flow、publish spread、share receiver、Trust insight、candidate flow、Admin auth error、map list resilience 和 blocked summary preflight 全部通过;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`git diff --check` 通过无输出;`npm run check` 通过且默认 readiness 包含评论接力检查;最后一次 `bash harness/init.sh` 完整跑通 -- 更新过的文件或工件:`utils/comment-relay.js`,`pages/detail/detail.js`,`pages/detail/detail.wxml`,`pages/detail/detail.wxss`,`scripts/check-comment-relay.mjs`,`scripts/check-devtools-readiness.mjs`,`scripts/check-viral-candidate.mjs`,`harness/viral-comment-relay-product-brief.md`,`harness/viral-comment-relay-design-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:尚未在 WeChat DevTools 或真机中验证真实评论成功后的 panel 插入位置、分享按钮触发、系统分享卡片、风险态无公开转发 CTA、窄屏换行、键盘/安全区和云端评论路径;自动检查不能证明真实分享转化提升 -- 下一步最佳动作:在 WeChat DevTools 中登录后对 active、stale、高举报、resolved/expired 任务分别提交或模拟评论成功状态,确认评论接力 panel 只在成功后出现,active 能打开分享面板,风险态只提示不盲转 - -### Session 059LoopCandidate - -- 日期:2026-06-16 -- 分支:`codex/iter-viral-loop-candidate` -- 本轮目标:把发布后扩散、分享接收侧引导、评论后接力三段高分能力收敛为一个更克制的传播闭环候选,避免同一屏出现多个主要传播 CTA -- 产品假设:发布者、分享接收者、刚评论的用户处在不同意图时刻;如果普通分享面板、接收侧引导和评论接力提示同时堆叠,会稀释下一步行动,因此同一时刻只保留一个主要传播行动更适合作为合入候选 -- 已完成:新增 `harness/viral-loop-candidate-product-brief.md` 和 `harness/viral-loop-candidate-design-checklist.md`;详情页普通分享面板改为仅在非发布、非分享接收、非评论接力状态下显示;`scripts/check-viral-candidate.mjs` 和 `scripts/check-comment-relay.mjs` 增加互斥展示 guard -- 运行过的验证:`node --check pages/detail/detail.js`;`node --check scripts/check-viral-candidate.mjs`;`node --check scripts/check-comment-relay.mjs`;`node --no-warnings scripts/check-viral-candidate.mjs`;`node --no-warnings scripts/check-comment-relay.mjs`;`node --no-warnings scripts/check-share-receiver.mjs`;`node --no-warnings scripts/check-share-message.mjs`;`node --no-warnings scripts/check-publish-spread.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`npm run check`;`bash harness/init.sh` -- 已记录证据:`node --no-warnings scripts/check-viral-candidate.mjs` 输出 `Viral candidate checks passed.`;`node --no-warnings scripts/check-comment-relay.mjs` 输出 `Comment relay checks passed.`;`node --no-warnings scripts/check-share-receiver.mjs` 输出 `Share receiver checks passed.`;`node --no-warnings scripts/check-share-message.mjs` 输出 `Share message checks passed.`;`node --no-warnings scripts/check-publish-spread.mjs` 输出 `Publish spread checks passed.`;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`git diff --check` 通过无输出;`npm run check` 通过且 readiness 包含 publish spread、comment relay、share receiver、candidate flow、map list 等检查;`bash harness/init.sh` 完整跑通 -- 更新过的文件或工件:`pages/detail/detail.wxml`,`scripts/check-viral-candidate.mjs`,`scripts/check-comment-relay.mjs`,`harness/viral-loop-candidate-product-brief.md`,`harness/viral-loop-candidate-design-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:尚未在 WeChat DevTools 或真机中验证发布成功页、分享接收页、普通详情页和评论成功页四种入口的实际视觉层级、分享按钮触发、窄屏换行和真实用户转化 -- 下一步最佳动作:运行完整验证后提交候选,并发送给用户评测 agent 与 F=98 的结果比较 - -### Session 060CommentSource - -- 日期:2026-06-16 -- 分支:`codex/iter-viral-comment-source` -- 本轮目标:在评论接力分享链路上补一个最小来源标识,让接收侧能明确区分“有人刚补了线索”的二跳入口,同时不回退普通分享面板互斥规则 -- 产品假设:如果评论接力路径携带 `source=comment`,接收侧就能把“先看最新评论/评论区已有新线索”说得更明确,二跳用户更容易继续确认或补充,而不是只看到泛化的 `from=share` 提示 -- 已完成:新增 `harness/viral-comment-source-product-brief.md` 和 `harness/viral-comment-source-design-checklist.md`;`utils/comment-relay.js` 的分享路径新增 `source=comment`;`utils/share-receiver.js` 在 `entryFrom === 'share' && source === 'comment'` 时强化“有人刚补了线索/先看最新评论”的接收文案,同时对 `stale`、高举报、已关闭、已过期、已隐藏继续保持谨慎;`pages/detail/detail.js` 继续传递 `entryQuery.source` 给接收侧 helper;`scripts/check-comment-relay.mjs`、`scripts/check-share-receiver.mjs` 和 `scripts/check-viral-candidate.mjs` 更新了来源与互斥检查 -- 运行过的验证:`bash harness/init.sh`;`node --check utils/comment-relay.js`;`node --check utils/share-receiver.js`;`node --check pages/detail/detail.js`;`node --check scripts/check-comment-relay.mjs`;`node --check scripts/check-share-receiver.mjs`;`node --check scripts/check-viral-candidate.mjs`;`node --no-warnings scripts/check-comment-relay.mjs`;`node --no-warnings scripts/check-share-receiver.mjs`;`node --no-warnings scripts/check-viral-candidate.mjs`;`node --no-warnings scripts/check-share-message.mjs`;`node --no-warnings scripts/check-publish-spread.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`npm run check` -- 已记录证据:`node --no-warnings scripts/check-comment-relay.mjs` 输出 `Comment relay checks passed.`;`node --no-warnings scripts/check-share-receiver.mjs` 输出 `Share receiver checks passed.`;`node --no-warnings scripts/check-viral-candidate.mjs` 输出 `Viral candidate checks passed.`;`node --no-warnings scripts/check-share-message.mjs` 输出 `Share message checks passed.`;`node --no-warnings scripts/check-publish-spread.mjs` 输出 `Publish spread checks passed.`;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`git diff --check` 通过无输出;`npm run check` 通过且 readiness 里继续包含 publish flow、publish spread、comment relay、share receiver、candidate flow、Admin auth error、map list resilience 和 blocked summary preflight;`bash harness/init.sh` 完整跑通 -- 更新过的文件或工件:`utils/comment-relay.js`,`utils/share-receiver.js`,`pages/detail/detail.js`,`scripts/check-comment-relay.mjs`,`scripts/check-share-receiver.mjs`,`scripts/check-viral-candidate.mjs`,`harness/viral-comment-source-product-brief.md`,`harness/viral-comment-source-design-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:尚未在 WeChat DevTools 或真机中验证 `source=comment` 的实际分享接收文案、窄屏换行、系统分享面板和真实二跳行为;自动检查只能证明路径和字符串没有回退 -- 下一步最佳动作:在 WeChat DevTools 中打开一条从评论接力进入的详情页,确认接收侧真的展示“有人刚补了线索/先看最新评论”,再观察高举报和过时状态是否仍保持谨慎 - -### Session 061ConfirmRelay - -- 日期:2026-06-16 -- 分支:`codex/iter-viral-confirm-relay` -- 本轮目标:围绕“确认成功后的可信接力”做最小可验证迭代,让用户刚确认一条低风险任务后可以把确认信号转给更可能路过的人,同时不鼓励过时/举报动作公开扩散 -- 产品假设:确认动作代表用户刚刚投入了判断成本;如果确认成功后提示“可以转给更可能路过的人继续补线索”,会比普通分享更可信,但文案必须只说确认信号,不能暗示完全属实 -- 已完成:新增 `harness/viral-confirm-relay-product-brief.md` 和 `harness/viral-confirm-relay-design-checklist.md`;新增 `utils/action-relay.js` 生成 confirm/stale/report 后提示;详情页新增 `actionRelayPrompt`,页面加载/重新进入默认隐藏,trust action 成功后显示;低风险 confirm 可用 `open-type="share"` 且 path 带 `source=confirm`,stale/report/高举报/过时/关闭态只显示谨慎提示;`utils/share-receiver.js` 支持 `source=confirm` 接收侧文案;`scripts/check-action-relay.mjs` 加入默认 readiness 和候选检查 -- 运行过的验证:`node --check utils/action-relay.js`;`node --check utils/share-receiver.js`;`node --check pages/detail/detail.js`;`node --check scripts/check-action-relay.mjs`;`node --check scripts/check-comment-relay.mjs`;`node --check scripts/check-share-receiver.mjs`;`node --check scripts/check-viral-candidate.mjs`;`node --check scripts/check-devtools-readiness.mjs`;`node --no-warnings scripts/check-action-relay.mjs`;`node --no-warnings scripts/check-comment-relay.mjs`;`node --no-warnings scripts/check-share-receiver.mjs`;`node --no-warnings scripts/check-viral-candidate.mjs`;`node --no-warnings scripts/check-share-message.mjs`;`node --no-warnings scripts/check-publish-spread.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`npm run check`;`bash harness/init.sh` -- 已记录证据:`node --no-warnings scripts/check-action-relay.mjs` 输出 `Action relay checks passed.`;`node --no-warnings scripts/check-comment-relay.mjs` 输出 `Comment relay checks passed.`;`node --no-warnings scripts/check-share-receiver.mjs` 输出 `Share receiver checks passed.`;`node --no-warnings scripts/check-viral-candidate.mjs` 输出 `Viral candidate checks passed.`;`node --no-warnings scripts/check-share-message.mjs` 输出 `Share message checks passed.`;`node --no-warnings scripts/check-publish-spread.mjs` 输出 `Publish spread checks passed.`;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`git diff --check` 通过无输出;`npm run check` 通过且 readiness 包含 `Action relay checks passed.`;`bash harness/init.sh` 完整跑通 -- 更新过的文件或工件:`utils/action-relay.js`,`utils/share-receiver.js`,`pages/detail/detail.js`,`pages/detail/detail.wxml`,`scripts/check-action-relay.mjs`,`scripts/check-comment-relay.mjs`,`scripts/check-share-receiver.mjs`,`scripts/check-viral-candidate.mjs`,`scripts/check-devtools-readiness.mjs`,`harness/viral-confirm-relay-product-brief.md`,`harness/viral-confirm-relay-design-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:尚未在 WeChat DevTools 或真机中验证 confirm/stale/report 后真实提示、`source=confirm` 接收页、open-type share、风险态无公开分享 CTA、窄屏换行和真实二跳行为 -- 下一步最佳动作:运行完整验证后提交,并发送给用户评测 agent 与 F/I=98 的最高分结果比较 - -### Session 062ReceiverConversion - -- 日期:2026-06-16 -- 分支:`codex/iter-viral-receiver-conversion` -- 本轮目标:围绕“分享接收者完成行动后的再传播”做最小可验证迭代,让 `from=share` 进入并完成确认或评论的用户可以继续接力给下一位更可能路过的人 -- 产品假设:如果接收者已经愿意确认或评论,说明这条内容完成了一次真实转化;此时提示“继续接力给下一位”比默认详情分享更贴近裂变链路,但普通详情入口和风险态不应触发 -- 已完成:新增 `harness/viral-receiver-conversion-product-brief.md` 和 `harness/viral-receiver-conversion-design-checklist.md`;新增 `utils/receiver-conversion.js` 生成 `from=share` 接收者确认/评论后的提示;详情页新增 `receiverConversionPrompt`,页面加载/重新进入默认隐藏,分享入口完成评论或信任动作后才设置,并优先于 comment/action relay;`source=receiver` 接收侧文案强调先看确认和评论;`scripts/check-receiver-conversion.mjs` 加入默认 readiness 和候选检查 -- 运行过的验证:`node --check utils/receiver-conversion.js`;`node --check utils/share-receiver.js`;`node --check pages/detail/detail.js`;`node --check scripts/check-receiver-conversion.mjs`;`node --check scripts/check-action-relay.mjs`;`node --check scripts/check-comment-relay.mjs`;`node --check scripts/check-share-receiver.mjs`;`node --check scripts/check-viral-candidate.mjs`;`node --check scripts/check-devtools-readiness.mjs`;`node --no-warnings scripts/check-receiver-conversion.mjs`;`node --no-warnings scripts/check-action-relay.mjs`;`node --no-warnings scripts/check-comment-relay.mjs`;`node --no-warnings scripts/check-share-receiver.mjs`;`node --no-warnings scripts/check-viral-candidate.mjs`;`node --no-warnings scripts/check-share-message.mjs`;`node --no-warnings scripts/check-publish-spread.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`npm run check`;`bash harness/init.sh` -- 已记录证据:`node --check` 覆盖 `utils/receiver-conversion.js`、`utils/share-receiver.js`、`pages/detail/detail.js` 和 5 个相关检查脚本,均通过;`node --no-warnings scripts/check-receiver-conversion.mjs` 输出 `Receiver conversion checks passed.`;`node --no-warnings scripts/check-action-relay.mjs` 输出 `Action relay checks passed.`;`node --no-warnings scripts/check-comment-relay.mjs` 输出 `Comment relay checks passed.`;`node --no-warnings scripts/check-share-receiver.mjs` 输出 `Share receiver checks passed.`;`node --no-warnings scripts/check-viral-candidate.mjs` 输出 `Viral candidate checks passed.`;`node --no-warnings scripts/check-devtools-readiness.mjs` 输出 `Receiver conversion checks passed.` 和 `DevTools readiness checks passed.`;`node --no-warnings scripts/check-share-message.mjs` 输出 `Share message checks passed.`;`node --no-warnings scripts/check-publish-spread.mjs` 输出 `Publish spread checks passed.`;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`git diff --check` 通过无输出;`npm run check` 通过且 readiness 包含 `Receiver conversion checks passed.`;`bash harness/init.sh` 完整跑通;当前自动检查只证明 helper、路径和模板互斥结构,不代表 DevTools/真机视觉或真实分享转化通过 -- 更新过的文件或工件:`utils/receiver-conversion.js`,`utils/share-receiver.js`,`pages/detail/detail.js`,`pages/detail/detail.wxml`,`pages/detail/detail.wxss`,`scripts/check-receiver-conversion.mjs`,`scripts/check-action-relay.mjs`,`scripts/check-comment-relay.mjs`,`scripts/check-share-receiver.mjs`,`scripts/check-viral-candidate.mjs`,`scripts/check-devtools-readiness.mjs`,`harness/viral-receiver-conversion-product-brief.md`,`harness/viral-receiver-conversion-design-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:尚未在 WeChat DevTools 或真机中验证 from=share 接收者完成确认/评论后的真实提示、`source=receiver` 接收页、open-type share、风险态无公开分享 CTA、窄屏换行和真实二跳行为 -- 下一步最佳动作:运行完整验证后提交,并发送给用户评测 agent 与 J=99 的最高分结果比较 - -### Session 063ReceiverAction - -- 日期:2026-06-16 -- 分支:`codex/iter-viral-receiver-action` -- 本轮目标:围绕“收到分享后的第一步行动入口”做最小可验证迭代,降低 `from=share` 接收者完成确认/评论的摩擦,并接上既有 receiverConversionPrompt 二跳 -- 产品假设:分享接收者第一步通常不是继续扩散,而是确认自己是否在附近、是否能补线索;在接收侧提示里放一个轻量 action strip,能让低风险 active 任务更快获得 confirm/comment,同时风险态继续保持谨慎 -- 已完成:新增 `harness/viral-receiver-action-product-brief.md` 和 `harness/viral-receiver-action-design-checklist.md`;新增 `utils/share-receiver-actions.js` 生成低风险接收者 action strip;详情页在 `shareReceiverGuide` 内展示“我在附近,确认一下”和“补一条线索”,分别复用现有 `react` confirm 与 `openCommentDialog`;`hidden` / `resolved` / `expired` / `stale` / 任意过时或举报信号不显示鼓励性 action strip;新增 `scripts/check-share-receiver-action.mjs` 并接入 `scripts/check-devtools-readiness.mjs` 和 `scripts/check-viral-candidate.mjs` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --no-warnings scripts/check-share-receiver-action.mjs` 红灯确认缺少 `utils/share-receiver-actions.js`,以及集成断言红灯确认 readiness 未接入;`node --check utils/share-receiver-actions.js`;`node --check pages/detail/detail.js`;`node --check scripts/check-share-receiver-action.mjs`;`node --check scripts/check-share-receiver.mjs`;`node --check scripts/check-receiver-conversion.mjs`;`node --check scripts/check-action-relay.mjs`;`node --check scripts/check-comment-relay.mjs`;`node --check scripts/check-viral-candidate.mjs`;`node --check scripts/check-devtools-readiness.mjs`;`node --no-warnings scripts/check-share-receiver-action.mjs`;`node --no-warnings scripts/check-share-receiver.mjs`;`node --no-warnings scripts/check-receiver-conversion.mjs`;`node --no-warnings scripts/check-action-relay.mjs`;`node --no-warnings scripts/check-comment-relay.mjs`;`node --no-warnings scripts/check-viral-candidate.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`npm run check`;`bash harness/init.sh` -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/viral-receiver-action`,对应约定 `/tmp/street-tasks-iter-worktrees/viral-receiver-action`;当前分支为 `codex/iter-viral-receiver-action`;新增检查先因缺少 `utils/share-receiver-actions.js` 按预期失败,补 helper 和详情接入后输出 `Share receiver action checks passed.`;集成断言先因 readiness 未运行新检查按预期失败,接入后通过;`node --check` 覆盖 helper、详情页和相关检查脚本均通过;`node --no-warnings` 检查输出 `Share receiver action checks passed.`、`Share receiver checks passed.`、`Receiver conversion checks passed.`、`Action relay checks passed.`、`Comment relay checks passed.`、`Viral candidate checks passed.`、`DevTools readiness checks passed.`;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`git diff --check` 通过无输出;`npm run check` 通过且 readiness 包含 `Share receiver action checks passed.`;`bash harness/init.sh` 完整跑通 -- 更新过的文件或工件:`utils/share-receiver-actions.js`,`pages/detail/detail.js`,`pages/detail/detail.wxml`,`pages/detail/detail.wxss`,`scripts/check-share-receiver-action.mjs`,`scripts/check-devtools-readiness.mjs`,`scripts/check-viral-candidate.mjs`,`harness/viral-receiver-action-product-brief.md`,`harness/viral-receiver-action-design-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:尚未在 WeChat DevTools 或真机中验证 from=share action strip 的真实布局、confirm 点击后的 receiverConversionPrompt、评论弹窗、游客登录引导、系统分享面板、风险态无按钮、窄屏换行和真实云端评论路径;自动检查只证明 helper、模板绑定、互斥结构和 readiness 接入 -- 下一步最佳动作:主 agent 复核后,在 WeChat DevTools 中分别打开 active 低风险、stale、高举报、resolved/expired/hidden 的 `from=share` 详情页,实测 action strip、confirm/comment 后提示和窄屏展示 - -### Session 064ViralJourneyEvidence - -- 日期:2026-06-16 -- 分支:`codex/iter-viral-journey-evidence` -- 本轮目标:N 组产品/设计/开发围绕“真实链路可验证性”补一个可复跑证据框架,验证从分享接收、完成行动、二跳接力到下一位接收者语境的核心链路,同时不声称替代 DevTools/真机 -- 产品假设:J/L/M 并列高分候选的主要短板是缺少连贯链路证据;把 helper 输出和详情页互斥条件组合成一个自动场景模型,可以降低后续迭代破坏真实传播链路的风险 -- 已完成:新增 `scripts/check-viral-journey-evidence.mjs`,直接导入 `buildShareReceiverGuide`、`buildShareReceiverActionStrip`、`buildReceiverConversionPrompt`、`buildActionRelayPrompt`、`buildCommentRelayPrompt` 和 `buildDetailShareMessage`,并静态读取 `pages/detail/detail.js/wxml`;新增 `harness/viral-journey-evidence-product-brief.md`、`harness/viral-journey-evidence-design-checklist.md` 和 `harness/viral-journey-manual-results.example.json`;readiness 和 viral candidate 都运行新证据脚本;证据脚本同时校验手测模板保持 `not_run` 且没有伪造 evidence -- 自动场景覆盖:`/pages/detail/detail?id=&from=share` 进入 active 且无 stale/report 的任务时,接收侧 guide 与 action strip 出现、普通 share panel 不出现;接收者 confirm/comment 后生成 `receiverConversionPrompt` 并优先于 action/comment relay;二跳分享路径带 `from=share&source=receiver`;`source=receiver` 接收说明是接力语境;普通入口和风险态不出现接收侧鼓励 action strip 或接收者公开接力 CTA -- 手测模板说明:`harness/viral-journey-manual-results.example.json` 只作为样例,`summary.overallStatus` 为 `not_run`,不包含真实执行结论;它不能被引用为 DevTools/真机通过证据 -- 运行过的验证:`node --no-warnings scripts/check-viral-journey-evidence.mjs` 先因脚本缺失按预期失败;补实现后运行 `node --check scripts/check-viral-journey-evidence.mjs`;`node --no-warnings scripts/check-viral-journey-evidence.mjs`;`node --no-warnings scripts/check-share-receiver-action.mjs`;`node --no-warnings scripts/check-receiver-conversion.mjs`;`node --no-warnings scripts/check-viral-candidate.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`npm run check`;`bash harness/init.sh` -- 已记录证据:新增证据脚本输出 `Viral journey evidence checks passed.`,并校验手测样例只能作为 `not_run` 模板;share receiver action 输出 `Share receiver action checks passed.`;receiver conversion 输出 `Receiver conversion checks passed.`;viral candidate 输出包含 `Viral journey evidence checks passed.` 和 `Viral candidate checks passed.`;readiness 输出包含 `Viral journey evidence checks passed.` 和 `DevTools readiness checks passed.`;JSON、harness、diff、npm check 和 init 结果以本 session 最终验证输出为准 -- 更新过的文件或工件:`scripts/check-viral-journey-evidence.mjs`,`scripts/check-devtools-readiness.mjs`,`scripts/check-viral-candidate.mjs`,`harness/viral-journey-evidence-product-brief.md`,`harness/viral-journey-evidence-design-checklist.md`,`harness/viral-journey-manual-results.example.json`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:未执行 WeChat DevTools/真机;自动脚本只证明 helper、路径、互斥结构和手测模板存在,不证明系统分享面板、真实点击、真实二跳、窄屏视觉、云端评论路径或分享转化提升 -- 下一步最佳动作:主 agent 复核后,在 WeChat DevTools 中用 active 低风险任务打开 `/pages/detail/detail?id=&from=share`,分别走确认和评论后分享,再用 `source=receiver` 打开二跳;同时用 stale/report/resolved/expired/hidden 夹具确认风险态不出现鼓励性接收动作 - -### Session 065ViralManualEvidenceGate - -- 日期:2026-06-16 -- 分支:`codex/iter-viral-manual-evidence-gate` -- 本轮目标:O 组产品+设计+开发 worker 在 N 组 `not_run` 模板基础上,新增传播链路真实本地手测结果文件的校验入口,同时不改变小程序产品交互 -- 产品假设:N 组自动模型能证明 helper 和页面互斥结构,但仍不能证明 DevTools/真机里的真实点击、系统分享面板、payload、二跳入口、窄屏布局和云端评论路径;因此需要一个只在 ignored/local 真实结果文件存在时生效的 gate,防止 example、占位文本或无 evidence 的 `passed` 被误当成验收通过 -- 已完成:新增 `harness/viral-journey-manual-evidence-product-brief.md` 和 `harness/viral-journey-manual-evidence-checklist.md`;新增 `scripts/check-viral-journey-manual-evidence.mjs`,默认扫描 ignored local viral journey 结果文件,无文件时通过但明确输出不代表 UI passed;真实文件存在时校验 schema、当前 branch/commit、environment、required journey 唯一性、`passed` evidence/actual/share payload、`failed` actual/followUp、`blocked` blocker/followUp,以及 `overallStatus` 聚合;显式拒绝未被 git ignore 的显式路径和 `harness/viral-journey-manual-results.example.json`;新增 `scripts/prepare-viral-journey-manual-evidence.mjs`,支持 `--dry-run` 和 ignored blocked draft 生成,draft 不含 passed journey;`scripts/check-devtools-readiness.mjs` 接入新 gate,`scripts/check-viral-journey-evidence.mjs` 要求新 gate/docs 存在 -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --no-warnings scripts/check-viral-journey-manual-evidence.mjs` 红灯确认脚本缺失;`node --check scripts/check-viral-journey-manual-evidence.mjs`;`node --check scripts/prepare-viral-journey-manual-evidence.mjs`;`node --check scripts/check-devtools-readiness.mjs`;`node --check scripts/check-viral-journey-evidence.mjs`;`node --no-warnings scripts/check-viral-journey-manual-evidence.mjs`;`node --no-warnings scripts/prepare-viral-journey-manual-evidence.mjs --dry-run`;`node --no-warnings scripts/check-viral-journey-evidence.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;显式传入 example 文件验证被拒绝;临时 ignored blocked draft 验证通过后清理;临时 bad passed-without-evidence 样例验证失败;临时 good passed-with-evidence/share-payload 样例验证通过后清理;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`npm run check`;`bash harness/init.sh` -- 已记录证据:初始 `node --no-warnings scripts/check-viral-journey-manual-evidence.mjs` 因 `MODULE_NOT_FOUND` 按预期失败;补实现后默认输出 `No viral journey manual evidence files found; nothing checked.` 和 `This is not UI passed evidence and does not mean DevTools or real-device UI passed.`;`prepare --dry-run` 输出将创建 `harness/manual-test-results.local-viral-journey.json`、当前 `codex/iter-viral-manual-evidence-gate@bf62161`,并说明没有写文件、不声称 UI passed;显式 example 输入被拒绝并提示 example 不能作为真实 manual evidence;显式未 ignored local 输入被拒绝;临时 bad passed 样例输出 `Bad passed sample rejected as expected.`;临时 good passed 样例输出 `Good passed sample accepted as expected.`;`node --no-warnings scripts/check-viral-journey-evidence.mjs` 输出 `Viral journey evidence checks passed.`;readiness 输出包含新 gate 的无文件/非 UI passed 文案并最终输出 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`git diff --check` 通过无输出;`npm run check` 完整跑通且 readiness 包含新 viral manual evidence gate;`bash harness/init.sh` 完整跑通 -- 更新过的文件或工件:`scripts/check-viral-journey-manual-evidence.mjs`,`scripts/prepare-viral-journey-manual-evidence.mjs`,`scripts/check-devtools-readiness.mjs`,`scripts/check-viral-journey-evidence.mjs`,`harness/viral-journey-manual-evidence-product-brief.md`,`harness/viral-journey-manual-evidence-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:没有执行 WeChat DevTools 或真机真实链路手测;当前无真实 local viral journey result 文件,因此新 gate 只验证了无文件语义、schema gate 和临时样例,不产生 UI passed;没有提交截图、录屏、payload 或日志;`harness/local-viral-journey-results*.json` 只有在本地 git ignore/exclude 后才会被 readiness 扫描,默认 prepare 使用现有 ignored 的 `harness/manual-test-results.local-viral-journey*.json` -- 下一步最佳动作:主 agent 复核后,用 `node --no-warnings scripts/prepare-viral-journey-manual-evidence.mjs --dry-run` 预览,真实手测时生成 ignored local draft,打开 DevTools/真机执行五条 required journey,填入 evidence 和 share payload/无法检查说明,再运行 `node --no-warnings scripts/check-viral-journey-manual-evidence.mjs ` 与 `node --no-warnings scripts/check-devtools-readiness.mjs` - -### Session 066ViralDevToolsJourneyLaunch - -- 日期:2026-06-16 -- 分支:`codex/iter-viral-devtools-journey-launch` -- 本轮目标:P 组产品/设计/开发围绕“传播链真实手测启动前置诊断”补一个单命令运行包,把 DevTools port/smoke blocker、viral journey evidence draft、existing local evidence scan 和五条必跑 journey 下一步收拢在一起 -- 产品假设:N/O 已能建模和校验传播链 evidence,但真实手测仍容易卡在 DevTools service port、草稿路径、payload 记录和 journey 顺序上;把启动前诊断做成无副作用单入口,可以让后续执行者更快判断当前是环境 blocked、证据 blocked、产品 failed,还是可进入真实 UI 手测 -- 已完成:产品 agent 新增 `harness/viral-devtools-journey-run-product-brief.md`;设计/QA agent 新增 `harness/viral-devtools-journey-run-checklist.md`;开发 agent 新增 `scripts/prepare-viral-journey-devtools-run.mjs`;主线程补齐 no-side-effect smoke access probe、recovery dry-run hint、`--strict` 环境 blocker 失败语义、`npm run prepare:viral-journey-run` 入口,并把新启动包接入 `scripts/check-devtools-readiness.mjs` -- 运行包行为:默认不写文件、不 quit/open DevTools、不 preview、不清缓存、不杀进程;依次运行 `scripts/inspect-devtools-port-state.mjs`、无副作用 `scripts/check-devtools-smoke-access.mjs`、`scripts/prepare-viral-journey-manual-evidence.mjs --dry-run`、`scripts/check-viral-journey-manual-evidence.mjs`,再从 `harness/viral-journey-manual-results.example.json` 输出五条 required journeys 和 share payload 注意事项 -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check scripts/prepare-viral-journey-devtools-run.mjs`;`node --check scripts/check-devtools-readiness.mjs`;`npm run prepare:viral-journey-run`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node --no-warnings scripts/prepare-viral-journey-devtools-run.mjs --strict` 负向;`npm run check`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check` -- 已记录证据:`npm run prepare:viral-journey-run` 输出 port status `unknown`、diagnosis `connect_refused`,smoke access `blocked`,service port 9420 无 listener 且无 matching ide-http-port declaration;同一输出确认没有 quit/open/preview/cache/process 副作用,没有写 local draft,并列出五条 journeys 与 `from=share&source=receiver` payload 要求;readiness 输出包含新启动包且最终 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`;strict 负向按预期 exit 1 并提示 port status `unknown`、smoke access `blocked`;`npm run check`、`bash harness/init.sh`、JSON、harness 和 `git diff --check` 均通过 -- 更新过的文件或工件:`scripts/prepare-viral-journey-devtools-run.mjs`,`scripts/check-devtools-readiness.mjs`,`package.json`,`harness/viral-devtools-journey-run-product-brief.md`,`harness/viral-devtools-journey-run-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:P 组仍未执行 WeChat DevTools 或真机真实链路手测;当前 9420 service port blocker 只是环境阻塞,不是 UI failed;没有真实 local viral journey result 文件,也没有截图、录屏、payload 或日志 evidence;readiness/diagnostic/dry-run/checker-passed 均不能写成 UI passed -- 下一步最佳动作:若要进入真实手测,先在 WeChat DevTools UI 启用 Settings -> Security Settings -> Service Port 并确认端口匹配 9420,重新运行 `npm run prepare:viral-journey-run` 直到 port/smoke ready;随后生成 ignored local draft,执行五条 viral journey,填入 evidence 和 share payload/无法检查说明,再运行 manual evidence checker 与 readiness - -### Session 067ViralBlockedEvidenceCapture - -- 日期:2026-06-16 -- 分支:`codex/iter-viral-blocked-evidence-capture` -- 本轮目标:Q 组产品/设计/开发把 P 组诊断到的 DevTools port/smoke blocker 显式落成 O 组 checker 可校验的 ignored local blocked result,避免继续停留在 no files/nothing checked -- 产品假设:P 能告诉执行者当前为什么不能手测,但没有留下 O checker 会扫描的结果文件;当环境 blocker 真实存在且用户显式运行 capture 时,把 blocker 写成全 blocked local JSON,可以让交接者清楚知道五条 journey 未执行的原因,并把“无 UI evidence”从口头说明变成可校验 artifact -- 已完成:产品 agent 新增 `harness/viral-blocked-evidence-capture-product-brief.md`;设计/QA agent 新增 `harness/viral-blocked-evidence-capture-checklist.md`;开发 agent 新增 `scripts/capture-viral-journey-blocked-evidence.mjs`;主线程修正文档命令名,新增 `npm run capture:viral-blocked-evidence`,并把 capture 脚本和文档接入 `scripts/check-devtools-readiness.mjs` 的文件/语义要求但不在 readiness 默认执行写文件 capture -- 脚本行为:显式运行时执行只读 `scripts/inspect-devtools-port-state.mjs` 与无副作用 `scripts/check-devtools-smoke-access.mjs`,写入 ignored local `harness/manual-test-results.local-viral-journey-blocked.json`,记录当前 branch/commit/testedAt/environment/summary,五条 required journeys 全部为 `blocked`,写入具体 port/smoke blocker、actual、followUp,并自动运行 `node --no-warnings scripts/check-viral-journey-manual-evidence.mjs ` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check scripts/capture-viral-journey-blocked-evidence.mjs`;`node scripts/capture-viral-journey-blocked-evidence.mjs --force`;`node scripts/capture-viral-journey-blocked-evidence.mjs --out harness/viral-journey-blocked.json` 负向;已存在文件未 `--force` 负向;篡改 `summary.overallStatus=passed` 后运行 manual evidence checker 负向;清理 ignored local JSON;`npm run check`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`bash harness/init.sh` -- 已记录证据:正向 capture 输出 `Checked viral journey manual evidence: harness/manual-test-results.local-viral-journey-blocked.json (overallStatus=blocked)`,生成 JSON 解析为 `schemaVersion=viral-journey-manual-results.v1`、五条 journey 均 `blocked`、DevTools 版本 `2.01.2510290; build 4240.111`、blocker 包含 `status=unknown`、`diagnosis=connect_refused`、9420 无 listener 和 smoke blocked;未 ignored 输出路径被拒绝;已有文件无 `--force` 被拒绝;篡改 `overallStatus=passed` 被 checker 拒绝并提示应为 `blocked`;清理后 `git status --ignored` 无 local result 残留 -- 更新过的文件或工件:`scripts/capture-viral-journey-blocked-evidence.mjs`,`scripts/check-devtools-readiness.mjs`,`package.json`,`harness/viral-blocked-evidence-capture-product-brief.md`,`harness/viral-blocked-evidence-capture-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:Q 组仍未执行 WeChat DevTools 或真机真实链路手测;capture 只证明当前环境 blocker 被结构化记录并通过 O checker,不证明 UI passed 或产品 failed;默认 readiness 不执行 capture,因此真实交接需要用户显式运行 `npm run capture:viral-blocked-evidence` -- 下一步最佳动作:若继续推进真实证据,先恢复 WeChat DevTools Service Port 9420 并确认 `npm run prepare:viral-journey-run` port/smoke ready;若仍 blocked,显式运行 `npm run capture:viral-blocked-evidence -- --force` 记录当前 blocker;恢复后执行五条 viral journey 并用真实 evidence 替换 blocked local JSON - -### Session 068ViralTargetedRelay - -- 日期:2026-06-16 -- 分支:`codex/iter-viral-targeted-relay` -- 本轮目标:R 组产品/设计/开发把 `from=share` 接收者完成 confirm/comment 后的二跳提示从泛化“继续接力”升级为目标化接力,降低用户自己思考“转给谁、为什么可信、对方先看什么”的成本 -- 产品假设:接收者刚确认或补线索后,已经有一次真实判断或贡献;如果二跳提示明确推荐目标人群、解释刚发生动作带来的可信信号,并告诉下一位先核对什么,更可能把一次接收转化为有边界的二次传播,而不是无差别扩散 -- 已完成:产品 agent 新增 `harness/viral-targeted-relay-product-brief.md`;设计/QA agent 新增 `harness/viral-targeted-relay-design-checklist.md`;开发 agent 扩展 `utils/receiver-conversion.js`,给低风险 receiverConversionPrompt 增加 `targetRows` 三行结构:`推荐转给`、`为什么可信`、`下一位先看`;目标化文案对齐当前 `lost_found`、`help_needed`、`street_update`、`check_in` 分类枚举;风险态、弱 stale/report、resolved/expired/hidden 继续保持 `targetRows` 为空且不展示公开接力 CTA -- 页面变化:`pages/detail/detail.wxml` 在 receiver conversion panel 内渲染目标化三行,且使用 `receiverConversionPrompt.targetRows && receiverConversionPrompt.targetRows.length` 防空;`pages/detail/detail.wxss` 增加两列 label/value 样式,保证短 label 固定、value 可换行 -- 检查变化:`scripts/check-receiver-conversion.mjs` 覆盖三行 label、紧凑文案、lost/found 差异、真实项目分类目标化、fallback、风险态空 targetRows 和公开 share CTA 只在 shouldRelay 时出现;`scripts/check-viral-journey-evidence.mjs` 覆盖接收者 confirm/comment 后 targetRows 存在;`scripts/check-devtools-readiness.mjs` 把 R 组产品/设计文档纳入 readiness 文件和语义文档扫描 -- 运行过的验证:`pwd`;`git log --oneline -5`;`bash harness/init.sh`;`node --check utils/receiver-conversion.js`;`node --check pages/detail/detail.js`;`node --check scripts/check-receiver-conversion.mjs`;`node --check scripts/check-viral-journey-evidence.mjs`;`node --check scripts/check-devtools-readiness.mjs`;`node --no-warnings scripts/check-receiver-conversion.mjs`;`node --no-warnings scripts/check-viral-journey-evidence.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`npm run check`;`bash harness/init.sh` -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/viral-targeted-relay`,对应约定 `/tmp/street-tasks-iter-worktrees/viral-targeted-relay`;当前分支为 `codex/iter-viral-targeted-relay`;receiver conversion 输出 `Receiver conversion checks passed.`;viral journey evidence 输出 `Viral journey evidence checks passed.`;readiness 输出包含 receiver conversion、viral journey evidence、manual evidence gate、DevTools manual-run preparation、share receiver、share receiver action、trust insight、candidate、admin auth、map list resilience 和 blocked summary preflight,并最终输出 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`;readiness 仍报告 9420 service port `connect_refused` 和 smoke access `blocked`,这只记录环境 blocker,不代表 UI passed;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`git diff --check` 通过无输出;`npm run check` 完整跑通;最终 `bash harness/init.sh` 完整跑通 -- 更新过的文件或工件:`utils/receiver-conversion.js`,`pages/detail/detail.wxml`,`pages/detail/detail.wxss`,`scripts/check-receiver-conversion.mjs`,`scripts/check-viral-journey-evidence.mjs`,`scripts/check-devtools-readiness.mjs`,`harness/viral-targeted-relay-product-brief.md`,`harness/viral-targeted-relay-design-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:R 组仍未执行 WeChat DevTools 或真机真实链路手测;未验证系统分享面板、真实 `from=share&source=receiver` payload、二跳接收者真实打开、窄屏换行、云端评论路径和转发率提升;本轮尝试查找本地 wcc/wcsc 编译器未找到可直接调用路径,因此没有记录 WXML/WXSS 编译器通过证据 -- 下一步最佳动作:提交 R 组并启动用户评测 agent;评测时重点比较 R 相比 Q 是否明显提升用户侧二跳接力意愿,同时保持风险态和关闭态不鼓励公开扩散 - -### Session 069ReceiverActionSource - -- 日期:2026-06-16 -- 分支:`codex/iter-viral-receiver-action-source` -- 本轮目标:S 组实现“接收者二跳 action 来源语义”的最小代码迭代,让 `source=receiver` 的下一位接收者能区分上一位刚确认还是刚补线索 -- 产品/设计上下文:检测到产品/设计 agent 并行新增 `harness/viral-receiver-action-source-product-brief.md` 与 `harness/viral-receiver-action-source-design-checklist.md`,本轮读取后按其边界执行,未回退这些未跟踪文件 -- TDD 红灯:先更新 `scripts/check-receiver-conversion.mjs` 与 `scripts/check-viral-journey-evidence.mjs`,`node --no-warnings scripts/check-receiver-conversion.mjs` 和 `node --no-warnings scripts/check-viral-journey-evidence.mjs` 均先因二跳 path 缺少 `receiverAction=confirm` 按预期失败;随后补充风险态 path 不应携带 `receiverAction` 的断言,两条检查再次先因弱风险/风险 confirm path 仍携带 action 按预期失败 -- 已完成:`utils/receiver-conversion.js` 只在 `shouldRelay` 为真且 action 为 `confirm/comment` 时把 `receiverAction=confirm/comment` 写入二跳 sharePath,继续保留 `from=share&source=receiver`;风险/关闭或任意 stale/report 信号下不携带 receiverAction,也不展示公开接力 CTA -- 已完成:`utils/share-receiver.js` 新增 `receiverAction` 归一化,只在 `source=receiver` 下识别小写 `confirm/comment`;confirm 文案强调“上一位刚确认/确认和现场信号”,comment 文案强调“上一位刚补线索/先看最新评论”,缺失或未知 action 保持 R 组泛化接力文案;`source=confirm`、`source=comment` 和普通 `from=share` 会忽略 receiverAction -- 已完成:`pages/detail/detail.js` 在 `entryQuery` 中显式保留 `receiverAction`,并传给 `buildShareReceiverGuide`,不改变已有 `source=comment`、`source=confirm`、`source=receiver` 基础行为 -- 检查变化:`scripts/check-receiver-conversion.mjs` 覆盖 confirm/comment 二跳 path、下一位 confirm/comment 文案、未知 action 回退、非 receiver source 忽略 action、风险态无 receiverAction;`scripts/check-share-receiver.mjs` 覆盖接收侧 helper 的 action 来源文案、未知 action 回退和风险态优先;`scripts/check-viral-journey-evidence.mjs` 覆盖同一链路在端到端模型里的 path、接收文案和风险态回退 -- readiness 说明:`scripts/check-devtools-readiness.mjs` 已经在 runCheck 中覆盖 `scripts/check-receiver-conversion.mjs` 与 `scripts/check-viral-journey-evidence.mjs`,所以本轮无需新增运行项;主线程只把 S 组产品/设计文档加入 required files/readinessDocs,并已运行 readiness 证明它会执行更新后的检查 -- 手测证据口径更新:`harness/viral-journey-manual-results.example.json`、`scripts/check-viral-journey-manual-evidence.mjs`、`scripts/prepare-viral-journey-devtools-run.mjs`、`harness/viral-devtools-journey-run-product-brief.md` 和 `harness/viral-devtools-journey-run-checklist.md` 已同步要求 confirm/comment 二跳 payload 分别包含 `receiverAction=confirm/comment`;readiness 也要求 S 组产品/设计文档存在 -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md`、`harness/feature_list.json`、产品/设计新增 harness 文档;`git log --oneline -5`;`bash harness/init.sh`;红灯运行 `node --no-warnings scripts/check-receiver-conversion.mjs` 与 `node --no-warnings scripts/check-viral-journey-evidence.mjs`;`node --check utils/receiver-conversion.js`;`node --check utils/share-receiver.js`;`node --check pages/detail/detail.js`;`node --check scripts/check-receiver-conversion.mjs`;`node --check scripts/check-share-receiver.mjs`;`node --check scripts/check-viral-journey-evidence.mjs`;`node --check scripts/check-viral-journey-manual-evidence.mjs`;`node --check scripts/prepare-viral-journey-devtools-run.mjs`;`node --check scripts/check-devtools-readiness.mjs`;`node --no-warnings scripts/check-receiver-conversion.mjs`;`node --no-warnings scripts/check-share-receiver.mjs`;`node --no-warnings scripts/check-viral-journey-evidence.mjs`;`node --no-warnings scripts/check-viral-journey-manual-evidence.mjs`;临时 ignored local manual evidence 正向/缺失 receiverAction 负向样例;`node --no-warnings scripts/check-viral-candidate.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`git diff --check -- utils/receiver-conversion.js utils/share-receiver.js pages/detail/detail.js scripts/check-receiver-conversion.mjs scripts/check-share-receiver.mjs scripts/check-viral-journey-evidence.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;全量 `git diff --check`;`npm run check`;最终 `bash harness/init.sh` -- 已记录证据:最终语法检查五条均通过且无输出;`node --no-warnings scripts/check-receiver-conversion.mjs` 输出 `Receiver conversion checks passed.`;`node --no-warnings scripts/check-viral-journey-evidence.mjs` 输出 `Viral journey evidence checks passed.`;`node --no-warnings scripts/check-share-receiver.mjs` 输出 `Share receiver checks passed.`;`node --no-warnings scripts/check-viral-candidate.mjs` 输出 `Viral journey evidence checks passed.` 和 `Viral candidate checks passed.`;`node --no-warnings scripts/check-devtools-readiness.mjs` 输出 `Receiver conversion checks passed.`、`Viral journey evidence checks passed.` 与最终 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`,同时仍报告 DevTools 9420 `connect_refused`/smoke `blocked`,这不是 UI passed;指定范围和全量 `git diff --check` 均通过无输出;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`npm run check` 完整跑通;最终 `bash harness/init.sh` 完整跑通 -- 更新过的文件或工件:`utils/receiver-conversion.js`,`utils/share-receiver.js`,`pages/detail/detail.js`,`scripts/check-receiver-conversion.mjs`,`scripts/check-share-receiver.mjs`,`scripts/check-viral-journey-evidence.mjs`,`scripts/check-viral-journey-manual-evidence.mjs`,`scripts/prepare-viral-journey-devtools-run.mjs`,`scripts/check-devtools-readiness.mjs`,`harness/viral-journey-manual-results.example.json`,`harness/viral-devtools-journey-run-product-brief.md`,`harness/viral-devtools-journey-run-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md`,`harness/viral-receiver-action-source-product-brief.md`;产品/设计 agent 并行新增的 `harness/viral-receiver-action-source-design-checklist.md` 保持原样 -- 已知风险或未解决问题:尚未在 WeChat DevTools 或真机中验证真实系统分享面板是否保留 `receiverAction` query、二跳路径真实打开后的 UI、confirm/comment 差异文案窄屏表现、云端评论成功后 path 生成、状态瞬间变风险时的真实 UI,以及 action 来源语义是否提升转化;当前 DevTools service port 9420 仍是环境 blocker -- 下一步最佳动作:恢复 DevTools service port 后,用 active 低风险任务分别从 `/pages/detail/detail?id=&from=share` 完成 confirm 和 comment,触发系统分享并记录实际 path;再分别打开 `source=receiver&receiverAction=confirm/comment`、未知 action、普通入口和风险态,确认接收文案与互斥规则符合预期 - -### Session 070ViralShareReason - -- 日期:2026-06-16 -- 分支:`codex/iter-viral-share-reason` -- 本轮目标:T 组产品/设计/开发在 R/S 的目标化二跳基础上增加“可转述理由”,让 `from=share` 接收者完成低风险 confirm/comment 后能直接看到一句可说给下一位的话,降低二次分享表达成本 -- 产品假设:继续增加 query 参数主要帮助下一位页面解析,但无法解决发送者在微信聊天里“我为什么转给你”的表达问题;一条短、克制、可转述的理由更贴近真实用户侧裂变,不依赖系统分享面板预填聊天文本 -- 已完成:产品 agent 新增 `harness/viral-share-reason-product-brief.md`;设计/QA agent 新增 `harness/viral-share-reason-design-checklist.md`;开发 agent 在 `utils/receiver-conversion.js` 中为低风险 `receiverConversionPrompt.shouldRelay` 返回 `shareReason`,confirm 文案为“我刚确认过,帮忙再核对一下”,comment 文案为“我刚补了线索,你先看最新评论”;风险/关闭/弱 stale/report 下 `shareReason` 为 `null` -- 页面变化:`pages/detail/detail.wxml` 在 receiver conversion 的三行 `targetRows` 后、actions 前渲染 `shareReason`,不作为第四行 target row;`pages/detail/detail.wxss` 增加低密度 label/value 样式,保证短理由可换行且不挤压主分享按钮 -- 检查变化:`scripts/check-receiver-conversion.mjs` 覆盖低风险 confirm/comment shareReason 存在、长度、禁用词、文案差异、风险态为空、WXML 顺序和非第四行 target row;`scripts/check-viral-journey-evidence.mjs` 覆盖端到端模型中的 shareReason、禁用词、风险态互斥和手测模板必须提示观察 shareReason;`harness/viral-journey-manual-results.example.json` 的 confirm/comment journey 已加入页面内 share reason 观察点;`scripts/check-devtools-readiness.mjs` 把 T 组产品/设计文档纳入 required files 和 readiness docs -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check utils/receiver-conversion.js`;`node --check pages/detail/detail.js`;`node --check scripts/check-receiver-conversion.mjs`;`node --check scripts/check-viral-journey-evidence.mjs`;`node --check scripts/check-devtools-readiness.mjs`;`node --no-warnings scripts/check-receiver-conversion.mjs`;`node --no-warnings scripts/check-viral-journey-evidence.mjs`;`node --no-warnings scripts/check-share-receiver.mjs`;`node --no-warnings scripts/check-viral-candidate.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`node --no-warnings scripts/check-viral-journey-manual-evidence.mjs`;`npm run check`;最终 `bash harness/init.sh` -- 已记录证据:`node --check` 语法检查均通过且无输出;`node --no-warnings scripts/check-receiver-conversion.mjs` 输出 `Receiver conversion checks passed.`;`node --no-warnings scripts/check-viral-journey-evidence.mjs` 输出 `Viral journey evidence checks passed.`;`node --no-warnings scripts/check-share-receiver.mjs` 输出 `Share receiver checks passed.`;`node --no-warnings scripts/check-viral-candidate.mjs` 输出 `Viral journey evidence checks passed.` 和 `Viral candidate checks passed.`;`node --no-warnings scripts/check-devtools-readiness.mjs` 输出 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`,同时仍报告 DevTools 9420 `connect_refused`/smoke `blocked`,这不是 UI passed;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`git diff --check` 通过无输出;manual evidence gate 输出没有本地结果文件且不是 UI passed evidence;`npm run check` 完整跑通并在 manual run package 中列出 confirm/comment share reason 观察点;最终 `bash harness/init.sh` 完整跑通;产品/设计/开发 agent 均完成且未提交 commit -- 更新过的文件或工件:`utils/receiver-conversion.js`,`pages/detail/detail.wxml`,`pages/detail/detail.wxss`,`scripts/check-receiver-conversion.mjs`,`scripts/check-viral-journey-evidence.mjs`,`scripts/check-devtools-readiness.mjs`,`harness/viral-journey-manual-results.example.json`,`harness/viral-share-reason-product-brief.md`,`harness/viral-share-reason-design-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:尚未在 WeChat DevTools 或真机中验证 shareReason 的真实布局、系统分享面板关系、真实 `from=share&source=receiver&receiverAction=confirm/comment` payload、云端评论成功后提示、窄屏换行,以及用户是否会在聊天里手动转述该理由;当前 DevTools service port 9420 仍是环境 blocker -- 下一步最佳动作:提交 T 组,并发送给用户评测 agent 与 R/S=99 的结果比较,重点看“可转述理由”是否比 S 的参数语义更接近真实自发裂变 - -### Session 071ViralRelayChannelPicker - -- 日期:2026-06-17 -- 分支:`codex/iter-viral-relay-channel-picker` -- 本轮目标:U 组产品/设计/开发在 T 的可转述理由基础上增加“适合转给”的场景建议,让 `from=share` 接收者完成低风险 confirm/comment 后不只知道怎么说,也知道更适合发给哪类场景 -- 产品假设:T 已解决“怎么说”的表达成本,下一层瓶颈是“发给谁/哪里”的选择成本;给 2-3 个泛化场景建议,如楼栋群、门卫/前台、路过朋友、同路线邻居,比继续润色 shareReason 更可能推动用户侧二跳 -- 已完成:产品 agent 新增 `harness/viral-relay-channel-picker-product-brief.md`;设计/QA agent 新增 `harness/viral-relay-channel-picker-design-checklist.md`;开发 agent 在 `utils/receiver-conversion.js` 中为低风险 `receiverConversionPrompt.shouldRelay` 返回 `relayChannels`,按 `category`、`intent` 和 confirm/comment 生成 2-3 个 label/hint 场景建议;风险/关闭/弱 stale/report 或 stale/report action 下 `relayChannels` 为空 -- 页面变化:`pages/detail/detail.wxml` 在 receiver conversion 的三行 `targetRows` 后、`shareReason` 前渲染 `relayChannels`,chip 只是场景提示,不带 `open-type="share"` 或联系人/群选择绑定;`pages/detail/detail.wxss` 增加可换行 chip 样式,保持主分享按钮唯一 -- 检查变化:`scripts/check-receiver-conversion.mjs` 覆盖 relayChannels 数量、短文案、禁用联系人/群关系暗示词、confirm/comment 差异、lost/found 差异、真实分类选项、风险态为空、path 不新增联系人/群 query、WXML 顺序和 chip 非 share CTA;`scripts/check-viral-journey-evidence.mjs` 覆盖同一链路和手测模板必须观察 relay channel suggestions;`harness/viral-journey-manual-results.example.json` 的 confirm/comment journey 已加入 2-3 个场景建议观察点;`scripts/check-devtools-readiness.mjs` 把 U 组产品/设计文档纳入 required files 和 readiness docs -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check utils/receiver-conversion.js`;`node --check pages/detail/detail.js`;`node --check scripts/check-receiver-conversion.mjs`;`node --check scripts/check-viral-journey-evidence.mjs`;`node --check scripts/check-devtools-readiness.mjs`;`node --no-warnings scripts/check-receiver-conversion.mjs`;`node --no-warnings scripts/check-viral-journey-evidence.mjs`;`node --no-warnings scripts/check-share-receiver.mjs`;`node --no-warnings scripts/check-viral-candidate.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`node --no-warnings scripts/check-viral-journey-manual-evidence.mjs`;`npm run check`;最终 `bash harness/init.sh` -- 已记录证据:`node --check` 语法检查均通过且无输出;`node --no-warnings scripts/check-receiver-conversion.mjs` 输出 `Receiver conversion checks passed.`;`node --no-warnings scripts/check-viral-journey-evidence.mjs` 输出 `Viral journey evidence checks passed.`;`node --no-warnings scripts/check-share-receiver.mjs` 输出 `Share receiver checks passed.`;`node --no-warnings scripts/check-viral-candidate.mjs` 输出 `Viral journey evidence checks passed.` 和 `Viral candidate checks passed.`;`node --no-warnings scripts/check-devtools-readiness.mjs` 输出 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`,同时仍报告 DevTools 9420 `connect_refused`/smoke `blocked`,这不是 UI passed;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`git diff --check` 通过无输出;manual evidence gate 输出没有本地结果文件且不是 UI passed evidence;`npm run check` 完整跑通并在 manual run package 中列出 relay channel suggestions 观察点;最终 `bash harness/init.sh` 完整跑通;产品/设计/开发 agent 均完成且未提交 commit -- 更新过的文件或工件:`utils/receiver-conversion.js`,`pages/detail/detail.wxml`,`pages/detail/detail.wxss`,`scripts/check-receiver-conversion.mjs`,`scripts/check-viral-journey-evidence.mjs`,`scripts/check-devtools-readiness.mjs`,`harness/viral-journey-manual-results.example.json`,`harness/viral-relay-channel-picker-product-brief.md`,`harness/viral-relay-channel-picker-design-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:尚未在 WeChat DevTools 或真机中验证 relay channel chips 的真实布局、系统分享面板关系、真实 payload、云端评论成功后提示、窄屏换行,以及用户是否会按“楼栋群/门卫/同路线”等场景建议手动转发;当前 DevTools service port 9420 仍是环境 blocker -- 下一步最佳动作:提交 U 组,并发送给用户评测 agent 与 R/S/T=99 的结果比较,重点看“发给谁/哪里”的场景建议是否比 T 的“怎么说”更接近真实自发裂变 - -### Session 072ViralTimelineShare - -- 日期:2026-06-17 -- 分支:`codex/iter-viral-timeline-share` -- 本轮目标:V 组产品/设计/开发在 U 的接收者二跳建议之外,补一个真正的微信系统传播渠道:详情页分享到朋友圈,同时遵守官方单页模式、`onShareTimeline` query 限制和不诱导分享边界 -- 产品假设:U 已经把“转给谁/怎么说”做到较高水平,但仍没有新增真实外部传播面;朋友圈分享降低具体选人的摩擦,可能让附近失物、地点动态和求助被弱关系看到,再回到 `from=share` 接收链路完成确认、评论或二跳 -- 已完成:产品 agent 新增 `harness/viral-timeline-share-product-brief.md`,明确朋友圈是系统渠道补齐而非替代 receiver relay;设计/QA agent 新增 `harness/viral-timeline-share-design-checklist.md`,列出官方 API、单页模式、风险互斥、文案和手测证据要求;开发 agent 新增 `utils/timeline-share.js` 和 `scripts/check-timeline-share.mjs` -- 页面变化:`pages/detail/detail.js` 新增 `onShareTimeline()` 并复用 timeline helper;`configureShareMenu(post)` 默认只展示 `shareAppMessage`,只有已加载任务为 `active` 且 `staleCount/reportCount` 都为 0 时才把 `shareTimeline` 和 `shareAppMessage` 一起放进 `wx.showShareMenu` -- 分享 payload:低风险 active 任务的 timeline query 为 `id=&from=share&source=timeline&shareChannel=timeline`,继续走现有 `from=share` 接收者说明;`onShareTimeline` 不返回自定义 `path`;风险、关闭、隐藏或未知任务即使 helper 被直接调用也只返回谨慎标题,且不带任务图片 -- 检查变化:`scripts/check-timeline-share.mjs` 覆盖 no-path payload、timeline query、低风险菜单门禁、onLoad 不提前启用 timeline、弱 stale/report 排除、风险态无 imageUrl、禁用诱导词;`scripts/check-devtools-readiness.mjs` 和 `scripts/check-viral-candidate.mjs` 已接入新检查和 V 组产品/设计文档 -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check utils/timeline-share.js`;`node --check pages/detail/detail.js`;`node --check scripts/check-timeline-share.mjs`;`node --check scripts/check-devtools-readiness.mjs`;`node --check scripts/check-viral-candidate.mjs`;`node --no-warnings scripts/check-timeline-share.mjs`;`node --no-warnings scripts/check-viral-candidate.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`node --no-warnings scripts/check-viral-journey-manual-evidence.mjs`;`git diff --check`;`npm run check`;最终 `bash harness/init.sh` -- 已记录证据:`node --no-warnings scripts/check-timeline-share.mjs` 输出 `Timeline share checks passed.`;`node --no-warnings scripts/check-viral-candidate.mjs` 输出 `Viral journey evidence checks passed.`、`Timeline share checks passed.` 和 `Viral candidate checks passed.`;`node --no-warnings scripts/check-devtools-readiness.mjs` 输出 `Timeline share checks passed.` 与最终 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`,同时仍报告 DevTools port 9420 `connect_refused`、smoke `blocked`,这不是 UI passed;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;manual evidence gate 输出没有本地结果文件且不是 UI passed evidence;`git diff --check` 通过无输出;`npm run check` 完整跑通;最终 `bash harness/init.sh` 完整跑通 -- 更新过的文件或工件:`utils/timeline-share.js`,`pages/detail/detail.js`,`scripts/check-timeline-share.mjs`,`scripts/check-devtools-readiness.mjs`,`scripts/check-viral-candidate.mjs`,`harness/viral-timeline-share-product-brief.md`,`harness/viral-timeline-share-design-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:尚未在 WeChat DevTools 或真机中验证右上角系统菜单是否真实出现“分享到朋友圈”、朋友圈卡片标题/图片、从朋友圈打开的单页模式首屏、tabBar 缺失下的信息可读性、基础库/微信版本差异、iOS/Android 差异、云端未登录读取路径和真实用户是否会从朋友圈继续确认/评论 -- 下一步最佳动作:提交 V 组,并发送给用户评测 agent 与 U=99 的结果比较,重点看“真实新增系统渠道”是否比 U 的“转给谁/哪里”建议更接近自发裂变;若继续迭代,优先把朋友圈手测 evidence schema 加入现有五条 viral journey manual-run 包 - -### Session 073ViralTimelineEvidence - -- 日期:2026-06-17 -- 分支:`codex/iter-viral-timeline-evidence` -- 本轮目标:W 组产品/设计/开发把 V 组朋友圈渠道纳入现有 ignored/local viral journey manual evidence schema,让真实手测必须覆盖 timeline 菜单、payload、单页模式、风险态无 timeline 和 U 轮 receiver 链路不回退 -- 产品假设:V 已补上微信朋友圈系统渠道,继续润色分享文案边际收益低;要突破 V=99 的扣分点,必须把真实菜单、朋友圈卡片、单页模式、query/payload 和风险态反证写进可复跑 evidence schema,而不是继续依赖 readiness 或口头描述 -- 已完成:产品 agent 新增 `harness/viral-timeline-evidence-product-brief.md`;设计/QA agent 新增 `harness/viral-timeline-evidence-checklist.md`;开发 agent 将 `harness/viral-journey-manual-results.example.json` 从五条 required journeys 扩为七条,新增 `timeline-share-channel` 与 `timeline-risk-gating`;提交前 review 收紧 `timeline-risk-gating`,使无法 inspect 系统菜单只能作为 blocked,不能替代 passed 的菜单缺失观察 -- 检查变化:`scripts/check-viral-journey-manual-evidence.mjs` 现在要求七条 required journeys;`timeline-share-channel` passed 必须记录真实系统菜单同时有好友分享和朋友圈、timeline payload/query 或无法检查原因、图片或图片说明、单页/首屏可读;inspectable query 必须包含 `id`、`from=share`、`source=timeline`、`shareChannel=timeline` -- 风险态变化:`timeline-risk-gating` passed 必须记录 shareTimeline 缺失或具体无法触发原因、谨慎 no-timeline 语义,且不得包含鼓励性朋友圈 CTA;风险态若能 inspect payload,标题也必须谨慎 -- 工具链变化:`scripts/prepare-viral-journey-devtools-run.mjs` 输出七条 run package,并区分 receiverAction share payload 与 timeline payload;`scripts/capture-viral-journey-blocked-evidence.mjs` 生成七条 blocked journeys;`scripts/check-viral-journey-evidence.mjs` 要求 example 中七条 journey 顺序和 timeline 观察点;`scripts/check-devtools-readiness.mjs` 要求 W 组产品/QA 文档存在;旧 P 组 run-package 文档和 Q 组 blocked-capture 文档已更新为“前五条 receiver 基线 + 两条 timeline,总计七条” -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check` 覆盖五个改动脚本;`node --no-warnings scripts/check-viral-journey-manual-evidence.mjs`;`node --no-warnings scripts/check-viral-journey-evidence.mjs`;`node --no-warnings scripts/prepare-viral-journey-devtools-run.mjs`;临时 ignored good sample 七条 journeys;临时 bad sample 缺 `shareChannel=timeline`;临时 bad risk sample 只有 inability-to-inspect 而无 observed no-shareTimeline;临时 positive risk sample 记录 observed no-shareTimeline;临时 blocked capture 七条 blocked journeys;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`npm run check`;最终 `bash harness/init.sh` -- 已记录证据:manual evidence 默认输出 `No viral journey manual evidence files found; nothing checked.` 且声明不是 UI passed;good sample 输出 `Checked viral journey manual evidence ... (overallStatus=passed)`;bad sample 被拒绝并报 `journey timeline-share-channel.timelinePayload.query must include shareChannel=timeline`;risk bad sample 被拒绝并报 `journey timeline-risk-gating must record observed shareTimeline/timeline menu absence; inability to inspect belongs in a blocked journey, not passed.`;risk positive sample 通过;blocked capture 输出 `overallStatus: blocked` 且 JSON 包含 7 条 journey;prepare/readiness 输出七条 run package 并仍报告 DevTools 9420 `connect_refused` / smoke `blocked`,这不是 UI passed;JSON、harness、diff、npm check 和最终 init 均通过 -- 更新过的文件或工件:`harness/viral-journey-manual-results.example.json`,`harness/viral-timeline-evidence-product-brief.md`,`harness/viral-timeline-evidence-checklist.md`,`harness/viral-devtools-journey-run-product-brief.md`,`harness/viral-devtools-journey-run-checklist.md`,`harness/viral-blocked-evidence-capture-product-brief.md`,`harness/viral-blocked-evidence-capture-checklist.md`,`scripts/check-viral-journey-manual-evidence.mjs`,`scripts/prepare-viral-journey-devtools-run.mjs`,`scripts/capture-viral-journey-blocked-evidence.mjs`,`scripts/check-viral-journey-evidence.mjs`,`scripts/check-devtools-readiness.mjs`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:尚未恢复 WeChat DevTools service port,未产生真实系统菜单截图、朋友圈卡片、单页模式首屏、真实 timeline query/payload、风险态菜单缺失截图或真机 iOS/Android 证据;W 只是让这些证据不可跳过,不能证明朋友圈渠道已经跑通或提升真实转化 -- 下一步最佳动作:提交 W 组并发送给用户评测 agent 与 V=99 比较;若继续迭代,优先恢复 DevTools service port 或生成符合七条 schema 的真实 blocked/passed local evidence,而不是继续扩展文案 - -### Session 074ViralTimelineLanding - -- 日期:2026-06-17 -- 分支:`codex/iter-viral-timeline-landing` -- 本轮目标:X 组产品/设计/开发在 V 的朋友圈系统渠道和 W 的七条 evidence schema 之上,补齐 `source=timeline` 打开详情后的接收语境,让朋友圈来的弱关系用户知道这是附近任务、先看状态和评论、能确认就确认、知道线索就补评论 -- 产品假设:朋友圈是弱关系和低上下文入口,普通 `来自转发` 不足以解释“为什么我看到这条”;把落地页语境从普通转发改为“朋友圈看到的附近任务,先核对再行动”,比继续叠加分享文案更贴近从曝光到 confirm/comment 的转化 -- 已完成:产品 agent 新增 `harness/viral-timeline-landing-product-brief.md`;设计/QA agent 新增 `harness/viral-timeline-landing-checklist.md`;开发 agent 在 `utils/share-receiver.js` 中识别 `source=timeline` -- 页面语义变化:低风险 active 且无 stale/report 信号的 `from=share&source=timeline` 接收引导使用 `kicker: 朋友圈看到`、`title: 附近任务,先核对一下`,summary/rows/note 要求先看状态、确认信号和最新评论,能现场核对就确认,知道更多就评论补线索;不新增 UI 节点、按钮、联系人读取、奖励或诱导分享 -- 风险态变化:`hidden/resolved/expired/reportCount>=2/stale/status=stale` 仍优先原风险/关闭语义;weak stale/report 的 timeline 入口使用 `先核对现场变化` 和 warn tone,不出现鼓励性朋友圈扩散;`source=receiver`、`receiverAction=confirm/comment`、`source=comment`、`source=confirm` 和普通 `from=share` 不被 timeline 分支覆盖 -- 检查变化:`scripts/check-share-receiver.mjs`、`scripts/check-viral-journey-evidence.mjs`、`scripts/check-viral-candidate.mjs` 增加 `source=timeline` 低风险文案、禁用词、弱风险/高风险/closed 优先级和回归断言;`harness/viral-journey-manual-results.example.json` 与 `scripts/prepare-viral-journey-devtools-run.mjs` 把 source=timeline 落地页 receiver context 纳入 `timeline-share-channel` 观察点;`scripts/check-devtools-readiness.mjs` 要求 X 产品/QA 文档存在 -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md`、`harness/feature_list.json`、`git log --oneline -5`;`bash harness/init.sh`;`node --check utils/share-receiver.js`;`node --check scripts/check-devtools-readiness.mjs`;`node --no-warnings scripts/check-share-receiver.mjs`;`node --no-warnings scripts/check-viral-journey-evidence.mjs`;`node --no-warnings scripts/check-viral-candidate.mjs`;`node --no-warnings scripts/check-timeline-share.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`node --no-warnings scripts/check-devtools-readiness.mjs` -- 已记录证据:`node --no-warnings scripts/check-share-receiver.mjs` 输出 `Share receiver checks passed.`;`node --no-warnings scripts/check-viral-journey-evidence.mjs` 输出 `Viral journey evidence checks passed.`;`node --no-warnings scripts/check-viral-candidate.mjs` 输出 `Viral journey evidence checks passed.`、`Timeline share checks passed.` 和 `Viral candidate checks passed.`;`node --no-warnings scripts/check-devtools-readiness.mjs` 输出七条 run package,其中 `timeline-share-channel` 要求观察 `source=timeline` receiver context,并仍报告 DevTools 9420 `connect_refused` / smoke `blocked`,这不是 UI passed;JSON、harness 和 diff 检查均通过 -- 更新过的文件或工件:`utils/share-receiver.js`,`scripts/check-share-receiver.mjs`,`scripts/check-viral-journey-evidence.mjs`,`scripts/check-viral-candidate.mjs`,`scripts/prepare-viral-journey-devtools-run.mjs`,`scripts/check-devtools-readiness.mjs`,`harness/viral-journey-manual-results.example.json`,`harness/viral-timeline-landing-product-brief.md`,`harness/viral-timeline-landing-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:尚未在 WeChat DevTools 或真机中验证真实朋友圈菜单、卡片、payload、单页模式首屏、`source=timeline` 文案窄屏布局、action strip 点击、confirm/comment 后 U/R/S/T 链路和真实转化;当前 DevTools service port 9420 仍是环境 blocker -- 下一步最佳动作:提交 X 组并发送给用户评测 agent 与 W=99 比较,重点看“朋友圈曝光后的落地理解”是否比纯证据基础设施更接近用户侧自发裂变;若继续迭代,优先拿真实 timeline evidence 或做真实转化/埋点准备 - -### Session 075ViralAttributionEvents - -- 日期:2026-06-17 -- 分支:`codex/iter-viral-attribution-events` -- 本轮目标:Y 组产品/设计/开发在 X 的朋友圈落地语境之后补充最小传播归因事件,让后续真实 DevTools/真机跑通时可以观察朋友圈和二跳是否带来 confirm/comment/relay,而不是继续只凭文案假设评估裂变 -- 产品假设:V/W/X 已把朋友圈渠道、证据 schema 和落地语境补齐,但没有可观测的转化事件;记录 `from=share` 打开详情后的 loaded/blocked、confirm/comment 成功和低风险二跳 relay,可帮助后续比较 timeline、receiver、comment、confirm 来源的实际链路,不声称当前转化已经提升 -- 已完成:产品 agent 新增 `harness/viral-attribution-events-product-brief.md`,定义 `share_detail_landing`、`share_detail_loaded`、`share_detail_blocked`、`share_confirm_success`、`share_comment_success`、`share_relay_intent`、`share_relay_success` 及字段白名单;设计/QA agent 新增 `harness/viral-attribution-events-checklist.md`,覆盖隐私红线、CloudBase 不可用不阻断、风险/关闭态边界和真实 payload 手测要求 -- 已完成:开发 agent 新增 `utils/viral-attribution.js`,实现本地 `viral_attribution_events` 存储、CloudBase best-effort 上报、source/channel/action/depth 归一化、字段白名单、粗粒度 distance bucket、低风险 relay guard 和二跳归因参数追加;主线程 review 后补上实际好友分享 path 与朋友圈 query 的 `share_id`、`parent_share_id`、`share_depth`,让下一位打开时能串起 relay 链路 -- 页面变化:`pages/detail/detail.js` 在 `onLoad` 的 `from=share` query 上生成归因 session 并记录 landing;详情加载成功/失败分别记录 loaded/blocked;分享来源用户评论成功或确认成功后记录 `share_comment_success`/`share_confirm_success`,不传评论正文;二跳好友分享返回 path 前同步追加归因 query,朋友圈 re-share 返回 query 前同步追加归因参数;receiverConversion relay 事件会按 `receiverAction` 记录 `conversion_action=confirm/comment`;事件失败不显示 toast、不阻断打开详情、评论、确认或分享 -- 云端变化:`cloudfunctions/posts/index.js` 新增 `recordViralAttribution` action 和 `viral_attribution_events` 集合写入,服务端只接收枚举和短字符串字段,并用 `user_id_hash` 存储不可逆 openid hash;README、PROJECT_SUMMARY 和 AGENTS.md 同步新增集合与隐私边界说明 -- 检查变化:新增 `scripts/check-viral-attribution.mjs` 覆盖字段白名单、敏感字段禁止、source/channel/action/depth 归一化、relay path/query 参数、低风险 relay guard、receiverConversion 事件 action、timeline re-share 记录、页面接入和云函数清洗;`scripts/check-viral-candidate.mjs` 与 `scripts/check-devtools-readiness.mjs` 已接入归因检查和 Y 组文档;`scripts/check-viral-journey-manual-evidence.mjs`、`scripts/check-viral-journey-evidence.mjs`、`scripts/prepare-viral-journey-devtools-run.mjs` 和 `harness/viral-journey-manual-results.example.json` 要求 confirm/comment 二跳 payload 可检查时包含 `share_id/parent_share_id/share_depth` -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md`、`harness/feature_list.json`、`git log --oneline -5`;`bash harness/init.sh`;`node --check utils/viral-attribution.js`;`node --check pages/detail/detail.js`;`node --check cloudfunctions/posts/index.js`;`node --check scripts/check-viral-attribution.mjs`;`node --check scripts/check-viral-journey-manual-evidence.mjs`;`node --check scripts/check-viral-journey-evidence.mjs`;`node --check scripts/prepare-viral-journey-devtools-run.mjs`;`node --check scripts/check-viral-candidate.mjs`;`node --check scripts/check-devtools-readiness.mjs`;`node --no-warnings scripts/check-viral-attribution.mjs`;`node --no-warnings scripts/check-viral-journey-evidence.mjs`;`node --no-warnings scripts/check-viral-journey-manual-evidence.mjs`;`node --no-warnings scripts/prepare-viral-journey-devtools-run.mjs`;`node --no-warnings scripts/check-viral-candidate.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`npm run check`;最终 `bash harness/init.sh` -- 已记录证据:归因检查输出 `Viral attribution checks passed.`;viral journey evidence 输出 `Viral journey evidence checks passed.`;manual evidence gate 输出无本地结果文件且明确不是 UI passed;prepare 输出七条 run package,并在 receiver-confirm/comment journey 中提示可检查 payload 必须含 `share_id`、`parent_share_id`、`share_depth=2/2_plus`;candidate 输出 `Viral journey evidence checks passed.`、`Timeline share checks passed.`、`Viral attribution checks passed.` 和 `Viral candidate checks passed.`;readiness 输出 `Viral attribution checks passed.` 与最终 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`,同时仍报告 DevTools 9420 `connect_refused`/smoke `blocked`,这不是 UI passed;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`git diff --check` 通过无输出;`npm run check` 完整跑通;最终 `bash harness/init.sh` 完整跑通 -- 更新过的文件或工件:`AGENTS.md`,`README.md`,`PROJECT_SUMMARY.md`,`utils/viral-attribution.js`,`pages/detail/detail.js`,`cloudfunctions/posts/index.js`,`scripts/check-viral-attribution.mjs`,`scripts/check-viral-candidate.mjs`,`scripts/check-devtools-readiness.mjs`,`scripts/check-viral-journey-manual-evidence.mjs`,`scripts/check-viral-journey-evidence.mjs`,`scripts/prepare-viral-journey-devtools-run.mjs`,`harness/viral-journey-manual-results.example.json`,`harness/viral-attribution-events-product-brief.md`,`harness/viral-attribution-events-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:尚未在 WeChat DevTools 或真机中验证真实系统分享面板是否保留归因参数、朋友圈/好友分享真实 payload、二跳接收页串联、CloudBase `viral_attribution_events` 集合部署、离线上报本地 fallback、窄屏布局、以及事件与真实用户转化的对应关系;`share_relay_success` 仍是客户端准备好分享 payload 并交给微信分享能力,不代表下一位实际打开 -- 下一步最佳动作:提交 Y 组并发送给用户评测 agent 与 X=99 比较;若继续迭代,优先恢复 DevTools service port 或用真机跑七条 journey,把 timeline 菜单、receiver 二跳 path、归因事件 payload 和风险态 no-shareTimeline 记录成真实 ignored/local evidence - -### Session 076ViralRealEvidenceRecovery - -- 日期:2026-06-17 -- 分支:`codex/iter-viral-real-evidence-recovery` -- 本轮目标:Z 组最小开发增量,把主 agent 已确认的 DevTools CLI quit 自锁与 macOS app-level quit fallback 经验沉淀到恢复工具里,同时避免默认检查产生 GUI 副作用 -- 产品/验证假设:评测只差真实 WeChat DevTools/真机 evidence;继续扩展传播文案收益低,当前更需要一个显式 opt-in 的 app-level quit/reopen 恢复入口,以及一个静态 guard 防止未来把默认 readiness 变成会 quit/open DevTools 的副作用检查 -- 已完成:`scripts/recover-devtools-service-port.mjs` 新增 `--app-quit-reopen`;`--quit-reopen` 与 `--app-quit-reopen` 互斥;默认和 `--dry-run` 仍只做 before/after 诊断并跳过恢复动作;app 模式从 DevTools bundle `Info.plist` 读取 `CFBundleIdentifier`,读不到时回退 `com.tencent.webplusdevtools` 并在 action detail 中报告来源;app 模式只用 macOS `osascript` 退出应用,再用已有 DevTools CLI `open --project ... --port ... --disable-gpu` 重开,不同时执行 CLI quit -- 已完成:新增 `scripts/check-devtools-recovery.mjs`,用 Node assert 静态检查新参数、互斥校验、默认无副作用、osascript app quit、bundle id fallback、mode 文案、next steps 文案和输出 redaction/summarization;`package.json` 新增 `check:devtools-recovery` 与显式 side-effect 命令 `recover:devtools-app-quit`,没有把 app quit 接入 `npm run check` -- 已完成:`scripts/check-devtools-readiness.mjs` 把新静态 guard 纳入 readiness gate,并要求 `harness/viral-real-evidence-recovery-product-brief.md` 与 `harness/viral-real-evidence-recovery-checklist.md` 存在;readiness 输出明确该 guard 不会 quit/reopen DevTools -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check scripts/recover-devtools-service-port.mjs`;`node --check scripts/check-devtools-recovery.mjs`;`node --check scripts/check-devtools-readiness.mjs`;`node scripts/check-devtools-recovery.mjs`;`node scripts/recover-devtools-service-port.mjs --dry-run --app-quit-reopen --project /tmp/street-tasks-iter-worktrees/viral-real-evidence-recovery --port 9420 --timeout-ms 5000`;互斥参数检查;`npm run recover:devtools-app-quit -- --timeout-ms 30000 --port 9420`;`node --no-warnings scripts/check-devtools-readiness.mjs` -- 已记录证据:`pwd` 确认为 `/private/tmp/street-tasks-iter-worktrees/viral-real-evidence-recovery`,对应约定 `/tmp/street-tasks-iter-worktrees/viral-real-evidence-recovery`;最近提交为 `ef8f0a5 feat: add viral attribution events`;`bash harness/init.sh` 完整跑通;三条 `node --check` 均通过;`node scripts/check-devtools-recovery.mjs` 输出 `DevTools recovery static checks passed.`;dry-run app 模式只输出 `DevTools app quit` skipped,未执行副作用;同时传 `--quit-reopen` 和 `--app-quit-reopen` 会报 `Choose only one recovery mode`;实际 app-level recovery 输出 `DevTools app quit: completed` 且 bundle id 来自 `Info.plist CFBundleIdentifier`,但 `DevTools open` timed out,after 状态仍是 `blocked`,service port 9420 仍 `ECONNREFUSED`;`node --no-warnings scripts/check-devtools-readiness.mjs` 输出 `DevTools recovery static checks passed.` 与 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`,并仍报告 DevTools 9420 `connect_refused` / smoke `blocked`,这不是 UI passed -- 更新过的文件或工件:`scripts/recover-devtools-service-port.mjs`,`scripts/check-devtools-recovery.mjs`,`scripts/check-devtools-readiness.mjs`,`package.json`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:主 agent 已实际运行 `recover:devtools-app-quit`,证明 app-level quit 可以完成,但 CLI open 仍因 DevTools service HTTP listener 未就绪而超时;因此仍没有恢复真实 DevTools service port,也没有产生朋友圈菜单、系统分享面板、真实 payload、单页模式首屏、窄屏或真机 passed evidence -- 下一步最佳动作:在 WeChat DevTools UI 中确认 Settings -> Security Settings -> Service Port 已开启且端口为 9420,或改用另一台真机/DevTools 环境;随后重新跑 `npm run inspect:devtools-port`、`npm run check:devtools-smoke`、`npm run prepare:viral-journey-run`,再用 ignored local evidence 文件记录七条真实 journey - -### Session 077DevToolsPortDeepForensics - -- 日期:2026-06-17 -- 分支:`codex/iter-devtools-port-deep-forensics` -- 本轮目标:AA 组不再继续执行恢复动作,而是把 DevTools 9420 blocker 从“open timeout / connect refused”推进到可复核的只读深层取证分叉,尤其判断当前是否缺失真正启动 service listener 所需的 `--enable-service-port` flag -- 产品/QA 假设:Z 组已经证明 app-level quit 可以完成但不能让 service port ready;AA 轮应补齐 user-data、最近日志、历史成功、bundled CLI gate 和隐私红线,让下一轮知道是 Service Port UI/config/global.enableCLI 方向,还是端口占用、权限、profile/session 或工具版本方向 -- 已完成:产品 agent 新增 `harness/devtools-port-deep-forensics-product-brief.md`,定义只读证据层、诊断分叉和不做恢复/不写 UI passed 的产品边界;QA agent 新增 `harness/devtools-port-deep-forensics-checklist.md`,定义 allowed/forbidden evidence、blocked codes、人工 UI 确认模板和真机替代方案边界 -- 已完成:`scripts/inspect-devtools-port-state.mjs` 新增 DevTools user-data root 摘要、WeappLog 尾部只读扫描、最近 `--ide-http-port` 启动行是否带 `--enable-service-port` 的计数、最近/历史 `cli server started at 127.0.0.1:` 信号、bundled CLI source 中 `global.enableCLI` 控制 `--enable-service-port` 的 gate 摘要,以及 `service_port_flag_missing`、`service_port_flag_mixed_recent_evidence`、`service_port_flag_gate_detected`、`historical_service_port_success` 等保守诊断码 -- 隐私边界:inspect 只输出计数、布尔值、版本摘要、通用日志日期标签和错误类别计数;不输出原始日志行、完整 user-data 路径、进程命令行、token/cookie/openid/昵称/项目历史、随机日志文件名或完整 HTTP 内容;脚本仍不 quit/open DevTools、不 kill 进程、不改缓存/配置/文件 -- 已完成:开发 agent 新增 `scripts/check-devtools-port-forensics.mjs`,静态约束 inspect 必须包含 log/user-data 只读入口、`--enable-service-port` 与 `--ide-http-port` 关系检查、`cli server started` 信号、`global.enableCLI` gate、诊断码和 raw log suppression;`package.json` 新增 `check:devtools-port-forensics` -- readiness 变化:`scripts/check-devtools-readiness.mjs` 要求 AA 产品/QA 文档存在,并运行 `scripts/check-devtools-port-forensics.mjs`;输出明确这是 read-only/static no-side-effect guard,不会 quit/open DevTools -- 当前只读诊断结果:`node scripts/inspect-devtools-port-state.mjs --project /tmp/street-tasks-iter-worktrees/devtools-port-deep-forensics --port 9420` 输出 `status: blocked`,诊断为 `declared_without_listener`、`connect_refused`、`service_port_flag_mixed_recent_evidence`、`historical_service_port_success`、`service_port_flag_gate_detected` -- 当前只读证据摘要:DevTools CLI/app bundle 可找到;user-data root 1 个、log dir 1 个;27 个 sanitized log tail 被扫描;最近 12 个日志窗口中有 5 个目标 port 日志文件、1 行带 `--enable-service-port` 的 target launch、4 行不带该 flag 的 target launch、0 条近期 cli server target start/ready;历史上有 1 条 cli server target start 和 1 条 `127.0.0.1:9420` ready 信号;lsof 无 listener,IPv4/IPv6 均 `ECONNREFUSED`,进程扫描仍有 1 个 requested-port declaration -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 与 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check scripts/inspect-devtools-port-state.mjs`;`node --check scripts/check-devtools-port-forensics.mjs`;`node --check scripts/check-devtools-readiness.mjs`;`node scripts/check-devtools-port-forensics.mjs`;`npm run check:devtools-port-forensics`;`node scripts/inspect-devtools-port-state.mjs --project /tmp/street-tasks-iter-worktrees/devtools-port-deep-forensics --port 9420`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check` -- 已记录证据:`node scripts/check-devtools-port-forensics.mjs` 和 `npm run check:devtools-port-forensics` 均输出 `DevTools port forensics static guard checks passed.`;readiness 输出新的 port forensics report、`DevTools port forensics static guard checks passed.` 和最终 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`;JSON 输出 `Checked 11 JSON files.`;harness 输出 `Harness OK: 6 features checked.`;`git diff --check` 无输出 -- 更新过的文件或工件:`scripts/inspect-devtools-port-state.mjs`,`scripts/check-devtools-port-forensics.mjs`,`scripts/check-devtools-readiness.mjs`,`package.json`,`harness/devtools-port-deep-forensics-product-brief.md`,`harness/devtools-port-deep-forensics-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:DevTools service port 9420 仍无 listener,smoke 仍 blocked,系统分享/朋友圈菜单/真实 payload/单页模式/CloudBase attribution/真机 journey 都没有 passed evidence;当前最强假设是 Service Port UI/config 或 `global.enableCLI` 状态没有让 app 带 `--enable-service-port` 启动 listener,而不是端口冲突或项目代码问题 -- 下一步最佳动作:请用户在 WeChat DevTools UI 中人工确认 Settings -> Security Settings -> Service Port 是否开启、显示端口是否为 9420、当前项目是否是本 worktree、是否有登录/升级/安全弹窗;若 UI 已开启但仍无 listener,下一轮应优先换端口/换 profile/换机器或走真机 evidence,不要再把静态 readiness 当作 UI passed - -### Session 078DevToolsServicePortConfigForensics - -- 日期:2026-06-17 -- 分支:`codex/iter-devtools-service-port-config-forensics` -- 本轮目标:AB 组在 AA 的 log/user-data/CLI gate 深层取证之上,继续补一层 DevTools Service Port 配置只读取证,判断 UI/config 是否显示 Service Port disabled、enabled、port mismatch、conflict 或 unconfirmed -- 产品/QA 假设:AA 已把 blocker 从普通 `connect_refused` 推进到 mixed recent flag evidence;AB 应避免继续盲目重启,先回答配置层是否明确指向 Service Port 关闭或端口不匹配,并且该结论不能替代 listener/smoke/UI/real-device evidence -- 已完成:产品 agent 新增 `harness/devtools-service-port-config-forensics-product-brief.md`,定义配置层取证目标、输出语义、诊断码、裂变目标关系和评测口径;QA agent 新增 `harness/devtools-service-port-config-forensics-checklist.md`,定义 allowed/forbidden evidence、只读命令边界、blocked/diagnosis mapping 和 static guard 期望 -- 已完成:`scripts/inspect-devtools-port-state.mjs` 新增 Service Port 配置只读扫描,仅读取 DevTools profile 下 `WeappVendor/cfg.json` 与 `WeappLocalData` 的 JSON 候选文件,输出类别计数、mtime bucket、service-port-like key 摘要、enabled states、port values、诊断码、confidence 和 next human confirmation;不输出完整路径、文件名、raw JSON、localStorage 值、cookie/session、账号、项目历史或 token -- 诊断变化:当前只读配置摘要显示 `service_port_config_disabled`,confidence 为 `medium`;配置状态为 `disabled`,端口状态为 `matches_9420`,`conflict count` 为 0,key 摘要只显示 `security.enable-service-port` 的 bool 为 `false`、`security.port` 的端口值为 `9420`;端口层仍是 `declared_without_listener` 和 `connect_refused`,所以结论仍是 blocked,不是 service port ready -- 已完成:`scripts/check-devtools-port-forensics.mjs` 扩展为 log/user-data + config 双层 static guard,要求配置取证入口、配置候选类别、enabled/mismatch/disabled/unconfirmed/conflict 诊断码、enabledStates/portValues 摘要、raw config content/file name suppression,以及原有 side-effect negative checks;`scripts/check-devtools-readiness.mjs` 要求 AB 产品/QA 文档存在,并继续运行该 no-side-effect static guard -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 与 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check scripts/inspect-devtools-port-state.mjs`;`node --check scripts/check-devtools-port-forensics.mjs`;`node --check scripts/check-devtools-readiness.mjs`;`node scripts/check-devtools-port-forensics.mjs`;`node scripts/inspect-devtools-port-state.mjs --project /tmp/street-tasks-iter-worktrees/devtools-service-port-config-forensics --port 9420`;`node --no-warnings scripts/check-devtools-readiness.mjs` -- 已记录证据:`node scripts/check-devtools-port-forensics.mjs` 输出 `DevTools port forensics static guard checks passed.`;inspect 输出 `status: blocked`,diagnosis 为 `declared_without_listener`、`connect_refused`、`service_port_flag_mixed_recent_evidence`、`historical_service_port_success`、`service_port_config_disabled`、`service_port_flag_gate_detected`,并显示 config state `disabled`、port state `matches_9420`、conflict count `0`、not claimed `DevTools smoke passed`/`DevTools UI journey passed`/`real-device journey passed`,且 raw config content 和 config file names 未打印;readiness 输出新的配置层报告,仍明确 `Static gates passed; DevTools and real-device visual acceptance are still required.` -- 代码审查修复:AB code review 指出端口多源冲突会先落到 `service_port_config_enabled_port_match`、local-data key 缺少敏感 denylist/port allowlist、side-effect guard 漏掉部分写入 API/危险命令;已修复为端口冲突优先返回 `service_port_config_conflict`,敏感 key 在匹配和记录前跳过,端口值只从明确 service/ide/http/cli/security port key 提取,并加严 static guard 覆盖敏感 key、冲突分支顺序、未登记诊断码和更多副作用 API/命令 -- 更新过的文件或工件:`scripts/inspect-devtools-port-state.mjs`,`scripts/check-devtools-port-forensics.mjs`,`scripts/check-devtools-readiness.mjs`,`harness/devtools-service-port-config-forensics-product-brief.md`,`harness/devtools-service-port-config-forensics-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:配置取证只说明当前本机配置摘要指向 Service Port disabled;它不自动开启开关、不证明 listener ready、不证明 DevTools UI、系统分享、朋友圈、payload、CloudBase readback 或真机 journey 通过;如果用户在 UI 中开启后配置和日志会改变,需要重新跑 `npm run inspect:devtools-port` 与 `npm run check:devtools-smoke` -- 下一步最佳动作:请用户在 WeChat DevTools UI 中打开 Settings -> Security Settings -> Service Port,并确认端口显示为 9420;开启后重新跑只读 inspect/smoke/prepare,若仍无 listener,再转向换端口、换 profile、换机器或真机 evidence 路线 - -### Session 079DevToolsServicePortUiConfirmation - -- 日期:2026-06-17 -- 分支:`codex/iter-devtools-service-port-ui-confirmation` -- 本轮目标:AC 组在 AB 的只读配置取证之后,新增“用户手动开启 Service Port 后复测准备”入口;该入口只能接收用户 UI 确认摘要并复跑只读 inspect/smoke/viral journey preparation,输出严格 blocked/ready 语义,不声称传播 journey passed -- 产品/QA 假设:Service Port 是否开启必须由用户在 WeChat DevTools UI 中人工确认;agent 只能在用户返回 `enabled/disabled/not_found/unavailable/not_confirmed` 与端口状态后只读复核。端口或 smoke ready 只表示可以开始真实手测,不等于 DevTools UI、真机或 viral journey passed -- 已完成:新增 `scripts/prepare-devtools-ui-confirmation-run.mjs`,支持 `--project`、`--port`、`--ui-service-port-state enabled|disabled|not_found|unavailable|not_confirmed`、`--ui-port-state matches_9420|mismatch|unconfirmed` 和 `--strict`;脚本默认不写文件、不自动化 UI、不 quit/open/preview/upload/kill/cache/settings 修改,只运行现有只读 inspect、smoke 和 viral journey preparation,并只输出命令标签、退出码、status、诊断、config/port/conflict、listener/smoke/prepare 摘要、nextStep 和 notClaimed -- 已完成:新增 `scripts/check-devtools-ui-confirmation.mjs` static guard,覆盖 AC 参数、允许状态词汇、disabled/unconfirmed/mismatch/listener/smoke/ready 映射、blocked nextStep、notClaimed、敏感输出抑制、子命令 stdout/stderr 不直出、side-effect API/命令禁用、strict 非 ready 非零退出,以及 “port/smoke ready is not viral journey passed” 文案 -- readiness 变化:`scripts/check-devtools-readiness.mjs` 要求 AC 产品/QA 文档存在,并只运行 `scripts/check-devtools-ui-confirmation.mjs` static guard;readiness 不运行需要用户 UI 状态参数的 AC prepare 脚本 -- npm 入口:`package.json` 新增 `prepare:devtools-ui-confirmation` 和 `check:devtools-ui-confirmation` -- 当前只读复核结果:未确认 UI 输出 `pre_manual_confirmation`;UI 仍关闭输出 `blocked_config_disabled`;传入 `enabled + matches_9420` 时,当前环境仍输出 `blocked_no_listener`,诊断包含 `service_port_not_listening_after_manual_confirmation`、`connect_refused`、`service_port_config_disabled` 和 `smoke_access_blocked`;strict 模式在 blocked 状态下返回非零;所有 AC 输出都包含 `notClaimed` 和 `port/smoke ready is not viral journey passed` -- 代码审查修复:review 指出 readiness 继承旧准备脚本 stdout 会暴露当前 worktree 路径、checklist 的 `notClaimed` 示例容易被复制成 passed 语义、`ready_for_manual_journey` 文档漏写 UI 端口匹配和 strict 子命令成功条件;已改为 readiness 捕获并脱敏该准备命令输出、checklist 使用 exact `notClaimed: no DevTools UI journey passed; no real-device journey passed; no viral journey passed`,并要求 `uiPortState=matches_9420` 与 `strictSubcommandNonzero=no` -- 运行过的验证:`pwd`;读取 AC product brief/checklist、`harness/claude-progress.md`、`harness/feature_list.json`、`git log --oneline -5`;`bash harness/init.sh`;`node --check scripts/prepare-devtools-ui-confirmation-run.mjs`;`node --check scripts/check-devtools-ui-confirmation.mjs`;`node --check scripts/check-devtools-readiness.mjs`;`node scripts/check-devtools-ui-confirmation.mjs`;`node scripts/prepare-devtools-ui-confirmation-run.mjs --project --port 9420 --ui-service-port-state not_confirmed --ui-port-state unconfirmed`;`node scripts/prepare-devtools-ui-confirmation-run.mjs --project --port 9420 --ui-service-port-state disabled --ui-port-state matches_9420`;`node scripts/prepare-devtools-ui-confirmation-run.mjs --project --port 9420 --ui-service-port-state enabled --ui-port-state matches_9420`;strict enabled+matches blocked check;`node --no-warnings scripts/check-devtools-readiness.mjs`;readiness 输出路径扫描;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`npm run check`;`bash harness/init.sh`;`git diff --check` -- 已记录证据:static guard 输出 `DevTools UI confirmation static guard checks passed.`;not_confirmed 输出 `status: pre_manual_confirmation`;disabled 输出 `status: blocked_config_disabled`;enabled+matches_9420 输出 `status: blocked_no_listener`、`listenerState: no_listener`、`smokeState: blocked`、`viralJourneyPreparation: blocked`、`notClaimed: no DevTools UI journey passed; no real-device journey passed; no viral journey passed`;strict enabled+matches_9420 blocked check 退出码为 1 且输出 `strictSubcommandNonzero: yes`;readiness 输出 `Running DevTools UI confirmation static guard... does not run the UI-state-dependent prepare script` 与最终 `DevTools readiness checks passed...`; JSON 输出 `Checked 11 JSON files.`;harness 输出 `Harness OK: 6 features checked.`;`git diff --check` 无输出 -- 更新过的文件或工件:`scripts/prepare-devtools-ui-confirmation-run.mjs`,`scripts/check-devtools-ui-confirmation.mjs`,`scripts/check-devtools-readiness.mjs`,`package.json`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:AC 入口只证明人工 UI 确认后的端口/smoke/手测准备状态;当前机器仍无 9420 listener,smoke blocked,配置摘要仍显示 disabled;真实朋友圈菜单、系统分享面板、payload、单页模式、CloudBase attribution、窄屏和真机 journey 仍未产生 passed evidence -- 下一步最佳动作:用户在 WeChat DevTools UI 中人工确认 Service Port 已开启且端口为 9420 后,运行 `npm run prepare:devtools-ui-confirmation -- --project --port 9420 --ui-service-port-state enabled --ui-port-state matches_9420`;若仍 blocked_no_listener 或 blocked_smoke_access,继续检查 IDE 实例、端口/profile 或换环境/真机 evidence - -### Session 080ViralManualJourneyEvidencePacket - -- 日期:2026-06-17 -- 分支:`codex/iter-viral-manual-journey-evidence-packet` -- 本轮目标:AD 组开发只读 “viral journey evidence packet” 入口,承接 AC 的 `ready_for_manual_journey` 状态;在未 ready 时明确 blocked/not_ready,在 ready 时只给出七条真实手测 journey 的执行包,不声明任何 DevTools UI、真机或 viral journey passed -- 产品/验证假设:AC 已把 Service Port UI 确认复核和端口/smoke blocker 收敛成可机器读取的状态;AD 入口只应消费 AC 状态并整理后续真实手测 evidence 采集清单,不能绕过 AC、不能写本地 evidence、不能自动化 UI,也不能把 readiness/static guard/packet ready 写成 passed evidence -- 已完成:新增 `scripts/prepare-viral-journey-evidence-packet.mjs`,支持 `--project`、`--port`、`--ui-service-port-state enabled|disabled|not_found|unavailable|not_confirmed`、`--ui-port-state matches_9420|mismatch|unconfirmed` 和 `--strict`;脚本只运行 `scripts/prepare-devtools-ui-confirmation-run.mjs` 子命令并解析 AC `status`、`nextStep` 和 `notClaimed`,不打印 raw stdout/stderr、本机路径、raw config/log 或 token/cookie/session/openid -- 已完成:当 AC 不是 `ready_for_manual_journey` 时,packet 输出 `status: not_ready_for_manual_journey`、`packetStatus: not_ready`、AC status、AC nextStep、下一步 blocker 文案和 exact `notClaimed: no DevTools UI journey passed; no real-device journey passed; no viral journey passed`;strict blocked 返回 1;blocked 输出不展开七条 journey evidence -- 已完成:当 AC ready 时,packet 输出 `status: ready_to_execute_manual_journey`、`packetStatus: ready_to_execute`,从 `harness/viral-journey-manual-results.example.json` 结构化读取七条 journey,并打印每条的 id/title/route/key evidence fields/payload checks;ready 输出仍包含 exact notClaimed,并提示回填 ignored local JSON 后运行 `node --no-warnings scripts/check-viral-journey-manual-evidence.mjs ` -- 已完成:新增 `scripts/check-viral-journey-evidence-packet.mjs` static guard,覆盖参数/状态词汇、AC ready gating、七条 journey coverage、notClaimed、防 passed 误导、no side effects、raw output 抑制、readiness 只跑 static guard、package scripts;`package.json` 新增 `prepare:viral-journey-evidence-packet` 和 `check:viral-journey-evidence-packet` -- readiness 变化:`scripts/check-devtools-readiness.mjs` 要求 AD packet prepare/static guard 和并行 agent 创建的 `harness/viral-manual-journey-evidence-packet-product-brief.md`、`harness/viral-manual-journey-evidence-packet-checklist.md` 存在,并只运行 `scripts/check-viral-journey-evidence-packet.mjs`;readiness 明确不运行需要 UI 状态参数的 packet prepare 命令 -- 并行协作边界:本轮确认两份 AD brief/checklist 已由其他 agents 创建,开发 agent 未修改它们;`git status` 中它们仍为未跟踪并行产物,后续由对应 agents/主流程处理 -- 运行过的验证:`pwd`;`git status --short --branch`;`git log --oneline -5`;读取 `harness/claude-progress.md`、`harness/feature_list.json`、`AGENTS.md`、AC prepare、readiness、package、manual journey example 和相关检查脚本;`bash harness/init.sh`;`node --check scripts/prepare-viral-journey-evidence-packet.mjs`;`node --check scripts/check-viral-journey-evidence-packet.mjs`;`node --check scripts/check-devtools-readiness.mjs`;`node scripts/check-viral-journey-evidence-packet.mjs`;blocked packet 输出检查;strict blocked exit 1 检查;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;最终 `bash harness/init.sh` -- 已记录证据:三条 `node --check` 均通过;`node scripts/check-viral-journey-evidence-packet.mjs` 输出 `Viral manual journey evidence packet static guard checks passed.`;disabled UI blocked packet 输出 `status: not_ready_for_manual_journey`、`packetStatus: not_ready`、`acStatus: blocked_config_disabled`、exact `notClaimed: no DevTools UI journey passed; no real-device journey passed; no viral journey passed`,并声明 raw child stdout/stderr、本机路径、raw config/logs 和敏感值被抑制;strict disabled blocked 检查输出 `exit=1`;readiness 输出 `Running viral manual journey evidence packet static guard. This does not run scripts/prepare-viral-journey-evidence-packet.mjs because that prepare script needs UI-state parameters.` 和最终 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`;JSON 输出 `Checked 11 JSON files.`;harness 输出 `Harness OK: 6 features checked.`;`git diff --check` 无输出;最终 `bash harness/init.sh` 完整跑通 -- 更新过的文件或工件:`scripts/prepare-viral-journey-evidence-packet.mjs`,`scripts/check-viral-journey-evidence-packet.mjs`,`scripts/check-devtools-readiness.mjs`,`package.json`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:当前本机 AC 状态仍可因 Service Port disabled/no listener/smoke blocked 而无法进入 packet ready;本轮没有自动开启 DevTools、没有写 ignored local evidence、没有执行真实系统分享菜单、朋友圈、payload、单页模式、CloudBase attribution、窄屏或真机 journey,因此仍没有 DevTools UI、real-device 或 viral journey passed evidence -- 下一步最佳动作:用户先在 WeChat DevTools UI 中确认 Service Port enabled 且端口匹配 9420;随后运行 `npm run prepare:viral-journey-evidence-packet -- --project --port 9420 --ui-service-port-state enabled --ui-port-state matches_9420 --strict`。只有 packet ready 后,才进入真实 DevTools/真机七条 journey,并把观察写入 ignored local JSON 再跑 `node --no-warnings scripts/check-viral-journey-manual-evidence.mjs ` - -### Session 081ViralManualSummaryIntegrity - -- 日期:2026-06-17 -- 分支:`codex/iter-viral-manual-summary-integrity` -- 本轮目标:AE 组开发只读 viral manual journey JSON 与 Markdown summary 同源完整性 guard,避免评审引用过期、串错或被手改坏的 local summary -- 产品/QA 输入:保留并基于并行产品/QA agent 新增的 `harness/viral-manual-summary-integrity-product-brief.md` 与 `harness/viral-manual-summary-integrity-checklist.md`;本轮不把它们改写成 UI passed 证据 -- 已完成:新增 `scripts/check-viral-manual-summary-integrity.mjs`,CLI 为 `--results --summary `;只接受 repo 内且 git ignored 的 `harness/manual-test-results.local-viral-journey*.json` 与 `harness/manual-test-summary.local-viral-journey*.md`;先运行 `scripts/check-viral-journey-manual-evidence.mjs`,再解析 Run/Summary/Journeys 表,比对 branch、commit、overallStatus,以及七条 required journey 的 id/title/status/actual/evidenceCount/blocker/followUp -- 已完成:guard 对 Markdown 全文扫描 raw evidence path/url/value/details、`cloud://`、本机路径、URL、token/cookie/session/authorization/bearer/password/private key/access/refresh token、手机号、openid/unionid 等敏感或原始证据内容;命中时只报类别和行号,不回显原文;同时拒绝 `passed by AE`、`AE passed`、`summary passed`、`readiness passed so UI passed`、`manual journey passed by checklist`、`DevTools UI passed by summary` 等误导语 -- 已完成:新增 `scripts/check-viral-manual-summary-integrity-preflight.mjs`,只扫描 `harness/manual-test-summary.local-viral-journey*.md` 并按同后缀寻找 `harness/manual-test-results.local-viral-journey*.json`;没有 summary 时输出 nothing checked 和 not UI passed evidence;summary 存在但结果 JSON 缺失或任一 pair guard 失败时返回失败 -- 已完成:`package.json` 新增 `check:viral-manual-summary-integrity`;`scripts/check-devtools-readiness.mjs` 纳入两支新脚本和两份 AE 文档,并运行 viral summary integrity preflight,文案明确只检查 ignored local JSON/summary pair,不证明 UI/真机/viral journey passed -- 已完成:`scripts/create-manual-summary.mjs` 对 viral local result 路径改走 viral manual evidence checker,保持其他 manual result 仍走原 generic checker;`scripts/check-map-list-blocked-summary-preflight.mjs` 排除 `local-viral-journey*` summary,避免 map-list blocked guard 误扫 viral summary -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md`、`harness/feature_list.json`、`git log --oneline -5`、AE product brief/checklist 和相关脚本;`bash harness/init.sh`;`node --check scripts/check-viral-manual-summary-integrity.mjs`;`node --check scripts/check-viral-manual-summary-integrity-preflight.mjs`;`node scripts/check-viral-manual-summary-integrity-preflight.mjs`;生成 ignored local blocked draft `harness/manual-test-results.local-viral-journey-ae-integrity.json`;用 `scripts/create-manual-summary.mjs` 生成 `harness/manual-test-summary.local-viral-journey-ae-integrity.md`;主 guard 正向检查;preflight 正向检查;篡改 branch 的临时 bad summary 负向检查;`npm run check:viral-manual-summary-integrity`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`npm run check`;最终 `bash harness/init.sh` -- 已记录证据:空 preflight 输出 `No local viral journey summary files found; nothing checked.` 和 `Preflight is not UI passed evidence...`;prepare 输出 `Checked viral journey manual evidence: harness/manual-test-results.local-viral-journey-ae-integrity.json (overallStatus=blocked)` 且说明不是 UI passed;summary generator 输出 `Manual summary draft created.`;主 guard 正向输出 `Viral manual journey summary integrity checks passed.` 以及 not-UI-passed/real-device/viral-journey disclaimer;preflight 正向输出检查 1 pair;负向 bad branch summary 失败并提示 `Summary Markdown Run branch does not match the source results JSON on line 7.`;readiness 输出 viral summary integrity preflight 通过、map-list blocked preflight 不再误扫 viral summary,并最终输出 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.` -- 更新过的文件或工件:`scripts/check-viral-manual-summary-integrity.mjs`,`scripts/check-viral-manual-summary-integrity-preflight.mjs`,`scripts/create-manual-summary.mjs`,`scripts/check-map-list-blocked-summary-preflight.mjs`,`scripts/check-devtools-readiness.mjs`,`package.json`,`harness/viral-manual-summary-integrity-product-brief.md`,`harness/viral-manual-summary-integrity-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:保留的 `harness/manual-test-results.local-viral-journey-ae-integrity.json` 和 `harness/manual-test-summary.local-viral-journey-ae-integrity.md` 是 ignored local blocked draft/summary,用于 guard 验证和本地 preflight;不提交、不 staging,也不代表真实 DevTools UI、系统分享面板、朋友圈、payload、CloudBase、窄屏或真机 journey passed -- 下一步最佳动作:在真实 WeChat DevTools 或真机完成七条 viral manual journey 后,把观察写入 ignored `harness/manual-test-results.local-viral-journey*.json`,用 `scripts/create-manual-summary.mjs` 生成 matching summary,并在评审引用前运行 `npm run check:viral-manual-summary-integrity` 或 `node scripts/check-viral-manual-summary-integrity-preflight.mjs` - -### Session 082ViralManualArtifactManifest - -- 日期:2026-06-17 -- 分支:`codex/iter-viral-manual-artifact-manifest` -- 本轮目标:AF 组开发只读 viral manual artifact manifest guard,承接 AE 的 JSON/Markdown summary integrity,避免评审引用截图、录屏、payload/readback 附件时出现串错 journey、泄露 raw 值或把附件清单误写成 UI/真机通过 -- 产品/QA 输入:产品 agent 新增 `harness/viral-manual-artifact-manifest-product-brief.md`,定义 artifact manifest 是“附件安全引用清单”;QA agent 新增 `harness/viral-manual-artifact-manifest-checklist.md`,定义七条 journey 的 screenshot、recording、payload-sample、cloud-readback、device-observation、risk-state-note 必收/条件必收/可选规则和脱敏要求 -- 已完成:新增 `scripts/check-viral-manual-artifact-manifest.mjs`,CLI 为 `--results --manifest `;只接受 repo 内且 git ignored 的 `harness/manual-test-results.local-viral-journey*.json` 与 `harness/manual-artifact-manifest.local-viral-journey*.json`;先运行 viral manual evidence gate,再比对 branch、commit、overallStatus、七条 journey status、sourceEvidenceCount 和 required artifact slots -- 已完成:guard 要求 manifest 使用 schema `viral-manual-artifact-manifest.v1`、exact 七条 journey、固定 required/conditional slots、`artifact::` 脱敏引用、payload/cloud readback 只记录字段名、不记录原始值;同时拒绝 raw path/url/query/payload、`cloud://`、本机路径、token/cookie/session、openid/unionid、精确经纬度、手机号邮箱、敏感 key 和 `DevTools UI passed by manifest` 等误导语 -- 代码审查修复:独立 review 指出 raw manifest 只在 JSON.parse 后扫描会漏掉 duplicate key 覆盖的敏感文本、slot map 与 QA 清单不一致、坐标/CloudBase env/身份 key 漏挡、timeline payload 和 cloud-readback 字段过弱、blocked/failed source blocker/followUp 未对齐;已修复为先扫描 raw manifest file text、拒绝 duplicate JSON keys、按 required/conditional/optional slot 校验、补 coordinate/env/identity banned keys、timeline payload 要求 `title` 和 `imageUrl` 字段名、receiver cloud-readback 要求 action/source/depth/count 字段名,并要求 `sourceBlocker/sourceFollowUp` 或 `blocker/followUp` 匹配 source results -- 二次代码审查修复:复审指出 source journey 为 passed 时 conditional slot 仍可写 blocked、`fieldsObserved` 中仍可填 coordinate/env/identity/credential 字段名;已改为 passed source 的 conditional slot 只能 `present` 或 `not_applicable`,并对 `fieldsObserved` 增加敏感字段名 denylist,同时保留 `title`、`imageUrl` 等允许的字段存在性记录 -- 三次代码审查修复:终审指出 `not_applicable` 仍可能绕过已触发的 conditional evidence、snake_case 身份/env aliases 漏挡;已从 source results 和 environment 推导 conditional slot 是否触发,CloudBase enabled/deployed 时 `cloud-readback` 必须 present,source 中已有可检查 payload 时 `payload-sample` 必须 present,并把 `avatar_url`、`publisher_avatar_url`、`env_id`、`nick_name` 等 canonical/snake_case 字段名纳入 object key 与 `fieldsObserved` denylist -- 四次代码审查修复:最终复核指出 raw/cloud/file/event 的 snake_case/canonical aliases 仍可绕过 object key 和 `fieldsObserved` denylist;已把 `rawpayload`、`rawquery`、`clouduri`、`cloudfileid`、`fileid`、`eventid` 等 canonical forms 纳入统一敏感字段集合,并用临时 ignored fixture 验证 `raw_payload` object key 与 `cloud_file_id` fieldsObserved 均按预期失败;复审返回 `No Critical/Important findings` 和 `Ready yes` -- 已完成:新增 `scripts/check-viral-manual-artifact-manifest-preflight.mjs`,扫描 ignored `harness/manual-artifact-manifest.local-viral-journey*.json` 并按同后缀寻找对应 viral results JSON;没有 manifest 时输出 nothing checked 和 not UI passed evidence;manifest 存在但结果缺失或任一 pair guard 失败时返回失败 -- 已完成:`package.json` 新增 `check:viral-manual-artifact-manifest`;`scripts/check-devtools-readiness.mjs` 纳入两支新脚本和两份 AF 文档,并运行 artifact manifest preflight;`.gitignore` 新增 `harness/manual-artifact-manifest.local*.json`,确保附件清单默认留在 ignored local evidence 区 -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md`、`harness/feature_list.json`、`git log --oneline -5` 和相关 AE/AD 脚本;`bash harness/init.sh`;`node --check scripts/check-viral-manual-artifact-manifest.mjs`;`node --check scripts/check-viral-manual-artifact-manifest-preflight.mjs`;`node --check scripts/check-devtools-readiness.mjs`;生成 ignored local blocked fixture `harness/manual-test-results.local-viral-journey-af-artifacts.json` 和 `harness/manual-artifact-manifest.local-viral-journey-af-artifacts.json`;主 guard 正向检查;preflight 正向检查;`npm run check:viral-manual-artifact-manifest`;duplicate-key raw path、缺 risk-state-note、sensitive latitude key、source blocker mismatch、timeline payload 缺 title/imageUrl、weak receiver cloud readback fields、raw camelCase payload、sensitive fieldsObserved latitude/env_id、snake_case identity key、passed source journey conditional slot blocked、triggered CloudBase readback not_applicable、snake_case raw_payload object key、snake_case cloud_file_id fieldsObserved 十四组负向检查;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`npm run check`;最终 `bash harness/init.sh` -- 已记录证据:主 guard 正向输出 `Viral manual artifact manifest checks passed.` 以及 not-UI-passed/real-device disclaimer;preflight 正向检查 1 pair;npm script 正向通过;十四组负向均按预期失败并在复原后主 guard 再次通过;最终 ignored local fixture 已清理,不提交;最终 `npm run check` 和 `bash harness/init.sh` 均返回 0,但 readiness 仍报告 DevTools service port 9420 `connect_refused` / `service_port_config_disabled`,不是 UI passed -- 更新过的文件或工件:`.gitignore`,`scripts/check-viral-manual-artifact-manifest.mjs`,`scripts/check-viral-manual-artifact-manifest-preflight.mjs`,`scripts/check-devtools-readiness.mjs`,`package.json`,`harness/viral-manual-artifact-manifest-product-brief.md`,`harness/viral-manual-artifact-manifest-checklist.md`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:AF 只验证附件 manifest 的字段、脱敏、journey 对齐和 notClaimed 口径;它不执行 DevTools/真机,不查看真实截图/录屏,不 inspect 系统分享或朋友圈 payload,不读 CloudBase 真实记录,也不证明 viral journey passed -- 下一步最佳动作:真实 DevTools/真机完成七条 journey 后,先让 ignored results JSON 通过 `node --no-warnings scripts/check-viral-journey-manual-evidence.mjs `,再生成 matching summary 并运行 `npm run check:viral-manual-summary-integrity`,最后填写 `harness/manual-artifact-manifest.local-viral-journey*.json` 并运行 `npm run check:viral-manual-artifact-manifest` - -### Session 083MapFocusLaunch - -- 日期:2026-06-22 -- 分支:`codex/iter-viral-manual-artifact-manifest` -- 本轮目标:回应用户希望从分享详情切回首页,并在首页定位到“地铁口捡到蓝色门禁卡”的体验诉求 -- 已完成:`pages/map/map.js` 支持启动参数 `focusPostId`、`postId` 或 `id`,并支持 `showList=1`;首页加载本地帖子后会一次性把地图中心移动到目标任务、设置选中态,并在列表打开时高亮该任务;本地 ignored 的 `project.private.config.json` 已从分享详情启动模式改为 `pages/map/map?focusPostId=post_001&showList=1` -- 运行过的验证:`bash harness/init.sh`;`node --check pages/map/map.js`;`node scripts/check-json.mjs`;单独解析 `project.private.config.json` -- 已记录证据:`bash harness/init.sh` 通过,输出 JSON 和 harness 检查成功;`node --check pages/map/map.js` 无语法错误;`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`project.private.config.json` 单独 JSON.parse 通过 -- 更新过的文件或工件:`pages/map/map.js`,`harness/feature_list.json`,`harness/claude-progress.md`;本地 ignored 配置 `project.private.config.json` -- 已知风险或未解决问题:当前 Computer Use 一度无法重新抓取微信开发者工具窗口,尚未能用模拟器截图证明首页定位视觉效果;但该路径只依赖首页本地帖子列表和已有选中态逻辑,仍需用户在 DevTools 点“编译”或重开项目后目视确认 -- 下一步最佳动作:在 WeChat DevTools 里选择“首页定位:蓝色门禁卡”编译模式并点击“编译”;期望页面路径为 `pages/map/map`,列表展开且“地铁口捡到蓝色门禁卡”卡片高亮,点击右侧 `›` 可进入普通详情页 - -### Session 084ShareReceiverFocusedHomeCta - -- 日期:2026-06-22 -- 分支:`codex/iter-viral-manual-artifact-manifest` -- 本轮目标:修复用户点击分享页左上角小房子返回首页时不够明显且无法定位当前任务的问题,并探索更清晰的回首页查询入口 -- 根因:左上角小房子是微信原生入口,返回 root `pages/map/map` 时不会携带 `focusPostId`,同时 DevTools 仍会显示既有原生 `WAServiceMainContext timeout`;页面可渲染,但不能满足“回首页查询这条”的产品意图 -- 已完成:在分享接收引导卡里新增“想先看它在哪?”区块和 `回首页查这条` 按钮;按钮调用 `goHomeWithPost`,通过 `wx.reLaunch` 打开 `/pages/map/map?focusPostId=<当前任务>&showList=1`,失败时回退普通首页 -- 运行过的验证:`bash harness/init.sh`;先让 `node scripts/check-share-receiver-action.mjs` 因缺少 `goHomeWithPost` 按钮失败;补实现后同一检查通过;`node --check pages/detail/detail.js`;`node --check pages/map/map.js`;`node scripts/check-json.mjs`;`git diff --check`;`node --no-warnings scripts/check-devtools-readiness.mjs`;WeChat DevTools 真实点击 `回首页查这条` -- 已记录证据:新静态检查覆盖 `bindtap="goHomeWithPost"`、按钮文案、`focusedMapUrl(postId)`、`wx.reLaunch` focused map URL 和样式类;DevTools 中分享页显示新按钮,点击后进入 `pages/map/map`,信息列表展开且可见/定位“地铁口捡到蓝色门禁卡” -- 更新过的文件或工件:`pages/detail/detail.js`,`pages/detail/detail.wxml`,`pages/detail/detail.wxss`,`scripts/check-share-receiver-action.mjs`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:左上角微信原生小房子仍不可自定义,也不会带业务 query;本轮用更明显的业务 CTA 绕开该入口。DevTools 控制台仍有既有 `WAServiceMainContext timeout`,但本轮观察新按钮跳转和首页定位成功 -- 下一步最佳动作:继续在真机验证分享页新 CTA 的可见性、点击反馈和首页定位行为;必要时再考虑隐藏原生导航或改 custom navigation,但那会扩大改动范围 - -### Session 085FocusedHomeDiagnosticsClear - -- 日期:2026-06-22 -- 分支:`codex/iter-viral-manual-artifact-manifest` -- 本轮目标:修复用户从分享页点击 `回首页查这条` 后,首页列表已打开但“地图启动中”诊断面板仍盖在地图上的误报式体验 -- 根因:`pages/map/map.js` 的 focused launch 成功路径会打开 `showList=1` 并定位目标任务,但仍调用延迟 700ms 的 `hideDiagnosticsSoon()`;同时 `pages/map/map.wxml` 允许 `diagnosticVisible` 面板在列表打开时继续渲染,所以用户会看到一个像错误的历史启动诊断浮层 -- 已完成:focused launch 成功后改为立即 `hideDiagnostics()`;诊断面板渲染条件改为 `diagnosticVisible && !showList`,确保任务列表已打开时不会遮挡地图;`harness/check-map-feed.mjs` 新增静态回归断言覆盖这两点 -- 运行过的验证:`bash harness/init.sh`;先让 `node harness/check-map-feed.mjs` 因诊断面板仍覆盖列表失败;补实现后 `node harness/check-map-feed.mjs` 通过;`node --check pages/map/map.js`;`node --check harness/check-map-feed.mjs`;`node scripts/check-map-list-resilience.mjs`;`node scripts/check-share-receiver-action.mjs`;WeChat DevTools 中从分享页点击 `回首页查这条` -- 已记录证据:DevTools 手动点击后进入 `pages/map/map`,信息列表展开,`地铁口捡到蓝色门禁卡` 卡片可见且无“地图启动中”黑色诊断浮层遮挡;控制台仍保留既有 DevTools `WAServiceMainContext timeout`,但页面可用且视觉遮挡已消失 -- 更新过的文件或工件:`pages/map/map.js`,`pages/map/map.wxml`,`harness/check-map-feed.mjs`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:本轮只收敛“成功回首页后诊断浮层误遮挡”的问题,没有处理 DevTools 基础库偶发 `WAServiceMainContext timeout` 控制台日志;该日志此前已记录为 DevTools/native 层既有现象 -- 下一步最佳动作:继续真机验证 `回首页查这条` 后列表打开、目标任务定位和无诊断遮挡;若真机仍有 native timeout 日志,再单独按 DevTools/基础库兼容问题排查 - -### Session 086FiveRoundStabilityReview - -- 日期:2026-06-22 -- 工作区:`/Users/bytedance/.codex/worktrees/0422/x`;AGENTS.md 里的历史根路径是 `/Users/bytedance/git/x`,本轮按当前 Codex worktree 执行并记录 -- 本轮目标:按用户要求建立小程序开发、JS、架构三个子 agent 方向,连续至少 5 轮检查并优化性能/稳定性;每轮实现后启动同身份 reviewer,至少 2 票 PASS 才进入下一轮 -- 已完成第 1 轮:admin 身份刷新稳定性。`utils/auth.js` 的 `refreshAdminRole` 现在在无 cloud runtime、getMyRole 失败、admins 集合缺失、非 admin 或 malformed 响应时保留 guest 状态并清除 stale admin;只有 `ok === true && role === 'admin'` 才升级管理员。`scripts/check-admin-auth-errors.mjs` 扩展了缺 cloud、调用失败、集合缺失、非管理员、legacy/malformed admin、stale admin 降级和严格 admin 成功用例。复审结果:Pauli PASS、Mill PASS、Copernicus PASS -- 已完成第 2 轮:详情页信任动作与关闭动作防重复/失败恢复。`pages/detail/detail.js`/`.wxml` 新增 `busyAction` 和 `resolving`,确认/过时/举报/关闭有 loading/disabled、try/catch/finally 和失败 toast;`utils/store.js` 在本地 fallback 中围绕异步 post lookup 做重复 reaction recheck;新增 `scripts/check-detail-action-guards.mjs` 并接入 readiness。复审结果:Einstein PASS、Schrodinger PASS、Gauss PASS -- 已完成第 3 轮:本地关闭权限边界。`utils/store.js` 的 local `resolvePost` 只允许当前发布者或管理员关闭,抛出 `FORBIDDEN`;详情页 `canResolve` 不再信任陈旧展示态 `isMine`;新增 `scripts/check-store-permission-guards.mjs` 覆盖非 owner 拒绝、owner 成功和 admin 成功并接入 readiness。复审结果:Jason PASS、Heisenberg PASS;Lagrange 已关闭,满足两票通过 -- 已完成第 4 轮:地图页生命周期/异步竞态。`pages/map/map.js` 新增 map active 标记和 posts/location/discovery request generation,onLoad 后第一次 onShow 跳过重复 refresh,hide/unload 统一 deactivate 并清 timer;refresh、location、focused launch callback、diagnostics timer 和 discoverNearby 都会丢弃 inactive/stale 回调;`harness/check-map-feed.mjs` 增加对应静态 guard。复审结果:Parfit PASS、Lorentz PASS、Plato PASS -- 已完成第 5 轮:云端评论 newest-first 稳定性。`cloudfunctions/posts/index.js` 的 `listComments` 现在在 `.limit(MAX_COMMENTS_PER_POST)` 前调用 `.orderBy('createdAt', 'desc')`,避免云端集合超过 50 条时先截断再 JS 排序;新增 `scripts/check-cloud-comment-order.mjs` 断言 `where -> orderBy -> limit -> get` 和 defensive sort,并接入 readiness。复审结果:Sagan PASS、Rawls PASS、Dewey PASS -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 与 `harness/feature_list.json`;`git log --oneline -5`;带 bundled Node PATH 的 `bash harness/init.sh`;各轮专项 `node --check` 和 `node --no-warnings` 检查;`node --no-warnings scripts/check-devtools-readiness.mjs`;子 agent 分方向审阅;最终再次运行 `node --check` 覆盖 `pages/detail/detail.js`、`pages/map/map.js`、`utils/auth.js`、`utils/store.js`、`cloudfunctions/posts/index.js`、4 个新增/扩展检查脚本、readiness 和 `harness/check-map-feed.mjs`;最终运行 `node --no-warnings` 覆盖 admin auth、detail action、store permission、cloud comment order 和 map feed guards;最终运行 `node scripts/check-json.mjs`、`node harness/check-harness.mjs`、`node --no-warnings scripts/check-devtools-readiness.mjs`、`git diff --check`、`bash harness/init.sh` -- 已记录证据:`harness/feature_list.json` 已补充 admin/detail/map 相关 round evidence,且保留原 feature 状态;专项输出包括 `Admin auth error checks passed.`、`Detail action guard checks passed.`、`Store permission guard checks passed.`、`Cloud comment order checks passed.` 和 `Map feed checks passed.`;JSON 输出 `Checked 11 JSON files.`;harness 输出 `Harness OK: 6 features checked.`;readiness 输出最终 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`;`git diff --check` 无输出;`bash harness/init.sh` 输出 `Harness init complete.` -- 验证限制:尝试运行 `npm run check` 时当前 shell 返回 `zsh:1: command not found: npm`;本轮已按仓库 fallback 用 bundled Node 逐项运行等价 node 门禁,但没有得到 npm wrapper 本身的通过证据 -- 已知风险或未解决问题:没有执行真实 WeChat DevTools/真机 UI 验收;readiness 仍报告 9420 service port/smoke blocker 属于环境阻塞,不是 UI passed。第 5 轮 reviewer 提醒线上高评论量场景建议在部署/运维侧确认 `post_comments(postId, status, createdAt desc)` 这类组合索引 -- 下一步最佳动作:先完成本轮最终验证命令;随后在 WeChat DevTools/真机上人工复查 admin 角色、详情信任动作、关闭权限、地图 hide/show/定位/找附近,以及云端评论列表 newest-first 行为 - -### Session 087FiveRoundPerformanceReview - -- 日期:2026-06-22 -- 工作区:`/Users/bytedance/.codex/worktrees/0422/x`;AGENTS.md 里的历史根路径是 `/Users/bytedance/git/x`,本轮按当前 Codex worktree 执行并记录 -- 本轮目标:按用户要求以性能专家身份连续进行 5 轮优化;每轮提出性能优化并启动三个同身份性能子 agent 审阅,至少 2 票 PASS 才算该轮通过 -- 已完成第 1 轮:地图 `buildPosts` 保持 raw localOnly 派生帖子,不再预先对所有帖子 `decorateMapPost`;NearbyPreview 改从 raw `baseVisiblePosts` 构建,减少重复装饰。初审 Hypatia 指出 buildPosts 仍预装饰,修复后相关后续复审通过;`discoverNearby` 同步修复为只装饰 selectedPost,避免 raw selectedPost 写入 UI -- 已完成第 2 轮:地图 `applyPostFilters` 将屏幕内筛选、分类计数和 openPostCount 合并到一次 `posts.forEach`,减少重复全量扫描。初审 Nash/Poincare 捕获 discovery raw selectedPost 回归,修复后 Galileo、Kant、Faraday 复审 PASS -- 已完成第 3 轮:`utils/store.js` 为 localOnly `listPosts` 增加 source-aware 短 TTL posts cache,减少地图刷新时反复读取 `wx` storage;同时用 `postsCache.source`、内部 source 派生、cloud cache 写入限制和动态 guard 防止 local/cloud/cache source 串线。多轮复审捕获并修复 localOnly/cloud 混用、cloud mutator 污染和 caller-provided source poisoning,最终 Hilbert、Peirce、Godel PASS -- 已完成第 4 轮:`sortedDerivedPosts` 在 raw posts 阶段预过滤 hidden,避免普通列表对 hidden 帖执行 `derivePost`、距离计算和排序;`listPosts` 强制 `includeHidden: false`,`listAllPosts` 继续保留 hidden。Banach 初审指出 `listPosts(center, { includeHidden: true })` 泄漏 hidden,修复并补 guard 后 Pascal、Gibbs、Popper 复审 PASS -- 已完成第 5 轮:`pages/admin/admin-review.js` 新增 `buildAdminSummary`,用一次遍历汇总 admin filter counts 和 stats,移除计数/统计的多次 `decoratedPosts.filter` 扫描;`scripts/check-admin-review.mjs` 补强静态 guard 并接入 readiness。Raman 初审指出 guard 未证明 summary 被使用且可漏过 hoisted repeated scans,修复后 Aristotle、Curie、Bohr 复审 PASS -- 运行过的验证:带 bundled Node PATH 的 `bash harness/init.sh`;`node --check` 覆盖 `pages/map/map.js`、`utils/store.js`、`pages/admin/admin-review.js`、`harness/check-map-feed.mjs`、`scripts/check-performance-guards.mjs`、`scripts/check-admin-review.mjs`、`scripts/check-devtools-readiness.mjs`;`node --no-warnings harness/check-map-feed.mjs`;`node --no-warnings scripts/check-performance-guards.mjs`;`node --no-warnings scripts/check-admin-review.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check` -- 已记录证据:`harness/feature_list.json` 已补充 map/admin 性能轮次证据;专项输出包括 `Map feed checks passed.`、`Performance guard checks passed.`、`Admin review helper checks passed.`;JSON 输出 `Checked 11 JSON files.`;harness 输出 `Harness OK: 6 features checked.`;readiness 输出 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`;`git diff --check` 无输出 -- 验证限制:当前 shell 中 `command -v npm` 无输出,未运行 npm wrapper;本轮使用 bundled Node 路径逐项运行仓库门禁。没有执行真实 WeChat DevTools/真机 UI 验收,readiness 中的 9420 service-port/smoke blocker 仍是环境阻塞,不是 UI passed evidence -- 已知风险或未解决问题:这些是性能和静态/动态 guard 优化,不改变 feature_list 的人工验收状态;地图原生层、真实定位、管理台真实管理员账号和真机性能体感仍需 DevTools/真机观察 -- 下一步最佳动作:如果要把本轮性能改动合入现有 PR,需要先提交并推送当前工作区改动;之后在 WeChat DevTools/真机复查地图列表/附近预览、localOnly 首屏刷新、hidden 任务可见性边界和管理台筛选统计 - -### Session 088LongShareDemoData - -- 日期:2026-06-22 -- 分支:`main` -- 本轮目标:修复用户反馈“分享页过期了,导致看不到返回首页按钮”,创建一条可长期体验的分享数据 -- 根因:`project.private.config.json` 的分享页启动模式固定打开 `post_001&from=share`;mock 里的 `post_001` 只有约 22 小时有效,而且本地 `wx` storage 一旦保存旧种子数据,后续启动不会自动刷新,可能继续读到过期的 `post_001`,从而隐藏分享接收动作区和 `回首页查这条` 入口 -- 已完成:`utils/mock-posts.js` 导出稳定的 `SHARE_DEMO_POST_ID='post_001'` 和 30 天 `SHARE_DEMO_TTL_MS`;`utils/store.js` 在 `seedPosts()` 中刷新缺失、过期或不足 7 天有效期的本地 `post_001`,并让详情页对该手测 fixture 优先使用本地长期样本,避免旧云端/旧缓存把体验入口变回过期态;`scripts/check-share-receiver-action.mjs` 新增长期分享数据和本地优先路径静态断言;`scripts/check-performance-guards.mjs` 在比较性能测试帖时忽略专用 `post_001` fixture,保留 localOnly 缓存读取次数断言 -- 运行过的验证:`pwd`;读取 `AGENTS.md`、`harness/claude-progress.md`、`harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node scripts/check-share-receiver-action.mjs`;`node --check utils/mock-posts.js`;`node --check utils/store.js`;`node --no-warnings scripts/check-performance-guards.mjs`;`node --check scripts/check-performance-guards.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;WeChat DevTools 当前项目 `/Users/bytedance/git/x` 的分享页视觉确认 -- 已记录证据:`node scripts/check-share-receiver-action.mjs` 输出 `Share receiver action checks passed.`;两条 `node --check` 无语法错误;`node --no-warnings scripts/check-performance-guards.mjs` 输出 `Performance guard checks passed.`;`node --no-warnings scripts/check-devtools-readiness.mjs` 输出 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`;DevTools 分享页显示 `地铁口捡到蓝色门禁卡`、`30天后过期`,并且 `回首页查这条` 按钮可见 -- 更新过的文件或工件:`utils/mock-posts.js`,`utils/store.js`,`scripts/check-share-receiver-action.mjs`,`scripts/check-performance-guards.mjs`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:DevTools 控制台仍有既有 `WAServiceMainContext timeout`,当前观察页面仍可用且不影响长期分享样本显示;真机分享入口仍需独立验证 -- 下一步最佳动作:在 WeChat DevTools 点击 `回首页查这条`,确认首页仍会打开列表并定位 `post_001`;随后真机验证分享页在长期样本下的滚动、按钮可见性和首页回跳 - -### Session 089ExpiredShareHomeReturn - -- 日期:2026-06-22 -- 分支:`main` -- 本轮目标:修复用户反馈“已过期的分享页也需要有回到首页的按钮” -- 根因:`回首页查这条` 之前放在 `shareReceiverActionStrip` 内,而 `buildShareReceiverActionStrip` 会对 `expired`、`resolved`、`hidden` 和风险态返回 `null`,所以过期分享页虽然仍有 `shareReceiverGuide`,但没有 action strip,也就没有回首页入口 -- 已完成:把 `share-receiver-map-return` 从 active-only action strip 中移到分享接收引导卡内,并用 `wx:if="{{!receiverConversionPrompt}}"` 避免二跳转化提示出现时抢主 CTA;过期分享页会保留 `shareReceiverGuide`,因此也会显示 `回首页查这条` -- TDD 证据:先修改 `scripts/check-share-receiver-action.mjs`,要求 expired share entry 仍有 receiver guide、回首页块不依赖 action strip、且出现在 action strip 之前;首次运行按预期失败,错误为 `focused home action should not depend on active-only receiver actions`;移动 WXML 后同一脚本通过 -- 运行过的验证:`bash harness/init.sh`;`node scripts/check-share-receiver.mjs`;`node scripts/check-share-receiver-action.mjs`;`node --check scripts/check-share-receiver-action.mjs`;`node --check pages/detail/detail.js`;`node --no-warnings scripts/check-devtools-readiness.mjs` -- 已记录证据:`node scripts/check-share-receiver.mjs` 输出 `Share receiver checks passed.`;`node scripts/check-share-receiver-action.mjs` 输出 `Share receiver action checks passed.`;readiness 输出 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.` -- 更新过的文件或工件:`pages/detail/detail.wxml`,`scripts/check-share-receiver-action.mjs`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:本轮通过静态结构和 helper 行为验证覆盖过期态;尚未用真实过期云端数据或真机分享链接做视觉验收 -- 下一步最佳动作:用一条真实 expired 分享链接打开详情页,确认 `已过期任务` 引导卡下方显示 `回首页查这条`,点击后回首页并展开附近列表 - -### Session 090PublishExpiryOptions - -- 日期:2026-06-23 -- 工作区:`/Users/bytedance/.codex/worktrees/e329/x`;AGENTS.md 里的历史根路径是 `/Users/bytedance/git/x`,本轮按当前 Codex worktree 执行;当前为 detached HEAD `9d2327a` -- 本轮目标:按用户要求把发布任务有效期从 `6小时/1天/3天` 改成 `1天/1周/1个月`,并增加自定义时间 -- 已完成:`utils/config.js` 的有效期预设改为 `1天`、`1周`、`1个月`、`自定义`;发布页默认有效期改为 `1天`;选择自定义后显示日期和时间 picker,展示具体截止时间;自定义时间需在未来 30 分钟到 30 天内,非法时会显示提示并阻止发布准备度通过;发布准备清单新增“有效期”项;本地 `utils/store.js` 与 `cloudfunctions/posts/index.js` 同步接受 `[24,168,720]` 预设小时和精确 `expiresAt` 自定义截止时间 -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md`、`harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`node --check pages/publish/publish.js`;`node --check pages/publish/publish-state.js`;`node --check utils/store.js`;`node --check cloudfunctions/posts/index.js`;`node --no-warnings scripts/check-publish-flow.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`node --no-warnings scripts/check-performance-guards.mjs`;`git diff --check`;WeChat DevTools 本地 `wcc` 全量编译 WXML;WeChat DevTools 本地 `wcsc -lc` 全量编译 WXSS;`npm run check`;最终再次 `bash harness/init.sh` -- 已记录证据:`node --check` 四条均无语法错误;`scripts/check-publish-flow.mjs` 输出 `Publish flow checks passed.` 并断言新四个有效期选项、自定义有效期 readiness 和 `5/5` 准备度;readiness 输出 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`;JSON 检查输出 `Checked 11 JSON files.`;harness 输出 `Harness OK: 6 features checked.`;性能 guard 输出 `Performance guard checks passed.`;`git diff --check` 无输出;`wcc` 和 `wcsc -lc` 退出码均为 0;`npm run check` 与 `bash harness/init.sh` 均通过 -- 更新过的文件或工件:`utils/config.js`,`pages/publish/publish.js`,`pages/publish/publish.wxml`,`pages/publish/publish.wxss`,`pages/publish/publish-state.js`,`utils/store.js`,`cloudfunctions/posts/index.js`,`scripts/check-publish-flow.mjs`,`scripts/check-performance-guards.mjs`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:本轮没有执行真实 WeChat DevTools/真机交互验收;readiness 仍报告 DevTools service port 9420 `connect_refused` 且配置显示 Service Port disabled,这不是 UI passed evidence。仍需实际操作自定义日期/时间 picker、提交本地/云端发布、检查详情页有效期展示;云端路径需要部署更新后的 `posts` 云函数后才会接受新有效期白名单和自定义 `expiresAt` -- 下一步最佳动作:在 WeChat DevTools 或真机打开发布 tab,分别点 `1天/1周/1个月/自定义`,选择一个合法自定义截止时间并发布,确认详情页显示对应剩余时间;部署 `posts` 云函数后再验证云端发布 - -### Session 091PublishCustomExpiryNoMax - -- 日期:2026-06-23 -- 工作区:`/Users/bytedance/.codex/worktrees/e329/x`;当前为 detached HEAD `9d2327a` -- 本轮目标:按用户反馈移除自定义有效期最大 30 天限制 -- 已完成:发布页自定义日期 picker 移除 `end` 限制;前端 `customExpiryError` 只校验自定义时间至少晚于当前 30 分钟;`utils/store.js` 和 `cloudfunctions/posts/index.js` 同步移除最大 30 天校验,仅保留下限校验;`scripts/check-publish-flow.mjs` 新增 45 天自定义有效期仍可发布的断言 -- 运行过的验证:`bash harness/init.sh`;`node --check pages/publish/publish.js`;`node --check utils/store.js`;`node --check cloudfunctions/posts/index.js`;`node --no-warnings scripts/check-publish-flow.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;WeChat DevTools 本地 `wcc` 全量编译 WXML;WeChat DevTools 本地 `wcsc -lc` 全量编译 WXSS;`npm run check`;最终再次 `bash harness/init.sh` -- 已记录证据:三条 `node --check` 均无语法错误;发布流程检查输出 `Publish flow checks passed.`,并覆盖 45 天自定义有效期没有 30 天上限;readiness 输出 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`;JSON 检查输出 `Checked 11 JSON files.`;harness 输出 `Harness OK: 6 features checked.`;`git diff --check` 无输出;`wcc`/`wcsc -lc` 均退出 0;`npm run check` 和最终 `bash harness/init.sh` 均通过 -- 更新过的文件或工件:`pages/publish/publish.js`,`pages/publish/publish.wxml`,`utils/store.js`,`cloudfunctions/posts/index.js`,`scripts/check-publish-flow.mjs`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:尚未在 WeChat DevTools/真机中实际打开 date picker 选择超过 30 天的日期并发布;云端路径仍需要部署更新后的 `posts` 云函数后才会接受新规则 -- 下一步最佳动作:在 DevTools 发布页选择超过 30 天的自定义日期确认 UI 不再限制;部署 `posts` 云函数后再验证云端发布同样接受超过 30 天的自定义有效期 - -### Session 092PublishExpiryLongTermPreset - -- 日期:2026-06-23 -- 工作区:`/Users/bytedance/.codex/worktrees/e329/x`;当前为 detached HEAD `9d2327a` -- 本轮目标:按用户最新反馈把发布有效期预设改成 `1周 / 1月 / 长期 / 自定义` -- 已完成:`utils/config.js` 的有效期预设改为 `1周`、`1月`、`长期`、`自定义`,发布页默认选中 `1周`;长期预设使用 `expiryType: long_term` 标记并在详情、个人发布、活动和管理展示中显示为 `长期有效`,避免展示成很大的天数;本地 `utils/store.js` 与 `cloudfunctions/posts/index.js` 同步接受 `1周/1月/长期` 预设和不设最大日期的自定义 `expiresAt` -- 运行过的验证:`bash harness/init.sh`;`node --check pages/publish/publish.js`;`node --check utils/config.js`;`node --check utils/store.js`;`node --check utils/format.js`;`node --check cloudfunctions/posts/index.js`;`node --no-warnings scripts/check-publish-flow.mjs`;`node --no-warnings scripts/check-candidate-flow.mjs`;`node --no-warnings scripts/check-performance-guards.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;WeChat DevTools 本地 `wcc` 全量编译 WXML;WeChat DevTools 本地 `wcsc -lc` 全量编译 WXSS;最终 `npm run check`;最终再次 `bash harness/init.sh` -- 已记录证据:`scripts/check-publish-flow.mjs` 断言有效期选项为 `1周/1月/长期/自定义`、长期展示为 `长期有效`、自定义 45 天仍可发布;候选流和性能 guard 均通过;readiness 输出 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`;JSON 检查输出 `Checked 11 JSON files.`;harness 输出 `Harness OK: 6 features checked.`;`git diff --check` 无输出;`wcc`/`wcsc -lc` 均退出 0;`npm run check` 和最终 `bash harness/init.sh` 均通过 -- 更新过的文件或工件:`utils/config.js`,`pages/publish/publish.js`,`pages/publish/publish.wxml`,`pages/publish/publish.wxss`,`pages/publish/publish-state.js`,`utils/store.js`,`utils/format.js`,`utils/post-presenter.js`,`pages/detail/detail.js`,`pages/admin/admin-review.js`,`cloudfunctions/posts/index.js`,`scripts/check-publish-flow.mjs`,`scripts/check-candidate-flow.mjs`,`scripts/check-performance-guards.mjs`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:尚未在真实 WeChat DevTools/真机中手动点击四个有效期选项和发布成功流;readiness 仍报告 DevTools service port 9420 阻塞,这不是 UI passed evidence。云端路径需要部署更新后的 `posts` 云函数后才会接受新的长期预设和自定义规则 -- 下一步最佳动作:在 WeChat DevTools 或真机打开发布页,确认四个有效期按钮、长期详情文案和超过 30 天的自定义日期均符合预期;部署 `posts` 云函数后再验证云端发布 - -### Session 093PublishDefaultLongTermImpact - -- 日期:2026-06-23 -- 工作区:`/Users/bytedance/.codex/worktrees/e329/x`;当前为 detached HEAD `9d2327a` -- 本轮目标:按用户要求把发布有效期默认选择改为 `长期`,并评估修改有效期对既有功能的影响 -- 已完成:发布页默认表单和默认选中态都改为从 `config.expiryOptions` 中查找 `type === 'long_term'` 的选项,因此默认选中 `长期`,重置表单后也保持 `长期`;`scripts/check-publish-flow.mjs` 增加静态守卫,要求默认值来自长期选项、默认 `expiryHours` 使用长期小时数并保留 `expiryType: long_term` -- 功能影响结论:有效期变化会影响任务何时自动变成 `expired`,进而影响列表排序/状态展示、评论、确认、分享接收侧风险提示等所有依赖 `expiresAt > Date.now()` 的功能。默认改为长期后,新任务默认会长期保持 active、可评论、可确认、可在普通列表展示;用户主动选择 `1周`、`1月` 或 `自定义` 时仍按对应截止时间过期;关闭、过时、举报和隐藏逻辑不依赖默认值,仍按原逻辑生效 -- 运行过的验证:`bash harness/init.sh`;`node --check pages/publish/publish.js`;`node --check scripts/check-publish-flow.mjs`;`node --no-warnings scripts/check-publish-flow.mjs`;`npm run check`;最终再次 `bash harness/init.sh`;`git diff --check` -- 已记录证据:发布流专项输出 `Publish flow checks passed.`;`npm run check` 通过并输出 JSON/harness/readiness 全部通过;最终 harness 输出 `Harness init complete.`;`git diff --check` 无输出 -- 更新过的文件或工件:`pages/publish/publish.js`,`scripts/check-publish-flow.mjs`,`harness/feature_list.json`,`harness/claude-progress.md` -- 已知风险或未解决问题:尚未在真实 WeChat DevTools/真机中手动确认发布页初始高亮为 `长期`;DevTools service port 9420 仍阻塞,readiness 不能替代 UI passed evidence。云端路径仍需部署更新后的 `posts` 云函数 -- 下一步最佳动作:在 DevTools UI 手动打开当前 worktree,确认发布页初始有效期高亮 `长期`,发布后详情页显示 `长期有效` -### Session 094UploadPackageSize - -- 日期:2026-06-25 -- 分支:`main` -- 本轮目标:修复用户反馈 WeChat DevTools 上传报错 `Error: 系统错误,错误码:80051, source size 48278KB exceed max limit 2MB` -- 根因:原始项目 checkout `/Users/bytedance/git/x` 中存在未跟踪的 `.understand-anything/` 本地分析产物目录,体积约 49MB;`project.config.json` 的 `miniprogramRoot` 为 `./` 且 `packOptions.ignore` 为空,DevTools 上传源码包时容易把该目录和其他本地工具目录一起纳入上传扫描,和报错中的 `48278KB` 高度吻合 -- 已完成:`.gitignore` 新增 `.understand-anything/`;`project.config.json` 的 `packOptions.ignore` 新增 `.git`、`.agents`、`.claude`、`.github`、`.understand-anything`、`harness`、`scripts`、`log` 目录,避免本地工具、harness、脚本和 git 元数据进入小程序上传包;未删除任何本地产物,未改动小程序运行代码 -- 运行过的验证:使用 bundled Node PATH 运行 `node scripts/check-json.mjs`、`node harness/check-harness.mjs`、`git diff --check`、按 `packOptions.ignore` 估算上传源码体积、最终 `bash harness/init.sh` -- 已记录证据:`node scripts/check-json.mjs` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.`;`git diff --check` 无输出;体积估算输出 `Approx packed source after packOptions.ignore: 579KB across 92 files`;最终 `bash harness/init.sh` 完整跑通并输出 `Harness init complete.` -- 已知风险或未解决问题:尚未由 WeChat DevTools 实际重新上传/预览验证;如果 DevTools 仍使用旧缓存,建议清理编译缓存或重新打开 `/Users/bytedance/git/x` 后再上传 -- 下一步最佳动作:在 WeChat DevTools 中重新上传或预览当前项目,确认不再出现 `source size ... exceed max limit 2MB` - -### Session 095ServiceSimplification - -- 日期:2026-07-02 -- 分支:`main` -- 本轮目标:按用户 goal 做服务优化,在不影响功能的前提下简化所有操作和非必要展示;每次修改后启动 3 个子 agent 审阅,至少 2 票 PASS 才算通过 -- 已完成:移除发布页单独准备度卡片和表单说明,保留定位块与底部主按钮状态;简化地图列表头部,移除重复分类说明和附近计数;简化详情页分享接收、发布后扩散、普通分享、TrustInsight、动作接力和评论接力中的辅助说明行;简化管理台、反馈页、我的发布、动态和个人页的重复副标题、空态提示和反馈计数;删除地图页已不展示的 `activeCategoryText`/`viewportPostCount` 状态和管理页已不展示的 `stats.feedback` -- 已完成:同步收窄发布状态和 TrustInsight 的数据/检查,移除已删除展示依赖的 `readiness.items`、`readiness.completionText`、`trustInsight.hint` 和 signal note 断言;`scripts/check-devtools-readiness.mjs` 内联 service simplification guard,覆盖活跃 readiness/smoke/runbook 文档和相关页面 JS/WXML/WXSS,防止重新引入旧准备度卡片、旧说明块、旧计数状态或未跟踪检查脚本依赖 -- 已完成:更新活跃设计/手测/readiness 文档,把当前发布验收口径统一为“发布状态、定位块、底部主按钮”;历史候选报告保留历史事实但明确标注不是当前发布页手测清单 -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md`、`harness/feature_list.json`、`git log --oneline -5`;使用 bundled Node PATH 运行 `bash harness/init.sh`;`node --check pages/map/map.js`、`node --check pages/admin/admin.js`、`node --check pages/publish/publish-state.js`、`node --check scripts/check-devtools-readiness.mjs`、`node --check scripts/check-publish-flow.mjs`、`node --check scripts/check-candidate-flow.mjs`、`node --check utils/format.js`;`node --no-warnings scripts/check-devtools-readiness.mjs`;`npm run check`;`git diff --check`;旧展示/旧文档术语定向 `rg` -- 已记录证据:`npm run check` 输出 `Checked 11 JSON files.`、`Harness OK: 6 features checked.`、`Service simplification checks passed.` 和最终 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`;`bash harness/init.sh` 输出 `Harness init complete.`;`git diff --check` 无输出;最终复审 Franklin、Ptolemy、Ramanujan 三票 PASS,无 Critical/Important findings,满足“两票通过”规则 -- 已知风险或未解决问题:没有执行真实 WeChat DevTools/真机视觉和点击验收;readiness 仍报告 9420 service port blocked / connection refused,这只能说明环境入口阻塞,不能写作 UI passed evidence。复审提出若后续继续极限清理,可再移除未渲染的 `nextAction.note` 和部分详情 helper 的旧字段,但它们不影响当前用户功能或可见展示 -- 下一步最佳动作:在 WeChat DevTools service port 恢复后,用发布、地图列表、详情信任动作/评论/分享、管理处置、反馈提交、个人页下一步做一次真实 UI smoke,并把结果记录为 passed/failed/blocked/not_covered - -### Session 096PullAndPublishPageStyle - -- 日期:2026-07-02 -- 分支:`main` -- 本轮目标:按用户要求拉取最新代码,并根据截图优化发布页样式,减少“卡片套卡片”的层级感,修正底部状态标题/禁用按钮颜色问题 -- 已完成:`git fetch origin` 后用 `git pull --ff-only --autostash origin main` 快进到 `a69d464`;保留远端默认长期/自定义有效期发布逻辑,同时解决 autostash 与本地服务简化改动在 `harness/claude-progress.md`、`pages/publish/publish-state.js`、`scripts/check-publish-flow.mjs` 的冲突 -- 已完成:发布页表单容器去掉全局 `panel` 叠层,改为单一轻量表单面板;`form-section` 改为透明分区和分隔线,去掉内层卡片边框/背景;底部 `publish-action` 状态标题颜色改为品牌深绿,禁用主按钮改成不透明浅绿底和更高对比文字 -- 运行过的验证:`node --check pages/publish/publish-state.js`;`node --check scripts/check-publish-flow.mjs`;`node --no-warnings scripts/check-publish-flow.mjs`;`npm run check`;`bash harness/init.sh`;`git diff --check`;`git diff --cached --check` -- 已记录证据:发布流专项输出 `Publish flow checks passed.`;`npm run check` 输出 `Checked 11 JSON files.`、`Harness OK: 6 features checked.`、`Service simplification checks passed.` 和最终 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`;`bash harness/init.sh` 输出 `Harness init complete.`;两条 diff whitespace 检查均无输出 -- 已知风险或未解决问题:没有执行真实 WeChat DevTools/真机视觉验收;readiness 仍报告 9420 service port blocked / connection refused。`git pull --autostash` 因冲突保留了 `stash@{0}` 作为备份,当前改动已应用到工作区并解决冲突,未删除该备份 -- 下一步最佳动作:在 DevTools 手动打开发布页,确认截图中的表单层级已经变为单层面板、底部“还差标题/补标题”状态颜色对比正常,并实际点选 `长期/自定义` 有效期回归发布流程 - -### Session 097PerformanceSecurityAudit - -- 日期:2026-07-02 -- 分支:`main` -- 工作区:`/Users/bytedance/.codex/worktrees/da89/x`;AGENTS.md 历史根路径仍写 `/Users/bytedance/git/x`,本轮按当前 Codex worktree 执行 -- 本轮目标:按用户 `/goal` 做一次全量性能优化和安全性检查 -- 已完成安全检查:梳理 WeChat 小程序、`utils/store.js`、`cloudfunctions/posts`、`cloudfunctions/getMyRole` 的信任边界;扫描 secret/历史高危前缀、CloudBase action 鉴权、管理员路径、评论/反馈/图片/病毒传播埋点白名单、CI workflow、Docker/IaC、repo-local `.agents/skills`;根项目和两个云函数目录 `npm audit --omit=dev --json` 均为 0 vulnerabilities -- 已完成安全修复:`cloudfunctions/getMyRole/index.js` 不再把 raw `OPENID` 和 raw admin document id 写入 CloudBase 日志,改为短 SHA-1 哈希;`.github/workflows/readiness.yml` 将 `actions/checkout@v4` 和 `actions/setup-node@v4` pin 到当前 tag 对应的 commit SHA,降低 tag move 供应链风险;`.gitignore` 新增 `.gstack/`,确保本地安全报告不会被提交 -- 已完成性能优化:`pages/me/me-state.js`、`pages/my-posts/my-posts.js`、`pages/activities/activities.js` 将重复 `filter` 统计改为单次遍历/`reduce`,减少个人页、我的发布和参与记录页对最多 100 条帖子与活动记录的重复扫描;`package.json` 增加 `"type": "module"`,消除 Node 检查脚本动态导入 ES module 时的重解析警告 -- 已生成本地报告:`.gstack/security-reports/2026-07-02T13-12-28Z-security.json`,该目录已被 `.gitignore` 忽略;报告结论为无仍开放的 high/critical 高置信发现,本轮有日志隐私和 CI action pinning 两项安全硬化已修复 -- 运行过的验证:`bash harness/init.sh`;`npm run check`;`node --check cloudfunctions/getMyRole/index.js`;`node --check pages/me/me-state.js`;`node --check pages/my-posts/my-posts.js`;`node --check pages/activities/activities.js`;`node scripts/check-me-state.mjs`;`node scripts/check-performance-guards.mjs`;根目录 `npm install --package-lock-only --ignore-scripts`/audit;`cloudfunctions/posts` 和 `cloudfunctions/getMyRole` 下 `npm audit --omit=dev --json`;`node scripts/check-json.mjs`;`git diff --check`;临时 getMyRole 日志隐私断言;`git ls-remote` 校验两个 GitHub Actions v4 tag 当前 SHA -- 已记录证据:`npm run check` 输出 `Checked 11 JSON files.`、`Harness OK: 6 features checked.`、`Performance guard checks passed.`、`Admin auth error checks passed.`、`DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`;`bash harness/init.sh` 输出 `Harness init complete.`;两个云函数目录 audit 均报告 critical/high/total 为 0;日志隐私断言输出 `getMyRole log privacy check passed.` -- 更新过的文件或工件:`.github/workflows/readiness.yml`,`.gitignore`,`package.json`,`package-lock.json`,`cloudfunctions/getMyRole/index.js`,`pages/me/me-state.js`,`pages/my-posts/my-posts.js`,`pages/activities/activities.js`,`harness/claude-progress.md`,忽略的本地 `.gstack/security-reports/2026-07-02T13-12-28Z-security.json` -- 已知风险或未解决问题:没有执行真实 WeChat DevTools/真机 UI 和性能体感验收;readiness 仍报告 9420 service port blocked / connection refused。未读取全局 AI skills/hooks,因为这会超出仓库范围;也未做 CloudBase 线上数据库权限 readback -- 下一步最佳动作:恢复 WeChat DevTools service port 后,做发布/地图/详情/管理/个人页真实 smoke;如果要进一步提升安全置信度,再做 CloudBase 线上集合权限 readback 和全局 agent skill/hook 扫描 - -### Session 098MainDirtyStyleFix - -- 日期:2026-07-09 -- 分支:`main` -- 工作区:`/Users/bytedance/git/x` -- 本轮目标:按用户澄清,review 并修复当前 `main` 工作区未提交样式改动引入的交互和样式回归 -- 根因:本地 dirty 样式刷新把地图原生 map 上方的 `cover-view` 浮层改成依赖 CSS 变量,而当前自定义 tabBar 注释和历史问题都表明 `cover-view` 样式支持更受限;个人页保留了空的 `profile-grid` 装饰节点,但对应绝对定位 CSS 已被删除,导致它会作为普通 grid 子项挤错头像/昵称/操作按钮;全局标题引入负字距,不适合小程序中文窄屏布局 -- 已完成:地图列表入口、定位/找一找工具、诊断面板和选中任务卡的关键 `cover-view` 样式改回硬编码值,并扩展 `scripts/check-map-list-resilience.mjs` 禁止这些原生覆盖层重新使用 `var(...)`;移除 `pages/me/me.wxml` 中残留的 `profile-grid`;把全局 `.title` 的 `letter-spacing` 改回 `0`;`scripts/check-devtools-readiness.mjs` 增加个人页残留节点和负字距回归检查;`DESIGN_SYSTEM.md` 记录普通 WXSS 用 token、原生覆盖层用硬编码值的例外 -- 运行过的验证:`bash harness/init.sh`;`node --check scripts/check-map-list-resilience.mjs`;`node --check scripts/check-devtools-readiness.mjs`;`node --no-warnings scripts/check-map-list-resilience.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;WeChat DevTools 本地 `wcc` 全量编译 WXML;WeChat DevTools 本地 `wcsc -lc` 全量编译 WXSS;`git diff --check`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;最终再次 `bash harness/init.sh` -- 已记录证据:地图列表静态 guard 输出 `Map list resilience checks passed.`;readiness 输出 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`;WCC/WCSC 均退出 0;`git diff --check` 无输出;JSON 检查输出 `Checked 11 JSON files.`;harness 输出 `Harness OK: 6 features checked.`;最终 `bash harness/init.sh` 输出 `Harness init complete.` -- 已知风险或未解决问题:当前环境没有 `npm`,所以 `npm run check` 无法直接执行;已分别执行其对应的 JSON、harness 和 readiness 子检查。DevTools service port 9420 仍为 blocked / connect_refused,因此本轮没有真实 WeChat DevTools/真机视觉和点击验收,不能写作 UI passed evidence。 -- 下一步最佳动作:在 WeChat DevTools service port 恢复后,重点手测地图页 `列表 N`、定位/找一找、marker 选中卡详情/关闭、打开抽屉,以及我的页头像/昵称/反馈或登录按钮是否仍在同一行布局内 - -### Session 099MainDirtyUiDevToolsFix - -- 日期:2026-07-10 -- 分支:`main` -- 工作区:`/Users/bytedance/git/x` -- 本轮目标:继续排查并修复当前 `main` 未提交 UI 刷新中的可复现样式和交互问题,并补真实 DevTools 页面级证据 -- 根因与修复:详情页仍保留空的 `hero-grid` 装饰节点,但本轮 dirty WXSS 已删除对应绝对定位规则;该节点因此成为 `.hero.stack` 的普通 flex 子项,额外插入 `20rpx` 间距。已删除该节点,并在 `scripts/check-devtools-readiness.mjs` 增加回归断言;保留上一轮地图 `cover-view` 硬编码、个人页 `profile-grid` 删除和标题零字距修复 -- DevTools 实测:使用 WeChat DevTools Stable `v2.01.2510290`、基础库 `3.15.2` 直接打开 `/Users/bytedance/git/x`;验证分享详情页、回首页后地图列表展开/收起、地图瓦片与选中任务卡、发布页滚动到底部、管理页异步数据加载、个人页布局;修复后重新从地图选中卡进入详情页,Hero 首屏间距恢复,未执行发布、评论、信任动作、隐藏或关闭等数据变更 -- 运行过的验证:`node --check scripts/check-devtools-readiness.mjs`;`node --no-warnings scripts/check-map-list-resilience.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;WeChat DevTools 本地 `wcc` 全量编译 WXML;WeChat DevTools 本地 `wcsc -lc` 全量编译 WXSS;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;`npm run check`;最终 `bash harness/init.sh` -- 已记录证据:地图 guard 输出 `Map list resilience checks passed.`;readiness 输出 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`;WCC/WCSC 均退出 0;页面截图与 accessibility tree 均确认详情、地图、发布、管理、个人页有可见内容且主要布局不互相遮挡 -- 已知风险或未解决问题:DevTools 控制台仍显示既有 `WAServiceMainContext` `Error: timeout`,service port `9420` 仍为 disabled/connect_refused;页面可直接在 GUI 中操作,但 CLI smoke 仍 blocked。未执行真机、安全区多机型、系统分享菜单、定位授权正反分支或会改变数据的提交/处置流程,因此不标记完整 UI/功能通过 -- 下一步最佳动作:启用 DevTools Service Port 后补自动 smoke,并在真机覆盖定位授权、键盘、系统分享菜单和一条完整发布/评论/处置闭环 - -### Session 100MapViewportEdges - -- 日期:2026-07-10 -- 分支:`main` -- 工作区:`/Users/bytedance/git/x` -- 本轮目标:按用户截图修复首页顶部绿色空白和自定义 tabBar 底部边缘透出地图 -- 根因与修复:`.task-map` 原先是普通流中的 `height: 100vh`,没有显式贴住页面顶部且继续延伸到自定义 tabBar 后方;`.map-page` 同时使用绿色背景,导致原生地图合成边界出现时顶部显示绿色空区、底部设备圆角露出地图。现将 `.map-page` 固定为 `height: 100vh` 和白色兜底背景,将 `.task-map` 绝对定位到 `top: 0; left: 0`,高度改为 `calc(100vh - 116rpx - env(safe-area-inset-bottom))`,列表展开态仍保持 `38vh` -- TDD 证据:先扩展 `scripts/check-map-list-resilience.mjs` 要求固定白色地图视口和明确的地图上下边界;首次运行按预期失败,分别报告 `.map-page should provide a fixed white viewport` 与 `.task-map should start at the top and stop above the custom tabBar`;修改 WXSS 后同一检查通过 -- DevTools 实测:在 WeChat DevTools Stable `v2.01.2510290` 的 iPhone 12/13 模拟器中,修复前复现导航栏下方绿色空白;热更新后地图瓦片紧贴导航栏下沿,地图在 tabBar 顶部结束,底部圆角外只显示白色兜底;点击 `列表 6` 后抽屉正常展开,点击 `收起` 后恢复折叠态 -- 运行过的验证:`bash harness/init.sh`;`node --check scripts/check-map-list-resilience.mjs`;`node --no-warnings scripts/check-map-list-resilience.mjs`;`node --no-warnings harness/check-map-feed.mjs`;WeChat DevTools 本地 `wcc` 全量编译 WXML;WeChat DevTools 本地 `wcsc -lc` 全量编译 WXSS;`npm run check`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;最终 `bash harness/init.sh` -- 已记录证据:地图 resilience 输出 `Map list resilience checks passed.`;map feed 输出 `Map feed checks passed.`;WCC/WCSC 退出 0;DevTools 修复后截图与 accessibility tree 均确认地图首屏、列表入口和展开抽屉有可见内容 -- 已知风险或未解决问题:DevTools 仍保留既有 `WAServiceMainContext Error: timeout`,Service Port `9420` 仍 disabled/connect_refused;本轮没有真机覆盖其他刘海/安全区尺寸,因此不标记整个地图功能 passing -- 下一步最佳动作:在至少一台真机和一台不同安全区模拟器确认地图顶部、tabBar 底部、列表抽屉和定位按钮边界一致 - -### Session 101PublishDuplicateTabBar - -- 日期:2026-07-10 -- 分支:`main` -- 工作区:`/Users/bytedance/git/x` -- 本轮目标:按用户截图修复发布页上滑后出现两套底部 tabBar -- 根因与修复:自定义 tabBar 整体使用固定 `cover-view/cover-image` 原生覆盖层;发布页滚动时该覆盖层会留下一个无文字的旧合成帧,形成上下两套图标。上一轮已经让原生地图在 tabBar 上方结束,因此 tabBar 不再需要覆盖地图;现将 `custom-tab-bar/index.wxml` 全部改为普通 `view/image`,保持原绑定、图标、文字和 active 指示条不变,并同步更新 WXSS 注释与设计系统约束 -- TDD 证据:先在 `scripts/check-devtools-readiness.mjs` 加入 tabBar 不得包含 `cover-view/cover-image`、必须保留单一普通视图 shell 的断言;首次运行按预期失败,错误为 `Custom tabBar should use normal view/image nodes; fixed cover nodes can leave duplicate native-layer frames while a page scrolls.`;结构替换后 readiness 通过 -- DevTools 实测:在 WeChat DevTools Stable `v2.01.2510290` 的 iPhone 12/13 模拟器中进入发布页,连续滚动和多次 `Page_Down` 后,active publish Webview 只包含一组 `地图/发布/管理/我的`,截图只显示一个底部 dock;切回地图页后地图瓦片和普通视图 tabBar 同时可见,没有被原生地图遮挡 -- 运行过的验证:`bash harness/init.sh`;`node --check scripts/check-devtools-readiness.mjs`;`node --no-warnings scripts/check-devtools-readiness.mjs`;WeChat DevTools 本地 `wcc` 全量编译 WXML;WeChat DevTools 本地 `wcsc -lc` 全量编译 WXSS;`npm run check`;`node scripts/check-json.mjs`;`node harness/check-harness.mjs`;`git diff --check`;最终 `bash harness/init.sh` -- 已记录证据:readiness 输出 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`;WCC/WCSC 退出 0;发布页滚动截图和 accessibility tree 均确认单 tabBar;地图页截图确认普通 tabBar 仍可见 -- 已知风险或未解决问题:DevTools 仍有既有 `WAServiceMainContext Error: timeout`,Service Port `9420` 仍 disabled/connect_refused;未在真机做快速连续滑动和安全区复测,因此不标记发布功能整体 passing -- 下一步最佳动作:在真机连续快速上下滑发布页,并切换四个 tab,确认没有重复 dock、点击失效或安全区抖动 - -### Session 102MapBridgeAndPackagePerformance - -- 日期:2026-07-14 -- 分支:`main` -- 工作区:`/Users/bytedance/git/x` -- 本轮目标:按用户要求做一轮可量化、低风险的性能优化,优先收敛当前 `map-feed-001` 首屏/刷新链路,并避免 DevTools 打包本地工具工件 -- 根因与修复:地图 `applyPostFilters` 每次刷新都会把 JS 内部原始 `posts` 放入 `Page.data/setData`,同时继续计算和传输已经没有 WXML 消费者的 `nearbyPreviewPosts` 与 `openPostCount`;现将原始帖子保留为 Page 实例字段 `this.posts`,删除已失去 UI 消费者的预览 helper/字段/计算,并让没有选中任务的普通地图拖动直接跳过 region-end 整批重建。`project.config.json` 同时排除本地 `.gstack/` 和未被应用引用的 `style-comparison.html`,只改变打包边界,不删除用户文件 -- TDD 证据:先更新 `harness/check-map-feed.mjs`,旧实现按预期失败并报告 `Map filtering should not compute or transfer presentation fields removed from the current UI.`;完成控制器状态迁移后新增普通地图拖动不得空重建的断言,旧 region-end 实现再次按预期失败并报告 `Ordinary map pans should not rebuild and resend an unchanged feed just to clear an empty selection.`;实现早返回后同一门禁通过 -- 性能证据:使用同一组 100 条合成任务、300 次 `applyPostFilters` 刷新,桌面 Node 微基准中的平均 `setData` JSON 负载从 `183,539 B` 降到 `119,848 B`,减少 `63,691 B / 34.7%`;同一脚本的 JS 处理加 JSON 序列化均值从 `0.541 ms` 降到 `0.380 ms`,减少约 `29.8%`。无选中任务时的普通 region-end 现在为 `0` 次 feed `setData`。这些数字是可重复的桌面合成基准,不代表真机帧率或原生 map bridge 耗时 -- 包体证据:按当前 `packOptions.ignore` 与独立 `cloudfunctionRoot` 估算,补充排除规则前当前树为 `588,892 B / 91 files`,排除 `.gstack/` 与 `style-comparison.html` 后为 `482,579 B / 86 files`,避免 `106,313 B / 18.1%` 本地非应用输入;没有采用收益较小且会延迟常用详情首开的 subpackage 改造,也没有改动视觉图片资产 -- 运行过的验证:`bash harness/init.sh` 基线;`node --no-warnings harness/check-map-feed.mjs` 失败/通过 TDD;`node --check pages/map/map.js`;`node --check utils/post-presenter.js`;`node scripts/check-json.mjs`;`node --no-warnings scripts/check-performance-guards.mjs`;`node --no-warnings scripts/check-map-list-resilience.mjs`;`npm run check`;`git diff --check`;前后 100 条/300 次 map payload 微基准;两条 BSD/macOS-safe `find | stat | awk` 包体边界估算;最终 `bash harness/init.sh` -- 已记录证据:`Map feed checks passed.`、`Performance guard checks passed.`、`Map list resilience checks passed.`、`Checked 11 JSON files.`、`Harness OK: 6 features checked.`、`DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`;`git diff --check` 无输出 -- 严格复审:按 `FP1` 到 `FP5` 将完整 diff、上下文和验证结果并发提交给三个 `super-relay-review` reviewer;第一轮一票批准、两票因 reviewer 输出格式不合法而按 blocked 处理,未把失败票算作通过;随后完整重跑三票,第二轮三位 reviewer 均对五个功能点给出 `APPROVE`,无 actionable finding。共同保留的限制是尚无 DevTools/真机性能轨迹和真实上传包详情 -- 已知风险或未解决问题:本轮没有得到真实 WeChat DevTools 性能面板、上传包详情或真机帧率/手势体感证据;readiness 仍确认 service port `9420` disabled/connect_refused,因此保留 `map-feed-001=in_progress`,不把桌面基准写作 UI/真机通过。未跟踪的 `style-comparison.html` 仍原样保留,只是不再进入小程序包 -- 下一步最佳动作:启用 DevTools Service Port 后,在 100 条附近任务样本下录制地图首屏、连续拖动、选中/清除 marker、打开列表和分类切换的性能轨迹,并用 DevTools 上传详情确认实际包体未包含 `.gstack/` 与 `style-comparison.html` diff --git a/harness/clean-state-checklist.md b/harness/clean-state-checklist.md deleted file mode 100644 index 25bc325..0000000 --- a/harness/clean-state-checklist.md +++ /dev/null @@ -1,10 +0,0 @@ -# 干净状态检查清单 - -- [ ] `bash harness/init.sh` 仍然可运行,或失败原因已记录 -- [ ] `npm run check:json` 已运行,结果已记录 -- [ ] `node harness/check-harness.mjs` 已运行,结果已记录 -- [ ] 当前进度已经记录到 `harness/claude-progress.md` -- [ ] 功能状态真实反映 passing 和未验证边界,没有假 passing -- [ ] 所有本轮新增或修改的 harness 文件都位于 `harness/`,根目录只保留 `AGENTS.md` 入口 -- [ ] 没有半成品步骤处于未记录状态 -- [ ] 下一轮会话无需聊天记录即可从仓库继续 diff --git a/harness/devtools-port-deep-forensics-checklist.md b/harness/devtools-port-deep-forensics-checklist.md deleted file mode 100644 index b623b9c..0000000 --- a/harness/devtools-port-deep-forensics-checklist.md +++ /dev/null @@ -1,384 +0,0 @@ -# AA DevTools Service Port Deep Forensics Checklist - -日期:2026-06-17 - -范围:用于 AA 组 QA / 设计 agent 在 DevTools service port `9420` 仍未 ready 时补深层取证口径。本文档只定义检查层、证据边界、blocked 状态和人工确认项;不执行 GUI 恢复,不修改 DevTools 设置,不修改用户数据,不创建真实 evidence 文件。 - -已知背景:Z 组已把 app-level quit / reopen 做成显式 opt-in,并证明一次运行中 `DevTools app quit: completed`,但 `DevTools open` 仍超时,service port 仍是 `connect_refused` / smoke `blocked`。因此 AA 取证必须区分“app 退出动作完成”和“service port listener 恢复”,不能把前者写成后者。 - -## 0. 全局边界 - -- [ ] 本清单只允许只读观察:端口探测、CLI help / version、app bundle 元数据、user data dir 摘要、最近日志摘要、进程树摘要、`lsof` / `netstat` 摘要、人工 UI 设置确认项、真机替代方案。 -- [ ] 不执行 `quit`、`open --project`、`preview`、`upload`、清缓存、杀进程、重装 DevTools、修改服务端口、修改用户配置、修改本地 storage 或 CloudBase 数据。 -- [ ] 不提交原始日志、完整命令输出、截图原图、真实 local evidence、用户目录、真实用户数据或完整本机路径。 -- [ ] 自动 readiness、静态 guard、dry-run、blocked draft、app quit 动作完成都不是 UI passed,也不是 service port recovered。 -- [ ] 任何 blocked 记录都必须写清 `blockedCode`、只读 evidence 摘要、影响范围和下一步人工确认项。 - -## 1. Evidence 共同规则 - -所有检查层只允许写“可判定但不可反推出用户隐私”的摘要。 - -Allowed evidence: - -- [ ] 状态:`ready`、`blocked`、`unknown`、`not_run`。 -- [ ] 计数:DevTools-like 进程数量、listener 数量、匹配日志行数、最近错误类型数量。 -- [ ] 脱敏字段:``、``、``、``、``、``。 -- [ ] 最小错误分类:`LISTEN present`、`no LISTEN`、`ECONNREFUSED`、`timeout`、`permission denied`、`ambiguous owner`。 -- [ ] 版本摘要:DevTools short version / build version / CLI help available 状态;无法确认时写原因。 -- [ ] 人工 UI 确认结论:开关已开 / 未开 / 未确认,端口号是否为 `9420` 或指定端口。 - -Forbidden evidence: - -- [ ] 完整本机路径,包括真实用户目录、真实项目绝对路径、DevTools user data 子路径、日志文件绝对路径。 -- [ ] cookie、token、Authorization、session、refresh token、access token、client secret、AppSecret、私有 AppID。 -- [ ] 完整进程命令行、完整环境变量、完整 CLI stderr、完整 HTTP header / body、完整 Console / Network / cloud logs。 -- [ ] 真实用户数据:openid、unionid、手机号、微信号、昵称、头像 URL、联系人、群名、评论正文、图片原始 URL、精确经纬度。 -- [ ] 可识别设备信息:设备序列号、真实 macOS 用户名、完整机器名、个人证书路径。 - -## 2. Blocked 状态码 - -只使用以下 blocked code,避免同一类阻塞被写成多个口径。 - -- `declared_without_listener`:只读进程摘要显示 DevTools 或 CLI 声明了目标 service port,但 `lsof` / `netstat` 没有对应 listener。 -- `connect_refused`:目标端口无可连接服务,`nc` / `curl` / smoke access 得到 connection refused。 -- `open_timeout`:显式 opt-in 的 CLI open / app reopen 尝试已经发生但等待端口超时;只能证明 open 未能使 listener ready。 -- `service_port_toggle_unconfirmed`:无法从人工 UI 或可信设置摘要确认 DevTools 的 Service Port 开关是否已开启。 -- `manual_ui_needed`:只读证据不足以继续,必须由用户在 DevTools UI 中确认设置、项目、版本、账号或设备状态。 - -Blocked 记录最小模板: - -```json -{ - "layer": "read-only-port", - "status": "blocked", - "blockedCode": "connect_refused", - "evidenceSummary": "Target port has no listener; IPv4 probe returned connection refused.", - "impact": "DevTools smoke, share payload inspection, timeline menu, and manual UI journeys were not executed.", - "nextHumanConfirmation": [ - "Confirm DevTools Settings > Security Settings > Service Port is enabled.", - "Confirm the displayed service port is 9420 or provide the actual port." - ], - "notClaimed": [ - "readiness/static guard equals UI passed", - "app quit completed equals service port recovered", - "DevTools smoke passed", - "real-device journey passed" - ] -} -``` - -## 3. QA 检查层 - -### 3.1 只读端口层 - -目的:确认目标 service port 是否存在 listener,并区分 refused、timeout、占用和归属不明。 - -Suggested read-only checks: - -```bash -npm run inspect:devtools-port -- --port 9420 -node scripts/inspect-devtools-port-state.mjs --port 9420 -nc -vz 127.0.0.1 9420 -curl -sS --max-time 3 http://127.0.0.1:9420/ > -``` - -Allowed evidence: - -- [ ] `port=9420`,`listener=yes/no/unknown`。 -- [ ] IPv4 / IPv6 连接状态摘要:`connected`、`connect_refused`、`timeout`、`permission_denied`。 -- [ ] HTTP 探针最小分类:`non-business response`、`404`、`auth-like response`、`empty`、`refused`、`timeout`。 -- [ ] 归属摘要:`DevTools-like owner`、`non-DevTools owner`、`ambiguous owner`、`no owner`。 - -Forbidden evidence: - -- [ ] 完整 HTTP response、headers、cookies、body、stack trace。 -- [ ] 完整 `curl -v` 输出或完整本机临时文件路径。 -- [ ] 其他本机服务的进程名、完整路径或私有参数。 - -Blocked mapping: - -- [ ] 有声明无 listener:`declared_without_listener`。 -- [ ] 探针明确 refused:`connect_refused`。 -- [ ] 探针超时且无法确认 listener:`manual_ui_needed` 或 `service_port_toggle_unconfirmed`。 - -### 3.2 CLI help / version 层 - -目的:确认 DevTools CLI 存在、可读取帮助或版本信息,但不调用有 GUI 副作用的子命令。 - -Suggested read-only checks: - -```bash - --help - --version -``` - -Allowed evidence: - -- [ ] `cliHelp=available/unavailable`。 -- [ ] `cliVersion=`。 -- [ ] CLI 路径摘要:``、``、`not_found`。 -- [ ] help 中是否列出 service-port 相关参数的摘要。 - -Forbidden evidence: - -- [ ] 完整 CLI 路径、完整 help 输出、完整 stderr。 -- [ ] 用户目录、安装目录下的个人路径片段。 -- [ ] 任何 `open`、`quit`、`preview`、`upload` 结果被混入本层证据。 - -Blocked mapping: - -- [ ] CLI 不存在或 help 不可读:`manual_ui_needed`。 -- [ ] CLI open 曾超时只能写到恢复尝试层:`open_timeout`,不能写成本层 passed 或 recovered。 - -### 3.3 App bundle 层 - -目的:确认 WeChat DevTools app bundle、bundle id 和版本摘要,支持判断是否在检查同一个工具实例。 - -Suggested read-only checks: - -```bash -defaults read /Contents/Info CFBundleIdentifier -defaults read /Contents/Info CFBundleShortVersionString -defaults read /Contents/Info CFBundleVersion -mdls -name kMDItemVersion -``` - -Allowed evidence: - -- [ ] `bundleId=`。 -- [ ] `shortVersion=`,`bundleVersion=`。 -- [ ] app 位置分类:`system Applications`、`user Applications`、`custom location`、`not found`。 -- [ ] 多 app bundle 计数和是否可能版本冲突。 - -Forbidden evidence: - -- [ ] 完整 app 路径,尤其是用户目录下的安装路径。 -- [ ] 完整 `mdls` 输出、签名详情、个人证书信息。 -- [ ] 把 bundle id 读取成功写成 service port ready。 - -Blocked mapping: - -- [ ] app bundle 找不到或多个 app bundle 归属不明:`manual_ui_needed`。 -- [ ] bundle 存在但端口仍无 listener:保持端口层 `connect_refused` 或 `declared_without_listener`。 - -### 3.4 User data dir 层 - -目的:确认 DevTools 是否可能使用某个 user data dir / profile,并只记录其状态摘要,不读取真实用户数据。 - -Suggested read-only checks: - -```bash -find -maxdepth 2 -type d -find -maxdepth 2 -type f -mtime -2 -``` - -Allowed evidence: - -- [ ] `userDataDirStatus=present/missing/unconfirmed`。 -- [ ] 最近变更文件计数、目录层级计数、profile 数量摘要。 -- [ ] 是否存在 service-port-like setting 的脱敏状态:`enabled`、`disabled`、`not_found`、`unreadable`、`unconfirmed`。 -- [ ] 权限摘要:`readable`、`permission_denied`、`not_checked`。 - -Forbidden evidence: - -- [ ] 完整 user data dir 路径。 -- [ ] 配置文件原文、数据库内容、local storage、cookie、session、账号、项目历史、最近打开项目列表。 -- [ ] 修改、删除、复制或压缩 user data dir。 - -Blocked mapping: - -- [ ] 无法确认 service port 开关:`service_port_toggle_unconfirmed`。 -- [ ] 需要 UI 中手动确认开关:`manual_ui_needed`。 - -### 3.5 最近日志层 - -目的:只读查看最近 DevTools / CLI 相关日志,提取错误类型和计数,定位 service port 为什么未 ready。 - -Suggested read-only checks: - -```bash -find -type f -mtime -2 -rg -i "service port|ide-http-port|listen|EADDRINUSE|ECONNREFUSED|timeout|open" -``` - -Allowed evidence: - -- [ ] 最近日志文件数量、时间范围、匹配行数。 -- [ ] 错误类型计数:`EADDRINUSE`、`ECONNREFUSED`、`timeout`、`permission denied`、`service port disabled`。 -- [ ] 只摘录极短、已脱敏的错误关键词,不粘贴上下文。 -- [ ] 日志来源分类:`DevTools app log`、`CLI log`、`system log`、`unconfirmed`。 - -Forbidden evidence: - -- [ ] 完整日志路径、完整日志片段、完整 stack trace、完整 request / response。 -- [ ] 账号、项目历史、插件、cookie、token、用户输入、真实 post 内容。 -- [ ] 把日志中出现 `open` 或 `quit` 当成恢复成功,除非端口层也有 listener evidence。 - -Blocked mapping: - -- [ ] 日志提示服务端口关闭但 UI 未确认:`service_port_toggle_unconfirmed`。 -- [ ] 日志只有 open timeout:`open_timeout`。 -- [ ] 日志不足以判断:`manual_ui_needed`。 - -### 3.6 进程树层 - -目的:确认是否有 DevTools-like 进程、是否多实例、是否有端口声明和父子进程异常。 - -Suggested read-only checks: - -```bash -ps -axo pid,ppid,comm -pgrep -fl "wechat|devtools|webplus|ide-http-port" -``` - -Allowed evidence: - -- [ ] DevTools-like 进程计数。 -- [ ] 父子层级摘要:`single tree`、`multiple trees`、`orphan-like child`、`unknown`。 -- [ ] 是否出现 `ide-http-port` 声明及端口号列表。 -- [ ] 多实例摘要:app bundle 数量、声明端口数量、疑似项目数量。 - -Forbidden evidence: - -- [ ] 完整进程命令行、完整参数、环境变量、真实项目路径、真实用户目录。 -- [ ] 其他用户或其他项目的进程细节。 -- [ ] 用进程存在替代 listener evidence。 - -Blocked mapping: - -- [ ] 进程声明 `9420` 但无 listener:`declared_without_listener`。 -- [ ] 多实例归属不明:`manual_ui_needed`。 -- [ ] 无 DevTools-like 进程且无法确认 UI 状态:`manual_ui_needed`。 - -### 3.7 `lsof` / `netstat` 层 - -目的:从系统 socket 视角确认端口监听、连接状态和占用归属。 - -Suggested read-only checks: - -```bash -lsof -nP -iTCP:9420 -lsof -nP -iTCP:9420 -sTCP:LISTEN -netstat -anv | rg "9420|LISTEN" -``` - -Allowed evidence: - -- [ ] listener 行数:`0`、`1`、`>1`。 -- [ ] socket 状态摘要:`LISTEN`、`ESTABLISHED`、`TIME_WAIT`、`none`。 -- [ ] 进程归属摘要:`DevTools-like`、`non-DevTools`、`unknown`。 -- [ ] 如果端口被其他进程占用,只写类别和影响,不写完整进程名或路径。 - -Forbidden evidence: - -- [ ] 完整 `lsof` 行、完整 PID / user / command / path。 -- [ ] 其他端口的大量 netstat 输出。 -- [ ] 把 `TIME_WAIT` 或历史连接写成 service port ready。 - -Blocked mapping: - -- [ ] `LISTEN=0` 且连接 refused:`connect_refused`。 -- [ ] 有端口声明但 `LISTEN=0`:`declared_without_listener`。 -- [ ] listener 归属不是 DevTools 或归属不明:`manual_ui_needed`。 - -### 3.8 DevTools settings UI 人工确认层 - -目的:只列出需要用户在 WeChat DevTools UI 中确认的设置;AA 不代替用户点击、不改设置。 - -需要请用户确认: - -- [ ] Settings / Security Settings 中 Service Port 是否开启。 -- [ ] UI 显示的 service port 是否为 `9420`;如果不是,请用户提供实际端口号。 -- [ ] 当前打开项目是否是目标 worktree;如果 UI 显示多个项目,请用户确认当前项目归属。 -- [ ] DevTools 版本、基础库版本、是否登录、是否存在升级提示或权限弹窗。 -- [ ] 是否同时开了多个 DevTools app / 项目窗口 / 账号环境。 -- [ ] 如果用户愿意人工介入,是否允许仅在 UI 中切换 Service Port 开关并重启 DevTools;默认答案必须当作未授权。 - -Allowed evidence: - -- [ ] 用户确认的开关状态:`enabled`、`disabled`、`unconfirmed`。 -- [ ] 用户确认的端口号:`9420`、`other:`、`unconfirmed`。 -- [ ] 用户确认的项目归属:`target project`、`different project`、`multiple projects`、`unconfirmed`。 -- [ ] 手工截图的脱敏摘要或附件编号,不提交原图。 - -Forbidden evidence: - -- [ ] 用户设置页完整截图、真实账号、头像、AppID、项目历史、完整项目路径。 -- [ ] 未经用户确认就写“Service Port 已开启”。 -- [ ] 把 UI 开关已开启写成 service port ready;还必须有 listener / smoke evidence。 - -Blocked mapping: - -- [ ] UI 开关无法确认:`service_port_toggle_unconfirmed`。 -- [ ] 用户需要打开设置页确认:`manual_ui_needed`。 -- [ ] UI 显示开启但端口 refused:保留端口层 `connect_refused`,并记录需要人工重启或换端口。 - -### 3.9 真机替代方案层 - -目的:当 DevTools service port 持续 blocked 时,允许改走真机 / UI 手动证据路线,但要明确它不恢复 CLI service port。 - -Allowed evidence: - -- [ ] 真机设备类型摘要:`iOS`、`Android`、`WeChat version known/unknown`、`screen width bucket`。 -- [ ] 手动打开小程序、分享菜单、朋友圈入口、落地页首屏、confirm/comment 转化的脱敏截图 / 录屏摘要。 -- [ ] payload 可检查性:`inspectable`、`not inspectable on device`、`checked by DevTools hook later`。 -- [ ] 若真机通过,写成 real-device journey evidence,不写成 service port recovered。 - -Forbidden evidence: - -- [ ] 真机联系人、聊天对象、群名、头像、昵称、手机号、微信号。 -- [ ] 二维码原图、完整分享卡片上下文、完整视频原片、完整系统日志。 -- [ ] 用真机 passed 覆盖 DevTools service port blocked 结论。 - -Blocked mapping: - -- [ ] 无真机、无法登录、无法扫码、无法触发分享菜单:`manual_ui_needed`。 -- [ ] 真机可以测 UI 但 payload 不可 inspect:对应 journey 可 `blocked` 或带未验证字段,不得写 payload passed。 - -## 4. 评审红线 - -- [ ] Readiness passed / static guard passed 只能说明静态结构和 no-side-effect 约束通过;不能写成 UI passed、DevTools smoke passed、share payload passed 或 real-device passed。 -- [ ] `DevTools app quit: completed` 只能说明 app-level quit 动作完成;不能写成 service port recovered。 -- [ ] CLI open timeout 后,即使 app 进程出现,也只能写 `open_timeout` 或后续端口状态;没有 listener 不能写 ready。 -- [ ] 端口 `LISTEN` 只能说明 service port 入口存在;不能替代项目打开、模拟器首屏、系统分享菜单、朋友圈菜单、payload、CloudBase attribution 或真机布局。 -- [ ] Blocked draft / dry-run / local schema checker passed 不能替代真实 UI evidence。 -- [ ] 任何证据若包含完整本机路径、cookie / token、完整进程命令行或真实用户数据,该层证据应退回重采。 -- [ ] 不允许为了拿分把 `not_run` 写成 `blocked`,也不允许把产品缺陷写成环境 blocked。 - -## 5. 人工介入请求模板 - -如果下一步需要用户介入,请只请求确认 UI 设置,不要求用户执行破坏性恢复动作。 - -```text -请在 WeChat DevTools UI 中帮忙确认以下设置,不需要清缓存、重装、改 AppID 或提交任何配置: -1. Settings > Security Settings > Service Port 是否开启? -2. 如果已开启,UI 显示的端口号是否为 9420?如果不是,请告知端口号。 -3. 当前打开的项目是否是本轮目标 worktree? -4. 是否同时打开了多个 DevTools 窗口或多个项目? -5. DevTools 是否有升级、登录、权限或安全弹窗阻止 service port? -6. 是否愿意在你确认后,由你手动重启 DevTools 或切换 Service Port 开关?未确认前我们不会执行任何恢复动作。 -``` - -## 6. 汇报口径 - -Status 应使用以下表达之一: - -- `Deep forensics checklist added; no GUI side effects run.` -- `Blocked at ; manual UI confirmation needed.` -- `Port listener ready, but DevTools UI / real-device evidence still not passed.` -- `Real-device alternative evidence collected, but service port remains blocked.` - -Files changed 只能列: - -- `harness/devtools-port-deep-forensics-checklist.md` - -Verification 至少包含: - -- [ ] `node harness/check-harness.mjs` -- [ ] `git diff --check` - -Top risks 至少提醒: - -- [ ] 9420 listener 仍可能缺失,导致 DevTools smoke 和 share payload inspect 继续 blocked。 -- [ ] 人工 UI 开关状态未确认前,不应继续假设 Service Port 已开启。 -- [ ] 真机可以补用户旅程证据,但不能证明 DevTools service port recovered。 diff --git a/harness/devtools-port-deep-forensics-product-brief.md b/harness/devtools-port-deep-forensics-product-brief.md deleted file mode 100644 index 1a95776..0000000 --- a/harness/devtools-port-deep-forensics-product-brief.md +++ /dev/null @@ -1,168 +0,0 @@ -# AA 组 DevTools Service Port 深层取证产品 Brief - -日期:2026-06-17 - -分支:`codex/iter-devtools-port-deep-forensics` - -工作目录:`/tmp/street-tasks-iter-worktrees/devtools-port-deep-forensics` - -角色:AA 组产品 agent - -## AA 目标 - -AA 轮的目标不是继续尝试恢复命令,而是把问题从“恢复命令失败”推进到“知道为什么 WeChat DevTools 声明了 `--ide-http-port 9420`,但本机没有可连接 listener”。 - -Z 组已经新增显式 opt-in `--app-quit-reopen`,实际运行后 app quit completed,但 CLI open 仍然 timed out,输出停在 `IDE may already started at port 9420, trying to connect`。恢复后状态仍是 9420 `ECONNREFUSED` / smoke `blocked`,所以当前不能写真实 DevTools、系统分享、朋友圈、单页模式、CloudBase attribution 或真机 journey passed。 - -AA 轮要定义后续只读深层取证应补齐哪些证据层,让下一轮能够判断 blocker 更像是用户设置未启用、陈旧 daemon/helper、DevTools 工具版本 bug、账号/UI 设置阻塞、端口/权限问题,还是其他环境状态。 - -## 当前已知事实 - -- DevTools CLI 可执行文件存在,前序诊断可调用到 CLI。 -- DevTools-like 进程曾声明 `--ide-http-port 9420`。 -- `127.0.0.1:9420` 和 `::1:9420` 仍拒绝连接,没有可用 smoke access。 -- `--app-quit-reopen` 完成 app-level quit 后,CLI open 仍 timeout,说明单纯重启 app 没有让 service port 进入 ready。 -- 当前 evidence 缺口仍是环境入口 blocker,而不是产品 journey 已经失败或通过。 - -## 需要补齐的只读证据层 - -### 1. Service port 开关和用户配置 - -目标:判断 DevTools UI 中的服务端口能力是否被禁用、未持久化、被策略覆盖,或当前用户配置不可读。 - -建议收集摘要: - -- 是否能在只读文件层看到 service port 相关配置项、端口号、启用状态或最近变更时间。 -- 是否存在多个配置来源互相覆盖,例如全局设置、项目设置、用户目录设置、workspace 设置。 -- 配置中声明的端口是否与进程参数 `--ide-http-port 9420` 一致。 -- 如果无法读取或定位配置,只记录 `service_port_config_unknown`,不要猜测 UI 已开启。 - -输出应脱敏,只写键名、布尔状态、端口号和文件类别;不要提交完整用户配置文件。 - -### 2. DevTools 用户数据目录 - -目标:判断 CLI open 是否连到了陈旧 user-data、错误 profile、损坏 profile,或与当前 App bundle 不匹配的数据目录。 - -建议收集摘要: - -- DevTools user-data 根目录候选路径是否存在。 -- 当前 CLI/App 进程使用的 user-data 目录路径摘要和修改时间。 -- 是否存在多个版本或多个 profile 目录,且最近活跃目录与当前进程不一致。 -- user-data 中是否有可脱敏的 service port、workspace、last session、crash recovery 线索。 - -禁止提交完整路径树、账号信息、缓存内容、storage、cookies、CloudBase 标识或项目历史。 - -### 3. 日志摘要 - -目标:确认 DevTools 或 CLI 是否明确记录了 service port 启动失败、端口绑定失败、权限失败、helper crash、profile lock、账号/安全设置阻塞等原因。 - -建议收集摘要: - -- 只读取最近一次 app quit/reopen 前后的日志窗口。 -- 抽取时间戳、组件名、错误类别、端口号、退出码或异常类型。 -- 重点检索 `ide-http-port`、`service port`、`listen`、`EADDRINUSE`、`EACCES`、`ECONNREFUSED`、`timeout`、`profile`、`lock`、`daemon`、`helper`、`crash`。 -- 输出最多保留短错误片段和计数,不提交原始日志文件。 - -日志若包含用户账号、项目路径、token、request id、完整 file id 或真实设备信息,必须只写脱敏摘要。 - -### 4. 进程树、daemon 和 helper - -目标:判断主 app quit 后是否仍有 daemon/helper/nwjs/node 子进程存活,导致 CLI 误以为 IDE 已启动,但服务端口实际没有起来。 - -建议收集摘要: - -- DevTools 主进程、CLI 进程、helper、daemon、renderer、node/nwjs 进程的父子关系。 -- 哪些进程声明 `--ide-http-port 9420`,哪些没有。 -- app quit completed 后是否仍存在孤儿 helper 或 daemon。 -- 进程启动时间是否早于本轮恢复命令,判断是否可能是 stale process。 - -只读取进程信息,不 kill、不 attach debugger、不采样内存、不转储命令行中的敏感参数。 - -### 5. 锁文件和 session 标记 - -目标:判断 CLI open timeout 是否由 profile lock、single-instance lock、workspace lock 或 crash recovery 状态导致。 - -建议收集摘要: - -- user-data 或 profile 目录下是否存在 lock、singleton、socket、pid、session restore、crash marker 类文件。 -- lock 文件修改时间是否早于 app quit/reopen,是否指向已不存在或仍存在的 PID。 -- 是否有多个 worktree/session 同时竞争同一 DevTools profile。 - -只记录文件类别、时间关系和 PID 是否存活,不删除 lock 文件、不 touch 文件、不修改 session 状态。 - -### 6. 实际 HTTP endpoint 探测 - -目标:区分“端口完全没 listener”和“端口有 listener 但 endpoint 不匹配、返回非预期、需要认证或卡住”。 - -建议收集摘要: - -- `127.0.0.1` 与 `::1` 的 TCP 连接状态。 -- 如果有 listener,记录最小 HTTP 结果:状态码、响应头关键字段、是否超时、是否非 DevTools 服务。 -- 探测常见只读 endpoint 时要有短超时,不能触发 open、compile、preview 或 project mutation。 -- 区分 `connection_refused`、`connect_timeout`、`http_timeout`、`unexpected_http_response`、`ready_like_response`。 - -当前已知状态是 `connection_refused`,下一轮只有在 listener 出现后才应进入 endpoint 细分。 - -### 7. 端口占用、防火墙和权限 - -目标:判断 9420 是否被其他进程占用、被系统策略拦截,或 DevTools 没有权限绑定本机端口。 - -建议收集摘要: - -- 9420 是否存在 LISTEN 进程;若存在,进程是否为 WeChat DevTools。 -- 是否存在短暂 bind 后退出的日志迹象。 -- 是否存在防火墙、网络过滤、企业安全软件或 macOS 权限提示相关线索。 -- 是否有 `EADDRINUSE`、`EACCES`、sandbox、quarantine、codesign、notarization 或 helper permission 错误摘要。 - -不要修改防火墙、隐私权限、系统设置或安全软件配置;只给出下一步人工排查建议。 - -## 本轮不做什么 - -- 不 kill DevTools、helper、daemon、node、nwjs 或其他用户进程。 -- 不改 WeChat DevTools 设置,不自动启用或关闭 service port。 -- 不删除、不移动、不清空 user-data、profile、缓存、lock、session、storage 或登录态。 -- 不运行 open/preview/compile/smoke/recovery 这类会改变 DevTools 状态的动作。 -- 不提交真实日志、完整进程列表、完整用户路径、截图、录屏、账号信息、CloudBase 标识或本地 result JSON。 -- 不把深层取证写成 DevTools ready、UI smoke passed、真机 passed 或产品 journey passed。 - -## 开发最小建议 - -后续如果要增强 inspect 脚本,建议只增加更具体的诊断分类和脱敏摘要,不把恢复动作塞进默认检查。 - -优先输出这些稳定分类: - -- `stale_daemon_declared_without_listener`:仍有 DevTools daemon/helper 或旧主进程声明 `--ide-http-port`,但端口没有 listener。 -- `service_port_disabled_or_unavailable`:配置或日志显示 service port 未启用、不可用,或 UI/账号策略阻止启用。 -- `open_timeout_after_app_quit`:app-level quit 已完成,但 CLI open 仍 timeout,说明恢复动作未建立可连接 IDE 服务。 -- `port_claimed_by_non_devtools_process`:9420 被非 DevTools 进程监听或占用。 -- `devtools_listener_unexpected_response`:端口有 listener,但 HTTP 响应不像 DevTools IDE 服务。 -- `profile_lock_or_singleton_blocked`:user-data/profile lock 指向仍存活或陈旧的 session,可能阻止新服务启动。 -- `version_or_bundle_mismatch`:CLI、App bundle、user-data 或 helper 版本摘要不一致。 - -每个分类至少应带四类字段:`observed`、`evidenceSummary`、`confidence`、`nextSafeAction`。`nextSafeAction` 只能建议人工 UI 检查、换端口、换 profile/机器或再次只读诊断;不能默认执行恢复。 - -## 如何评测 - -AA 轮本身不要求恢复 ready。评测重点是:即使 9420 仍 blocked,下一轮也能知道 blocker 更接近哪一类原因,而不是只看到“open timeout”。 - -可接受的评测结论应至少落到以下分叉之一: - -- 用户设置:service port UI/config 未启用、未持久化、被账号/安全设置限制,下一步是人工打开 DevTools 设置并复核。 -- 陈旧进程:主 app 已退出,但 daemon/helper/旧进程仍声明端口或持有 session,下一步是人工确认是否安全清理现场。 -- 工具版本 bug:App/CLI/helper 版本或日志显示已知异常,下一步是换 DevTools 版本、换机器或收集最小 bug report。 -- 账号/UI 设置阻塞:必须登录、必须通过 UI 安全提示、或企业策略阻止 service port,下一步由用户在 UI 中处理。 -- 端口/权限问题:9420 被占用、绑定失败、防火墙/权限拦截,下一步是换端口或人工检查系统权限。 -- 未知但可复核:各层均无定论,但有明确缺失层和下一条只读命令建议。 - -不能接受的评测写法: - -- “app quit completed,所以 DevTools 已恢复。” -- “进程声明了 `--ide-http-port 9420`,所以 service port 已开启。” -- “静态 readiness 通过,所以真实 UI smoke passed。” -- “没有 listener,但继续假设系统分享/朋友圈/CloudBase journey passed。” - -## Key hypothesis - -当前最强假设是:CLI/app 层能发现一个声明 `--ide-http-port 9420` 的 DevTools session 标记或旧进程状态,因此 open 流程误判 IDE 已启动并尝试连接;但实际负责绑定 IDE HTTP service 的 listener 没有启动、已崩溃、被配置禁用,或被 profile/session/权限状态阻塞。 - -AA 轮要帮助下一轮把这个假设拆开验证:先证明 listener 为什么没起来,再谈恢复 service port;service port ready 之后,才进入真实 DevTools/真机 evidence 采集。 diff --git a/harness/devtools-port-forensics-checklist.md b/harness/devtools-port-forensics-checklist.md deleted file mode 100644 index 02e848e..0000000 --- a/harness/devtools-port-forensics-checklist.md +++ /dev/null @@ -1,195 +0,0 @@ -# DevTools 端口只读 Forensics 清单 - -日期:2026-06-14 - -范围:用于 `codex/iter-devtools-port-forensics` 分支在 `/tmp/street-tasks-iter-worktrees/devtools-forensics` 上做 WeChat DevTools 服务端口只读排查。本清单只记录准备、进程、端口、版本、路径和 blocked 口径;不退出 DevTools、不打开或重启项目、不杀进程、不清缓存、不写用户配置、不提交原始敏感日志。 - -已知背景:M 组诊断到 `9420` service port blocked;N 组受控 `--quit-reopen` 后仍未恢复 `9420`。O 组目标是把后续只读 forensics 观察项和判定口径固定下来,避免继续把 DevTools 入口 blocked 和产品 smoke 通过混在一起。 - -## 0. 准备 / 基线 - -- [ ] 确认工作树、分支和提交。 - - ```bash - pwd - git branch --show-current - git rev-parse --short HEAD - git status --short - ``` - - 期望:工作目录为 `/tmp/street-tasks-iter-worktrees/devtools-forensics`,分支为 `codex/iter-devtools-port-forensics`。macOS 可能把 `/tmp` 显示为 `/private/tmp`,记录时仍写本轮约定 worktree 路径。 - -- [ ] 跑基础 harness,确认不是仓库基础状态异常。 - - ```bash - bash harness/init.sh - ``` - - 期望:JSON 检查和 harness 自检通过。若失败,先记录 baseline 异常,不继续把 DevTools 端口状态写成 `ready`。 - -- [ ] 明确只读边界。 - - 不执行 `quit`、`open --project`、`preview`、`upload`、清缓存、删除配置、修改 `project.private.config.json` 或 DevTools 设置。 - - 不使用 `kill`、`pkill`、`killall`、`launchctl kickstart`、`rm -rf` 或任何会改变 DevTools / 用户配置的命令。 - - 不把完整 `ps`、Console、Network、云端日志或 CLI stderr 原文提交;只写脱敏摘要和本地附件编号。 - -- [ ] 记录本轮 forensics 不是产品验证。 - - 端口 `ready` 只表示“DevTools service port 看起来可连接”。 - - 端口 `blocked` 只表示“DevTools service port 入口不可用或无法确认”。 - - 端口 forensics 结果不能写成产品 smoke 通过;只有真实 DevTools UI 操作或真机旅程执行并记录证据,才可证明用户可见流程通过。 - -## 1. 只读进程与端口检查 - -- [ ] 观察 DevTools-like 进程,不粘贴完整命令行。 - - ```bash - ps aux | rg -i "wechat|微信|devtools|ide-http-port" - ``` - - 摘要只记录:进程数量、是否像 WeChat DevTools、是否声明 `--ide-http-port`、声明的端口、是否出现多个不同 app 路径或多个项目路径。不要提交用户目录、完整启动参数、环境变量或账号信息。 - -- [ ] 检查目标端口是否监听。 - - ```bash - lsof -nP -iTCP:9420 -sTCP:LISTEN - nc -vz 127.0.0.1 9420 - nc -vz ::1 9420 - curl -sS --max-time 3 http://127.0.0.1:9420/ || true - ``` - - 摘要只记录:`LISTEN` 是否存在、IPv4 / IPv6 是否 connection refused、`curl` 是 refused、timeout、404、鉴权错误还是其他非业务响应。不要提交完整 HTTP body、header、cookie 或本机路径。 - -- [ ] 检查是否存在端口声明但无监听。 - - 若 `ps` 中出现 `--ide-http-port 9420`,但 `lsof -iTCP:9420 -sTCP:LISTEN` 无结果,并且 `nc` / `curl` 是 connection refused,则记录为“声明 `ide-http-port` 但无监听”。 - - 若有监听但监听进程不是 DevTools-like,记录为“端口被其他进程占用,归属未知”,不要直接写 `ready`。 - - 若监听端口不是 `9420`,但有其他 `--ide-http-port ` 声明,逐个做同样只读监听检查,并记录端口映射摘要。 - -- [ ] 检查是否多个 DevTools 实例。 - - 统计 DevTools-like 进程数量、app bundle 路径数量、声明端口数量和可能的项目路径数量。 - - 如果多个实例共享或声明不同端口,记录“多实例,端口归属需人工确认”。 - - 不把其他 worktree 或其他 DevTools 实例的可用端口误写成本分支 `ready`。 - -## 2. DevTools App / CLI 版本与路径摘要 - -- [ ] 记录 DevTools app 路径摘要。 - - ```bash - ls -ld /Applications/wechatwebdevtools.app - mdls -name kMDItemVersion /Applications/wechatwebdevtools.app 2>/dev/null || true - defaults read /Applications/wechatwebdevtools.app/Contents/Info CFBundleShortVersionString 2>/dev/null || true - defaults read /Applications/wechatwebdevtools.app/Contents/Info CFBundleVersion 2>/dev/null || true - ``` - - 可提交摘要:`appPath=`、`shortVersion=`、`bundleVersion=`。若 DevTools 安装在用户目录,只写 ``,不要写 `/Users//...`。 - -- [ ] 记录 CLI 路径和只读可用性。 - - ```bash - /Applications/wechatwebdevtools.app/Contents/MacOS/cli --help - ``` - - 可提交摘要:`cliPath=`、`cliHelp=available/unavailable`、`cliVersion=`。不要执行 `open`、`quit`、`preview` 或任何会改变 IDE 状态的 CLI 子命令。 - -- [ ] 记录版本无法确认时的原因。 - - App 不存在:写“默认 app 路径不存在”。 - - CLI 无法运行:写“CLI help unavailable,退出码或错误类型已脱敏”。 - - 没有 UI 权限或 DevTools 未运行:写“UI 版本无法确认”,不要伪造版本。 - -## 3. Blocked 字段记录 - -blocked / unknown / ready 摘要至少包含: - -- `forensicsAt`:日期时间和时区,使用 `2026-06-14` 当日记录。 -- `branch`:`codex/iter-devtools-port-forensics`。 -- `commit`:`git rev-parse --short HEAD`。 -- `worktree`:`/tmp/street-tasks-iter-worktrees/devtools-forensics`。 -- `baseline`:`bash harness/init.sh` 通过 / 失败摘要。 -- `devtoolsAppPath`:``、`` 或“无法确认”。 -- `devtoolsVersion`:版本号或“无法确认”。 -- `cliPath`:``、`` 或“无法确认”。 -- `cliHelp`:available / unavailable。 -- `devtoolsProcessCount`:DevTools-like 进程数量。 -- `multiInstanceSummary`:单实例 / 多实例 / 无法确认。 -- `ideHttpPortDeclarations`:声明端口列表,例如 `[9420]`。 -- `listenEvidence`:`lsof` / `nc` / `curl` 的脱敏结论。 -- `declaredButNotListening`:yes / no / unknown。 -- `status`:`ready` / `blocked` / `unknown`。 -- `actualResult`:最小错误摘要,例如 `9420 connection refused`、`declared ide-http-port but no listener`、`multiple instances ambiguous`。 -- `manualJourneyImpact`:哪些 DevTools UI / 真机 smoke journey 尚未执行。 -- `nextAction`:后续人工 UI 恢复建议。 -- `evidenceLocation`:本地附件编号或安全外部位置,不写原始绝对路径。 -- `redactionStatus`:已脱敏 / 待复核。 - -示例摘要: - -```text -forensicsAt: 2026-06-14 14:30 CST -branch: codex/iter-devtools-port-forensics -commit: -worktree: /tmp/street-tasks-iter-worktrees/devtools-forensics -baseline: harness init passed -devtoolsAppPath: -devtoolsVersion: -cliPath: -cliHelp: available -devtoolsProcessCount: 1 -multiInstanceSummary: single DevTools-like process observed -ideHttpPortDeclarations: [9420] -listenEvidence: 9420 no LISTEN; IPv4 and IPv6 connection refused -declaredButNotListening: yes -status: blocked -actualResult: DevTools process declares --ide-http-port 9420 but no service listener is reachable -manualJourneyImpact: DevTools compile, map smoke, publish smoke, detail trust smoke, and real-device preview not executed -nextAction: Human operator should inspect DevTools UI service-port setting and perform normal UI recovery before rerunning smoke -evidenceLocation: local attachment F-port-01; raw logs not committed -redactionStatus: sanitized summary only -``` - -## 4. 判定规则 - -- `ready` - - 目标端口或明确声明的 DevTools service port 有 `LISTEN`。 - - `nc` / `curl` 不再是 connection refused;返回 404、鉴权错误或 DevTools 非业务响应均可说明端口入口存在。 - - 只有一个可归属当前 DevTools 实例的端口,或多实例归属已通过只读证据清楚区分。 - - `bash harness/init.sh` 通过。 - - 备注:`ready` 只代表端口入口可用,不代表 DevTools 已打开当前项目,也不代表任何产品旅程通过。 - -- `blocked` - - `ps` 声明 `--ide-http-port 9420`,但 `lsof` 无监听,`nc` / `curl` connection refused。 - - DevTools-like 进程不存在,且 CLI / app 路径无法确认到可用入口。 - - 端口被其他进程占用,无法确认是 DevTools service port。 - - 多个 DevTools 实例或多个端口冲突,无法把可用端口归属到本 worktree。 - - baseline 失败且会影响 DevTools 入口判断。 - -- `unknown` - - `ps`、`lsof`、`nc` 或 `curl` 权限不足或输出不完整。 - - 有监听但响应无法判断是否 DevTools service port。 - - DevTools 版本、CLI 路径或端口声明缺失,且只读检查不足以确认 blocked。 - - 多实例信息不足,但也没有明确 connection refused 或占用证据。 - -## 5. 脱敏规则 - -- 不提交真实 AppID、openId、unionId、头像 URL、昵称、手机号、精确经纬度、CloudBase 环境 ID、requestId、完整 `cloud://`、token、cookie、二维码、个人设备标识或本机用户路径。 -- 不提交完整 `ps aux`、CLI stderr、Console、Network、云函数日志、数据库截图或 DevTools 导出文件。 -- 本清单允许写当前测试 worktree 路径 `/tmp/street-tasks-iter-worktrees/devtools-forensics`;其他本机路径泛化为 ``、``、`` 或 ``。 -- 进程和端口输出只写最小结论:进程数量、声明端口、是否监听、connection refused / timeout / non-business response。 -- 真实截图、录屏、原始日志和命令完整输出只放 ignored 本地附件目录或外部安全位置;可提交内容只保留摘要和附件编号。 -- 写入可提交报告前运行并人工复核: - - ```bash - git status --short --ignored - git diff -- harness '*.md' '*.json' - rg --no-ignore -n -i "(api[_-]?key|secret|token|password|passwd|pwd|private[_-]?key|session|cookie|authorization|bearer|access[_-]?token|refresh[_-]?token|client[_-]?secret|appsecret|wx[0-9a-f]{16,}|sk-[A-Za-z0-9_-]{20,}|AKIA[0-9A-Z]{16})" . - ``` - - 期望:真实附件和 local 结果仍是 ignored;secret scan 没有新增真实敏感值。若命中的是规则说明,也要人工确认不是实际凭据。 - -## 6. 后续人工 UI 恢复建议 - -O 组不执行恢复动作,只把建议交给有本机 UI 权限的执行者: - -- 打开微信开发者工具 UI,进入“设置 -> 安全设置”,确认“服务端口”开启;若 UI 显示关闭,手动开启后重新做只读端口检查。 -- 确认当前只保留必要的 DevTools 实例;如果多个项目或多个版本同时运行,先人工识别当前项目和端口归属。 -- 正常退出整个 DevTools app 后重开,不只关闭模拟器窗口;恢复前保存必要的脱敏 blocked 摘要。 -- 重新打开目标 worktree 后,先跑端口 access 检查,再做 DevTools 编译、地图首屏、发布状态、详情信任动作和真机预览 smoke。 -- 若 `9420` 长期 blocked,可人工切换服务端口、升级或重装 DevTools、换机器验证,或改用 UI 手测路径记录 CLI blocked。 -- 恢复后仍需按真实 DevTools UI / 真机旅程记录 `passed`、`failed`、`blocked` 或 `not_covered`;端口恢复本身不能替代产品 smoke 证据。 diff --git a/harness/devtools-port-forensics-product-brief.md b/harness/devtools-port-forensics-product-brief.md deleted file mode 100644 index 20d133e..0000000 --- a/harness/devtools-port-forensics-product-brief.md +++ /dev/null @@ -1,130 +0,0 @@ -# O 组 DevTools 端口 Forensics 产品简报 - -日期:2026-06-14 - -分支:`codex/iter-devtools-port-forensics` - -工作目录:`/tmp/street-tasks-iter-worktrees/devtools-forensics` - -对象:继续判断 WeChat DevTools smoke blocker 的产品、QA 和开发 agent。 - -## 问题 - -M 组已确认 DevTools smoke access 被 9420 服务端口阻塞:存在 DevTools-like 进程声明 `--ide-http-port 9420`,但 `127.0.0.1:9420` 和 `::1:9420` 都返回 `ECONNREFUSED`。N 组随后新增受控恢复脚本并执行过 `--quit-reopen`,但 CLI `quit` 和 `open` 均 timeout,恢复后仍 blocked。 - -O 组不再重复恢复动作,也不继续扩大 smoke 或业务验证范围。本轮问题是:当前 DevTools 环境里,端口声明、实际监听、CLI 可用性、应用版本和进程状态之间是什么关系;这些关系能支持哪些判断,不能支持哪些判断。 - -2026-06-14 的只读 forensics 摘要: - -- `scripts/check-devtools-smoke-access.mjs --project /tmp/street-tasks-iter-worktrees/devtools-forensics --port 9420 --timeout-ms 5000` 返回 `status: blocked`。 -- DevTools CLI 存在且可执行,来源为标准 macOS 应用包:`/Applications/wechatwebdevtools.app/Contents/MacOS/cli`。 -- WeChat DevTools 应用版本为 `2.01.2510290`,bundle version 为 `4240.111`。 -- 进程列表中有 1 个 DevTools-like 主进程声明 `--ide-http-port 9420`。 -- `lsof -nP -iTCP:9420 -sTCP:LISTEN` 没有监听结果。 -- `nc -vz -G 3 127.0.0.1 9420` 和 `nc -vz -G 3 ::1 9420` 都是 connection refused。 - -当前最稳妥的解释是:DevTools 进程仍在,但声明的 IDE HTTP 服务没有在 9420 建立可连接监听。CLI 可执行并不等于服务端口 ready;进程声明端口也不等于端口正在监听。 - -## 用户价值 - -真实 DevTools 或真机 smoke 仍是判断地图首屏、发布状态、定位授权、详情信任区、评论和图片链路的必要证据。O 组的价值不是让这些用户旅程通过,而是降低误判成本: - -- 让后续执行者知道当前 blocker 是 DevTools 环境入口问题,不是已复现的产品缺陷。 -- 把“CLI 可执行”“进程存在”“端口声明”“端口监听”拆开,避免任一单点被误写成 smoke ready。 -- 给人工 UI 恢复、换端口或换机器提供可复核判断依据。 -- 避免继续运行会改变环境的动作,保护已有 DevTools 现场、登录态和其他 worktree。 - -## 范围内 - -- 新增本产品简报,定义 O 组只读 forensics 口径。 -- 读取 harness、已有 M/N 组产品简报和相关脚本,理解前序证据。 -- 运行无副作用命令确认当前状态:`bash harness/init.sh`、默认模式的 `check-devtools-smoke-access`、`ps`、`lsof`、`nc`、Info.plist 版本读取。 -- 解释 DevTools 端口声明、监听、CLI、应用版本和 blocked 判断之间的关系。 -- 明确后续人工 UI 恢复、换端口、换机器或重新跑只读诊断的判断分叉。 - -## 非目标 - -- O 组不是产品功能迭代,不新增或改变任何用户可见能力。 -- O 组不是真实 DevTools smoke,不证明地图、发布、详情、评论、图片、云端或跨用户路径通过。 -- O 组不是恢复端口,不重复 N 组的 `--quit-reopen`,不执行 CLI open/preview/quit。 -- 不修改业务代码、页面、云函数、脚本、配置或用户本地 DevTools 设置。 -- 不清理缓存、不杀进程、不改服务端口、不改 AppID、不改 `project.private.config.json`。 -- 不提交业务代码或本地配置变更;本轮 harness 文档和只读诊断脚本可以作为可审计工件提交。 - -## 只读安全边界 - -允许动作: - -- 读取项目文档、harness 文件、M/N 组简报和脚本源码。 -- 运行 baseline:`bash harness/init.sh`。 -- 运行默认无 launch side effects 的端口诊断脚本;必须避免 `--attempt-open`。 -- 运行 `ps`、`lsof`、`nc`、Info.plist 读取等只读探测。 -- 只记录脱敏摘要,不提交完整进程输出、用户目录、日志、截图、二维码或账号信息。 - -禁止动作: - -- 不运行 `scripts/recover-devtools-service-port.mjs --quit-reopen`。 -- 不运行 DevTools CLI `quit`、`open`、`preview` 或任何会启动、关闭、重载 IDE 的命令。 -- 不杀 DevTools、Chrome、node、nwjs 或其他用户进程。 -- 不清 DevTools 编译缓存、全局缓存、登录态、storage、CloudBase 数据或本地配置。 -- 不编辑业务代码或脚本。 - -若需要越过这些边界,必须把本轮结论停在 `blocked`,交给有 UI 权限且已确认风险的执行者处理。 - -## 成功标准 - -本 brief 完成后,后续 agent 应能复述并验证以下结论: - -- 当前 blocked 不是因为缺少 CLI:CLI 可执行存在。 -- 当前 blocked 不是因为没有 DevTools 进程:存在 DevTools-like 进程声明 `--ide-http-port 9420`。 -- 当前 blocked 的关键矛盾是:声明了 9420,但 9420 没有监听,IPv4/IPv6 都拒绝连接。 -- 当前只读 forensics 不等于 smoke ready,也不等于任何产品旅程 passed。 -- 下一步应由人工 UI 恢复、换端口、换机器或重新打开 DevTools 后,再用同一诊断口径复核。 - -真正的成功不是写完文档,而是让下一位执行者不会把 DevTools 环境 blocker 误当成产品通过或产品失败。 - -## 失败/Blocked 口径 - -以下情况均应记录为 `blocked`: - -- `9420` 没有监听,或 `127.0.0.1` / `::1` 仍为 `ECONNREFUSED`。 -- DevTools-like 进程存在但只声明端口,无法证明 HTTP 服务实际 ready。 -- CLI 可执行存在,但无法通过只读诊断确认端口 ready。 -- 需要执行 quit/open、杀进程、清缓存、改配置或 UI 操作才能继续判断。 -- DevTools UI、服务端口开关、当前打开项目、基础库版本或模拟器状态无法被只读方式确认。 - -建议记录格式: - -```text -status: blocked -type: DevTools port forensics blocked -date: 2026-06-14 -branch: codex/iter-devtools-port-forensics -worktree: /tmp/street-tasks-iter-worktrees/devtools-forensics -devtoolsVersion: 2.01.2510290 / 4240.111 -cliEvidence: CLI executable exists in standard macOS app bundle -processEvidence: one DevTools-like process declares --ide-http-port 9420 -listenEvidence: no listener on 9420; 127.0.0.1 and ::1 connection refused -scopeBoundary: read-only forensics only; no quit/open/kill/cache/config action -impact: real DevTools smoke and product journey judgment remain unavailable -nextAction: manual UI recovery, alternate port, alternate machine, then rerun read-only diagnostics -``` - -不要写成 `failed`,除非已经能进入真实产品旅程并复现用户可见缺陷。不要写成 `passed`,因为没有 DevTools smoke 或真机旅程证据。 - -## 如何避免误判为产品通过 - -- 把状态分层:`cli_available`、`process_declares_port`、`port_listening`、`devtools_smoke_ready`、`product_journey_passed` 不能互相替代。 -- `process_declares_port=true` 只说明启动参数存在,不说明服务端口可连接。 -- `cli_available=true` 只说明工具入口存在,不说明 CLI 可以连接 IDE。 -- `harness/init.sh` 通过只说明 JSON 和 harness 自检通过,不说明 DevTools、模拟器或真机通过。 -- O 组 brief 只能升级“环境 blocker 可解释性”,不能升级任何 feature status。 -- 没有具体 DevTools/真机步骤、版本、环境、实际观察和脱敏证据时,所有用户旅程继续保持未验证或 blocked。 - -## 建议下一步 - -首选由有本机 UI 权限的人在 WeChat DevTools 中手动确认“设置 -> 安全设置 -> 服务端口”状态,并明确当前打开项目是否是 `/tmp/street-tasks-iter-worktrees/devtools-forensics` 或目标 smoke worktree。若 UI 中服务端口显示异常,人工恢复后再运行默认模式的 `scripts/check-devtools-smoke-access.mjs` 复核。 - -若 UI 显示端口已开启但 9420 仍拒绝连接,建议换一个明确记录的新端口或换机器验证,避免继续在同一个疑似卡死的 IDE 会话中重复 quit/open。换端口或换机器后,仍必须分别记录 CLI 可用性、进程声明、监听状态和真实 smoke 结果。 - -端口 ready 后才进入真实 smoke。最小 smoke 至少应覆盖打开项目、编译、地图首屏、发布页只读检查和任一详情页只读检查;定位授权、图片上传、发布闭环、评论、云端和跨用户路径仍需单独手测证据。 diff --git a/harness/devtools-readiness-checklist.md b/harness/devtools-readiness-checklist.md deleted file mode 100644 index 1ba0d67..0000000 --- a/harness/devtools-readiness-checklist.md +++ /dev/null @@ -1,337 +0,0 @@ -# DevTools / 真机发布流程手测清单 - -日期:2026-06-14 - -范围:用于 `codex/iter-devtools-readiness` 候选版在微信 DevTools 与真机环境中的发布前人工验收。此文档是待执行清单,不代表任何项目已完成手测。 - -## 0. 测试环境记录 - -每轮测试开始前先补齐以下信息;同一提交在不同设备、基础库或网络下测试时,复制一份记录。 - -| 项目 | 记录 | -| --- | --- | -| 测试人 | | -| 测试日期 / 时间 | | -| 分支 | `codex/iter-devtools-readiness` | -| 提交 SHA | | -| 微信 DevTools 版本 | | -| 调试基础库版本 | | -| 运行环境 | DevTools / 真机 / 真机预览 / 真机调试 | -| 机型与系统 | | -| 屏幕宽度 / 是否窄屏 | | -| 微信版本 | | -| 网络环境 | Wi-Fi / 4G / 5G / 弱网 / 断网 | -| 定位权限初始状态 | 未询问 / 已允许 / 已拒绝 / 系统定位关闭 | -| 登录状态 | 游客 / 已登录 | -| CloudBase 环境 ID | | -| 云函数部署状态 | 未部署 / 已部署 / 版本或提交 | -| Storage 部署状态 | 未配置 / 已配置 / 权限规则 | -| 数据库集合状态 | posts / comments / users / 其他集合是否存在 | -| 本地 Storage 状态 | 首次打开 / 保留历史数据 / 已清空 | -| 备注 | | - -## 1. 通用执行与证据规范 - -- [ ] 每项测试前确认正在运行的分支、提交 SHA 和环境记录一致。 - - 通过标准:DevTools 项目路径、Git 提交、基础库版本与记录表一致。 - - 失败证据:记录实际路径、实际 SHA、截图 DevTools 项目信息或终端 `git rev-parse --short HEAD` 输出。 -- [ ] 失败项必须记录“复现步骤、期望结果、实际结果、环境、证据链接或文件名”。 - - 通过标准:任何失败都能被下一位测试者按步骤复现或判断不可复现。 - - 失败证据:截图、录屏、控制台报错、Network 面板请求、云函数日志、数据库记录截图至少保留一种。 -- [ ] 对视觉问题记录具体设备和屏宽。 - - 通过标准:能区分是通用样式问题、窄屏问题、安全区问题还是单一机型问题。 - - 失败证据:全屏截图,必要时附带局部放大图和当前页面滚动位置。 -- [ ] 对交互问题记录触发前后的页面状态。 - - 通过标准:能看出点击前按钮状态、点击后 loading / 禁用 / 跳转 / toast / 弹窗变化。 - - 失败证据:连续截图或 10 秒以内短录屏。 - -## 2. DevTools 编译与控制台检查 - -- [ ] 使用微信 DevTools 打开项目并完成一次普通编译。 - - 通过标准:编译成功,首屏进入小程序,不出现阻断运行的编译错误。 - - 失败证据:DevTools 编译面板截图,复制首条错误和相关文件路径。 -- [ ] 检查 Console 面板首屏日志。 - - 通过标准:没有未捕获异常、Promise rejection、组件找不到、页面路径找不到、权限配置缺失等错误。 - - 失败证据:Console 截图,复制完整错误栈。 -- [ ] 切换调试基础库到本轮目标版本后重新编译。 - - 通过标准:页面表现与默认基础库一致,没有 API 兼容性错误。 - - 失败证据:记录基础库版本、错误栈和受影响页面。 -- [ ] 执行“清缓存并编译”后进入地图页、发布页、详情页。 - - 通过标准:清空本地缓存后种子数据、游客态和页面跳转仍可用。 - - 失败证据:记录清缓存类型、首个异常页面和 Console 输出。 -- [ ] 打开 Network / 云开发调试面板观察图片、评论、云函数请求。 - - 通过标准:成功请求有明确返回;失败请求有用户可理解的错误提示,不静默卡住。 - - 失败证据:请求详情截图,包含 URL / 云函数名、状态码、返回体或错误码。 - -## 3. 地图页 - -### 3.1 首屏与基础布局 - -- [ ] 首次进入地图页。 - - 通过标准:地图、顶部信息、附近任务列表入口、marker 和首屏任务信息可见;没有空白闪屏长期停留。 - - 失败证据:首屏截图,记录停留时长和 Console 错误。 -- [ ] 使用保留本地数据再次进入地图页。 - - 通过标准:历史发布或 mock 任务正常显示,状态和距离不出现明显异常。 - - 失败证据:截图并记录本地 Storage 是否清空。 -- [ ] 在窄屏设备或 DevTools 小屏模拟器检查首屏。 - - 通过标准:标题、距离、分类、按钮不重叠;可点击区域不贴边。 - - 失败证据:全屏截图,标出重叠或裁切区域。 - -### 3.2 列表抽屉 - -- [ ] 打开和关闭列表抽屉。 - - 通过标准:抽屉动画稳定,背景地图仍可识别,关闭后页面可继续操作。 - - 失败证据:短录屏,记录是否出现蒙层残留或滚动锁死。 -- [ ] 列表抽屉滚动到底部。 - - 通过标准:最后一条任务不被安全区或底部区域遮挡。 - - 失败证据:底部截图,记录机型是否带 Home 指示条。 -- [ ] 列表任务为空或全部隐藏 / 过期时检查空态。 - - 通过标准:空态文案清楚,不显示不可点击的残留任务卡片。 - - 失败证据:截图和触发数据条件。 - -### 3.3 Marker 与列表进入详情 - -- [ ] 点击地图 marker 进入任务详情。 - - 通过标准:跳转到正确 post id 的详情页,返回后地图状态可继续操作。 - - 失败证据:录屏,记录 marker 对应标题和详情页标题。 -- [ ] 从列表任务卡片进入详情。 - - 通过标准:进入同一任务详情,距离、状态、图片数量与列表一致。 - - 失败证据:列表截图、详情截图和 post id。 -- [ ] 快速连续点击多个 marker。 - - 通过标准:不会重复打开多个详情页或进入错误任务。 - - 失败证据:录屏和页面栈表现。 - -### 3.4 定位允许 / 拒绝 / 超时 - -- [ ] 首次定位弹窗选择允许。 - - 通过标准:地图中心更新到当前位置附近,附近任务距离刷新,没有权限错误残留。 - - 失败证据:授权弹窗后录屏,记录定位结果或错误码。 -- [ ] 首次定位弹窗选择拒绝。 - - 通过标准:页面保留默认中心或可浏览状态,并出现可理解的定位失败 / 手动继续提示。 - - 失败证据:拒绝后截图和 Console 输出。 -- [ ] 系统定位关闭或模拟定位超时。 - - 通过标准:页面不无限 loading;重试入口可见且可再次触发定位。 - - 失败证据:等待时长、错误提示截图、系统定位状态。 -- [ ] 从设置重新允许定位后回到小程序。 - - 通过标准:重新定位成功或提示用户重试,不需要重启小程序才能恢复。 - - 失败证据:设置前后录屏。 - -### 3.5 长文案与图片卡片视觉 - -- [ ] 地图列表中展示长标题任务。 - - 通过标准:长标题自然换行或省略,不挤压分类、距离和操作区域。 - - 失败证据:任务卡片截图,记录标题长度。 -- [ ] 地图列表中展示长正文任务。 - - 通过标准:正文摘要不会撑破卡片,不遮挡下一条任务。 - - 失败证据:滚动截图。 -- [ ] 地图列表中展示 0 / 1 / 多图任务。 - - 通过标准:无图无空洞,单图和多图缩略图比例稳定,加载失败不影响文字阅读。 - - 失败证据:图片加载前后截图或失败占位截图。 - -## 4. 发布页 - -### 4.1 游客与登录态 - -- [ ] 游客进入发布页。 - - 通过标准:登录提示、位置确认状态和底部主按钮状态一致,不能绕过登录发布。 - - 失败证据:首屏截图和点击主按钮后的表现。 -- [ ] 已登录用户进入发布页。 - - 通过标准:登录提示消失或降级,表单可编辑,底部主按钮按表单和位置状态变化。 - - 失败证据:登录态截图,记录登录方式。 -- [ ] 登录状态切换后返回发布页。 - - 通过标准:页面状态刷新,不沿用游客禁用态或旧用户信息。 - - 失败证据:切换前后录屏。 - -### 4.2 必填校验与发布状态 - -- [ ] 空表单直接点击主按钮。 - - 通过标准:提示第一个需要补齐的字段,不提交空任务。 - - 失败证据:点击后 toast / 文案截图。 -- [ ] 逐项填写标题、详情、分类、地点、有效期。 - - 通过标准:底部按钮说明同步更新,没有状态滞后。 - - 失败证据:逐项截图或录屏。 -- [ ] 输入超长标题、超长正文、超长地点。 - - 通过标准:计数、换行、省略和按钮区域稳定,不出现横向滚动或遮挡。 - - 失败证据:截图并记录输入长度。 -- [ ] 切换分类,尤其失物招领的 lost / found 子类型。 - - 通过标准:分类文案、子类型选择和最终详情展示一致。 - - 失败证据:发布页与详情页截图。 - -### 4.3 定位确认与失败重试 - -- [ ] 点击使用当前位置并允许授权。 - - 通过标准:定位中有 loading,成功后显示已确认位置,允许继续发布。 - - 失败证据:录屏,记录定位耗时。 -- [ ] 定位授权拒绝后点击重试。 - - 通过标准:重试路径清楚,不出现死循环 loading;必要时引导去设置。 - - 失败证据:拒绝后和重试后截图。 -- [ ] 弱网或定位超时场景下点击重试。 - - 通过标准:失败提示可读,重试不会重复提交或清空已填表单。 - - 失败证据:网络状态、错误提示和表单保留情况。 -- [ ] 修改地点补充后再次确认位置。 - - 通过标准:位置摘要与地点补充关系清楚,不把补充地址误当经纬度来源。 - - 失败证据:位置卡片截图。 - -### 4.4 键盘与安全区 - -- [ ] 标题、详情、地点输入时弹起键盘。 - - 通过标准:当前输入框、字数、底部按钮都可见或可滚动到可见。 - - 失败证据:键盘弹起截图,记录机型。 -- [ ] 在带 Home 指示条设备上滚动到页面底部。 - - 通过标准:最后一个字段和底部主按钮不被系统安全区遮挡。 - - 失败证据:底部截图。 -- [ ] 反复聚焦 / 失焦输入框。 - - 通过标准:页面滚动位置可恢复,底部固定区域不跳动到遮挡内容。 - - 失败证据:短录屏。 - -### 4.5 图片数量、过大与上传失败 - -- [ ] 添加 1 张图片。 - - 通过标准:缩略图出现,删除按钮可点击,发布后详情可见。 - - 失败证据:发布页和详情页截图。 -- [ ] 添加到图片数量上限。 - - 通过标准:上传入口按预期隐藏或禁用,缩略图网格不变形。 - - 失败证据:上限状态截图。 -- [ ] 尝试选择超过上限的图片。 - - 通过标准:只保留允许数量,并给出清楚提示或系统选择限制。 - - 失败证据:选择前后截图。 -- [ ] 选择过大图片或异常格式图片。 - - 通过标准:失败时有提示,已填表单不丢失,图片区域不留下损坏占位。 - - 失败证据:文件大小、格式、错误提示截图。 -- [ ] 模拟上传失败或 Storage 未配置。 - - 通过标准:发布流程不静默成功;用户能看到上传失败原因或重试提示。 - - 失败证据:Network / 云开发请求截图、页面提示、云端错误码。 - -### 4.6 发布提交与跳转 - -- [ ] 填齐表单后发布。 - - 通过标准:按钮进入提交中状态,提交成功后跳转到新任务详情页。 - - 失败证据:录屏,记录新 post id 或详情标题。 -- [ ] 发布过程中快速重复点击主按钮。 - - 通过标准:只创建一条任务,不重复提交。 - - 失败证据:录屏和数据列表 / 数据库记录。 -- [ ] 发布成功后返回地图页。 - - 通过标准:新任务在地图或列表中可见,状态、图片、距离和标题正确。 - - 失败证据:详情页返回地图后的截图。 - -## 5. 详情页 - -### 5.1 TrustInsight 各状态 - -- [ ] 打开普通 active 任务详情。 - - 通过标准:TrustInsight 展示确认、过时、举报、评论等信号,文案层级清楚。 - - 失败证据:面板截图。 -- [ ] 打开高确认数任务。 - - 通过标准:good / 正向状态明确,数字变化不导致布局跳动。 - - 失败证据:操作前后截图。 -- [ ] 打开过时数较高或已 stale 任务。 - - 通过标准:warn 状态能提示谨慎判断,但不遮挡详情正文和信任动作。 - - 失败证据:面板和动作区截图。 -- [ ] 打开举报数较高任务。 - - 通过标准:danger 状态可读,用户能理解风险来源。 - - 失败证据:面板截图和数据条件。 -- [ ] 打开 resolved 任务。 - - 通过标准:显示已解决语义,信任动作变为只读或不可操作。 - - 失败证据:详情页截图和点击动作结果。 -- [ ] 打开 expired 任务。 - - 通过标准:显示已过期语义,发布者信息、评论、历史信号仍可阅读;不可继续执行信任动作。 - - 失败证据:详情页截图和点击动作结果。 - -### 5.2 评论入口与弹窗 - -- [ ] 点击评论区标题右侧入口。 - - 通过标准:评论弹窗打开,输入框、取消、发送、字数统计可见。 - - 失败证据:弹窗截图。 -- [ ] 点击悬浮评论按钮。 - - 通过标准:打开同一评论弹窗,不遮挡评论正文或底部安全区。 - - 失败证据:入口位置截图和弹窗截图。 -- [ ] 游客态点击评论入口。 - - 通过标准:触发登录提示或登录流程,不允许匿名静默提交评论。 - - 失败证据:点击后截图。 -- [ ] 输入空评论、超长评论、正常评论并发送。 - - 通过标准:空评论不可发送,超长评论有约束,正常评论发送后列表刷新。 - - 失败证据:三种状态截图和评论列表刷新结果。 -- [ ] 键盘弹起时检查评论弹窗。 - - 通过标准:发送按钮和字数统计不被键盘遮挡,关闭键盘后弹窗布局恢复。 - - 失败证据:键盘弹起和收起截图。 - -### 5.3 信任动作刷新 - -- [ ] 点击确认。 - - 通过标准:确认数加一,按钮变为已确认或不可重复点击,TrustInsight 同步刷新。 - - 失败证据:点击前后截图。 -- [ ] 点击过时。 - - 通过标准:过时数加一,达到阈值时状态变为 stale;列表和详情状态一致。 - - 失败证据:点击前后截图、返回列表截图。 -- [ ] 点击举报。 - - 通过标准:举报数加一,达到阈值时任务隐藏;普通列表不再显示隐藏任务。 - - 失败证据:点击前后截图、地图列表截图。 -- [ ] 同一用户重复执行同一信任动作。 - - 通过标准:重复动作被阻止,计数不重复增加。 - - 失败证据:两次点击录屏和计数变化。 -- [ ] 信任动作失败或网络断开。 - - 通过标准:页面提示失败并保留可恢复状态,不出现本地计数和云端计数永久不一致。 - - 失败证据:断网状态、错误提示、恢复网络后的计数截图。 - -### 5.4 窄屏换行与只读状态 - -- [ ] 320px 级别窄屏打开详情页。 - - 通过标准:标题、标签、时间、地点、距离、TrustInsight 和按钮不互相遮挡。 - - 失败证据:全屏长截图。 -- [ ] 长标题、长正文、长地点、长评论昵称。 - - 通过标准:文本自然换行或省略,图片和元信息不被覆盖。 - - 失败证据:局部截图,记录文案长度。 -- [ ] resolved / expired / hidden 相关只读状态。 - - 通过标准:用户能看清历史信息,但不会看到可点击的确认、过时、举报或发送评论入口。 - - 失败证据:页面截图和点击结果。 - -## 6. 跨用户与云端路径 - -- [ ] 用户 A 发布带图片任务,用户 B 打开地图和详情。 - - 通过标准:图片跨用户可见,缩略图和详情大图均可加载。 - - 失败证据:A 发布截图、B 详情截图、Storage fileID 或 URL。 -- [ ] 用户 A 发布评论,用户 B 刷新详情。 - - 通过标准:评论持久化并跨用户可见,评论数量与列表一致。 - - 失败证据:A 评论后截图、B 刷新后截图、comments 集合记录。 -- [ ] 用户 A 执行信任动作,用户 B 打开同一任务。 - - 通过标准:确认 / 过时 / 举报计数和 TrustInsight 状态跨用户同步。 - - 失败证据:A 操作后截图、B 详情截图、posts 集合记录。 -- [ ] CloudBase posts 集合缺失。 - - 通过标准:页面出现可理解错误提示,不空白、不无限 loading。 - - 失败证据:集合状态截图、页面提示、Console / 云函数错误。 -- [ ] CloudBase comments 集合缺失。 - - 通过标准:评论入口或评论列表提示异常,不影响任务详情主体阅读。 - - 失败证据:集合状态截图、评论区截图、错误日志。 -- [ ] CloudBase Storage 未配置或权限拒绝。 - - 通过标准:图片上传 / 读取失败时有提示,文本任务仍可按设计处理。 - - 失败证据:Storage 配置截图、上传错误、页面提示。 -- [ ] 云函数未部署或版本不一致。 - - 通过标准:相关操作失败时能提示用户,并在 Console / 日志中定位具体云函数。 - - 失败证据:云函数列表截图、调用错误、页面提示。 -- [ ] 弱网下刷新详情和提交评论。 - - 通过标准:loading、失败、重试状态清楚,不重复创建评论或信任动作。 - - 失败证据:网络条件、录屏、云端记录数量。 - -## 7. 最终记录模板 - -### 本轮结论 - -- 结论:通过 / 有条件通过 / 不通过 / 阻塞 -- 测试范围: -- 未覆盖范围: -- 阻塞问题: -- 可接受风险: -- 建议下一步: - -### 失败项记录 - -| ID | 页面 / 场景 | 环境 | 复现步骤 | 期望结果 | 实际结果 | 严重级别 | 证据 | -| --- | --- | --- | --- | --- | --- | --- | --- | -| DR-001 | | | | | | P0 / P1 / P2 / P3 | | - -### 严重级别建议 - -- P0:阻断编译、首屏不可用、发布 / 详情核心路径完全不可用、数据丢失或重复创建严重。 -- P1:核心路径在常见设备或常见权限状态下失败,例如定位拒绝后无法恢复、发布成功不跳详情、跨用户图片不可见。 -- P2:明显视觉 / 交互问题但存在绕行方式,例如窄屏遮挡、键盘遮住按钮、失败提示不够清楚。 -- P3:轻微文案、间距、单一机型适配问题,不影响主要任务完成。 diff --git a/harness/devtools-readiness-product-brief.md b/harness/devtools-readiness-product-brief.md deleted file mode 100644 index c3f9cd0..0000000 --- a/harness/devtools-readiness-product-brief.md +++ /dev/null @@ -1,166 +0,0 @@ -# H 组 DevTools/真机手测产品准入简报 - -日期:2026-06-14 - -分支:`codex/iter-devtools-readiness` - -对象:后续执行 WeChat DevTools 或真机手测的 agent。 - -## 1. H 组目标 - -- 明确候选版进入 DevTools/真机手测前的产品准入条件。 -- 规定发布、地图、详情、TrustInsight、图片和云端路径的必测旅程。 -- 统一“待验证”“通过”“阻塞”“失败”的证据口径,避免把自动检查或工具超时误判为用户旅程通过。 -- 给出发布准入结论模板,方便后续 agent 直接记录结果。 - -## 2. H 组非目标 - -- 不新增功能,不修改发布状态机、详情 TrustInsight、评论入口、地图列表或存储逻辑。 -- 不用自动脚本替代 WeChat DevTools 或真机手测。 -- 不把 DevTools CLI、云函数、Storage 的环境问题当作产品功能已通过。 -- 不提交代码,不调整其它 harness 文件。 - -## 3. 进入手测前必须满足的自动检查 - -以下检查全部通过后,才建议开始 DevTools/真机手测: - -1. `node --check pages/publish/publish.js` -2. `node --check pages/publish/publish-state.js` -3. `node --check pages/detail/detail.js` -4. `node --check utils/format.js` -5. `node --no-warnings scripts/check-publish-flow.mjs` -6. `node harness/check-trust-insight.mjs` -7. `node scripts/check-json.mjs` -8. `node harness/check-harness.mjs` -9. `git diff --check` -10. 如本机 WeChat DevTools `wcc`/`wcsc` 可用,编译全部 WXML/WXSS;不可用时记录为“编译未验证”,不要记为通过。 - -自动检查只能证明候选版具备手测入口。真机授权、定位、键盘、安全区、地图交互、跳转和云端路径必须靠 DevTools 或真机手测确认。 - -## 4. 必测用户旅程 - -### 4.1 地图列表到详情 - -- 冷启动进入地图页,允许或拒绝定位各测一次。 -- 地图标记和列表卡片都能打开同一任务详情。 -- 详情页标题、分类、地点、距离、状态、有效期与列表展示一致。 -- 隐藏或过期任务不应出现在普通地图列表中;如无法构造数据,记录为“未覆盖”。 - -### 4.2 发布状态与定位失败重试 - -- 首次进入发布页时观察定位块和底部主按钮,字段缺失、定位等待、定位失败、位置已确认的文案和按钮状态要一致。 -- 拒绝定位或模拟定位失败后,重试入口可见,点击后状态有明确变化。 -- 标题、详情、分类、有效期、位置确认都完成前,主按钮不得表现为可发布。 -- 提交中态不得允许重复提交。 - -### 4.3 发布成功后详情跳转 - -- 使用真实 DevTools 或真机流程完成一次发布。 -- 发布成功后应跳转到新任务详情页。 -- 新详情页展示刚提交的标题、详情、分类、地点、有效期和初始信任数据。 -- 返回地图或列表后,新任务可见且状态为 active。 - -### 4.4 详情 TrustInsight 与评论入口 - -- 从地图或发布成功页进入 active 详情,TrustInsight 应解释确认、过时、举报和评论数。 -- 点击“确认仍有效”后,确认数和最近确认时间刷新;同一用户重复确认应被阻止或给出清楚反馈。 -- 标记过时、举报在阈值前后的状态变化和文案要清楚,不应暗示举报等于任务已处理。 -- 评论区标题右侧入口和悬浮评论入口在窄屏下不得遮挡 TrustInsight 或主要信任动作。 -- resolved、expired、hidden 相关状态如无法用正常路径构造,记录为“未覆盖”,不要推断通过。 - -### 4.5 图片与云端路径风险 - -- 发布页如出现图片选择或上传入口,至少验证选择、取消、上传失败、提交失败后的用户反馈。 -- 当前候选若未部署云函数或 Storage,不得把图片上传、云端评论、云端持久化标为通过。 -- 若本地 mock 存储与云端能力表现不同,结论中必须分开记录“本地路径通过”和“云端路径未验证/阻塞”。 - -## 5. 证据升级规则 - -某项从“待验证”变成“通过”必须同时满足: - -- 记录测试环境:DevTools 版本或真机机型、基础库版本、网络状态、定位授权状态。 -- 记录入口路径:从哪个页面、点击哪个入口、输入哪些关键字段。 -- 记录期望与实际结果一致;如有截图、录屏、控制台日志或生成的任务 id,应写入证据。 -- 记录失败恢复:至少对该旅程的关键失败分支完成一次验证,或明确写“未覆盖失败分支”。 -- 自动脚本通过只能作为前置证据,不能单独让用户旅程从“待验证”变成“通过”。 - -建议状态口径: - -- `待验证`:尚未在 DevTools 或真机完整执行。 -- `通过`:按本 brief 完成手测且证据完整。 -- `失败`:手测可复现的产品或功能问题。 -- `阻塞`:环境、工具、云端资源或权限导致无法判断产品行为。 -- `未覆盖`:本轮未构造到该场景,不能视为通过。 - -## 6. 阻塞和异常记录方式 - -### DevTools CLI 端口超时 - -如果 `open`、`preview` 或 CLI 连接 9420 等端口超时,记录为: - -- 状态:`阻塞` -- 类型:`DevTools CLI 端口超时` -- 证据:命令、开始时间、超时时长、错误摘要 -- 影响:无法通过 CLI 启动或预览;不代表页面手测通过或失败 -- 下一步:改用已打开的 DevTools 手动导入项目,或在确认 CLI 服务恢复后重试 - -### WAServiceMainContext timeout - -如果出现 `WAServiceMainContext timeout`,记录为: - -- 状态:`阻塞` 或 `失败`,取决于是否能稳定复现到具体页面行为 -- 证据:触发页面、操作步骤、控制台日志、截图或录屏 -- 影响:标明是启动、编译、页面加载还是交互阶段超时 -- 下一步:重启 DevTools、清缓存、重新编译后复测;复测前不得标为通过 - -### 云函数或 Storage 未部署 - -如果云函数、Storage、云端评论或上传服务未部署,记录为: - -- 状态:`阻塞` -- 类型:`云端资源未部署` -- 证据:缺失的函数或 bucket 名称、控制台错误、相关页面路径 -- 影响:云端路径未验证;本地 mock 路径结论单独记录 -- 下一步:部署资源后复测,或明确本候选仅准入本地 mock 演示 - -## 7. 推荐发布准入结论格式 - -```text -发布准入结论:通过 / 有条件通过 / 不通过 / 阻塞 - -自动检查: -- 通过项: -- 未验证项: -- 失败项: - -DevTools/真机环境: -- 工具/设备: -- 基础库: -- 定位授权: -- 网络: - -用户旅程结论: -- 地图列表到详情: -- 发布状态/定位失败重试: -- 发布成功后详情跳转: -- 详情 TrustInsight/评论入口: -- 图片/云端路径: - -关键证据: -- 截图/录屏/日志/任务 id: -- 失败分支证据: - -阻塞或风险: -- DevTools CLI: -- WAServiceMainContext: -- 云函数/Storage: -- 其它: - -最终建议: -- 是否可进入下一轮候选: -- 必须先修复或补测的事项: -``` - -## 8. H 组核心结论 - -当前候选的最大缺口是 DevTools/真机证据,不是继续加功能。只有当自动检查通过,并且上述用户旅程在真实 DevTools 或真机中留下可追溯证据后,相关项才能从“待验证”升级为“通过”。任何 CLI 超时、运行时 timeout、云端资源未部署都应记录为阻塞或未验证,不能用脚本结果替代手测结论。 diff --git a/harness/devtools-recovery-command-checklist.md b/harness/devtools-recovery-command-checklist.md deleted file mode 100644 index 6dd373e..0000000 --- a/harness/devtools-recovery-command-checklist.md +++ /dev/null @@ -1,95 +0,0 @@ -# DevTools Recovery Dry-run 命令验收清单 - -日期:2026-06-14 - -范围:用于 AG 轮验收 DevTools recovery dry-run 手动入口。目标是确认新增 package script 只做诊断、默认无副作用,并且当前 9420 service port blocker 下不会把 UI smoke 误记为 passed。 - -## 0. 进入前确认 - -- [ ] 工作目录为 `/tmp/street-tasks-iter-worktrees/devtools-recovery-command`;macOS 显示 `/private/tmp/...` 时按同一 worktree 记录。 -- [ ] 已运行 `bash harness/init.sh`,且 JSON、harness、blocked-summary preflight 基线通过。 -- [ ] 本清单只验证命令和证据口径;不默认退出 DevTools、不重开 DevTools、不清缓存、不改真实 AppID。 - -## 1. Package Script 验收 - -- [ ] `package.json` 存在 AG 新增的 recovery dry-run 手动入口:`npm run inspect:devtools-recovery`。 -- [ ] 该 script 必须等价调用: - - ```bash - node scripts/recover-devtools-service-port.mjs --dry-run - ``` - -- [ ] dry-run script 可附加 `--project`、`--port 9420`、`--timeout-ms` 等诊断参数,但不得默认附加会产生副作用的 `--quit-reopen`。 -- [ ] 默认 `npm run check` 不调用 recovery dry-run,不调用 `recover-devtools-service-port.mjs`,不调用 `--quit-reopen`,不触发 DevTools `quit` / `open` / `preview`。 -- [ ] AF 的手动诊断命令仍独立存在: - - `npm run inspect:devtools-port` - - `npm run check:devtools-smoke` - -## 2. Dry-run 输出验收 - -执行 AG 新增的 recovery dry-run script: - -```bash -npm run inspect:devtools-recovery -``` - -输出至少应包含: - -- [ ] `WeChat DevTools service port recovery report`。 -- [ ] `Before status:`,包含 dry-run 前的 smoke status、exitCode 和摘要。 -- [ ] `Actions attempted/skipped:`,列出 DevTools quit、reopen wait、DevTools open。 -- [ ] `After status:`,包含 dry-run 后的 smoke status、exitCode 和摘要。 -- [ ] `Next steps:`,说明如果仍 blocked 应如何进入带副作用恢复或人工处理。 - -当前 blocked 环境下的期望: - -- [ ] `Before status` 应为 `blocked`,对应 9420 service port 无监听、connection refused、wait IDE port timeout 或等价 blocker 摘要。 -- [ ] `After status` 仍应为 `blocked`;dry-run 不能把 blocked 变成 ready。 -- [ ] `Actions attempted/skipped` 中 DevTools quit、reopen wait、DevTools open 都应为 `skipped`。 -- [ ] skipped 原因应能看出是 `--dry-run` 或未允许 `--quit-reopen`。 -- [ ] dry-run 结果只能作为诊断证据;UI smoke 不可记为 `passed`。 - -## 3. 如用户决定执行带副作用恢复 - -只有用户明确接受 quit/open 影响后,才执行以下复测之一。 - -- [ ] 直接运行带 `--quit-reopen` 且不带 `--dry-run` 的 node 命令: - - ```bash - node scripts/recover-devtools-service-port.mjs \ - --project /tmp/street-tasks-iter-worktrees/devtools-recovery-command \ - --port 9420 \ - --timeout-ms 30000 \ - --quit-reopen - ``` - -- [ ] 或执行等价人工 UI 操作:手动退出 WeChat DevTools,重新打开当前项目,并确认服务端口设置。 -- [ ] 记录 side effects:DevTools 是否被退出、是否重开、模拟器/控制台/预览二维码/真机调试是否被中断、是否打开了正确 worktree。 -- [ ] 恢复动作结束后必须复跑 AF 命令: - - ```bash - npm run inspect:devtools-port - npm run check:devtools-smoke - ``` - -- [ ] 只有 `check:devtools-smoke` 显示 `status: ready` 且退出码为 0 后,才能进入真实 DevTools UI smoke。 -- [ ] 真实 UI smoke 需要单独操作和记录;即使 recovery 成功,也不自动等于地图、列表、发布、详情或真机验证 passed。 - -## 4. 证据记录格式 - -建议写入 `harness/claude-progress.md` 的摘要: - -```text -### Session 0XXAG - -- 日期:2026-06-14 -- 分支: -- 本轮目标:验证 AG DevTools recovery dry-run 手动入口和默认无副作用口径 -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`npm run inspect:devtools-recovery` -- 已记录证据:package script `inspect:devtools-recovery` 存在并调用 `recover-devtools-service-port.mjs --dry-run`;默认 `npm run check` 未调用 recovery dry-run、未调用 `--quit-reopen`、未触发 DevTools quit/open -- dry-run 结果:before=;after=;actions=;nextSteps=<输出摘要> -- 当前 blocked 口径:before/after 仍 blocked 时,记录 service port 9420 blocker;actions skipped;UI smoke=,不得写 passed -- 如执行带副作用恢复:命令或人工 UI 操作=<...>;side effects=<退出/重开/中断摘要>;复跑 `inspect:devtools-port` 结果=<...>;复跑 `check:devtools-smoke` 结果=<...> -- 已知风险或未解决问题:<例如 9420 仍无 listener,真实 DevTools UI smoke 未执行> -- 下一步最佳动作:<若 ready,进入真实 UI smoke;若 blocked,人工确认服务端口或换机复测> -``` diff --git a/harness/devtools-recovery-command-design-note.md b/harness/devtools-recovery-command-design-note.md deleted file mode 100644 index 3b8e158..0000000 --- a/harness/devtools-recovery-command-design-note.md +++ /dev/null @@ -1,108 +0,0 @@ -# DevTools Recovery Dry-Run 命令设计说明 - -日期:2026-06-14 - -## 背景与目标 - -AF 已新增 DevTools 服务端口诊断和 strict smoke 手动命令。AG 计划基于 `scripts/recover-devtools-service-port.mjs` 增加 recovery dry-run 手动入口,用来判断当前环境是否适合执行恢复动作,并演练恢复报告结构。 - -该入口的核心定位是“恢复准入/演练”,不是默认检查,也不是自动恢复。现有 recovery 脚本默认是 diagnostic-only;只有用户显式传入 `--quit-reopen`,且没有传入 `--dry-run` 时,才允许执行 quit/open WeChat DevTools 的 side-effect 动作。 - -本说明用于约束命令命名、报告层次和状态文案,避免三类误解: - -- 把 dry-run blocked 误读成产品功能 bug 或恢复失败。 -- 把 dry-run 报告误读成 DevTools 已恢复。 -- 把会 quit/open DevTools 的 side-effect 命令误接入默认 `check`、CI 或普通 readiness。 - -## 命令命名原则 - -- `inspect:*`:只读诊断。可以读取端口、进程、CLI、项目路径等状态;不得 quit/open DevTools、清缓存、写文件或启动 preview。 -- `check:*`:strict/门禁。适合在手动门禁或 CI 中表达“通过/阻塞”,blocked 时应返回失败;不得偷偷执行恢复 side effect。 -- recovery dry-run:描述为“恢复准入/演练”。它可以复用 recovery 报告结构,但必须明确 actions 是 skipped,不应纳入默认 `npm run check`、`check:readiness`、`harness/init.sh` 或 CI。 -- side-effect recovery:必须显式带有 `--quit-reopen`,并在命令名、说明或运行输出中标注“会退出并重新打开 WeChat DevTools”。不能作为默认检查入口。 - -推荐表达: - -- `inspect:devtools-port`:只读端口与进程取证。 -- `check:devtools-smoke`:strict smoke access 门禁,blocked 可失败。 -- AG recovery dry-run 手动入口:`恢复准入/演练:运行 recovery dry-run,确认当前是否仍被服务端口阻塞,以及若要恢复会跳过或尝试哪些动作。` -- side-effect 手动入口:`显式恢复动作:仅在用户接受 quit/open DevTools 后运行,不进入默认 check。` - -## Recovery 报告层次 - -Recovery 报告建议固定为四段,并保持段落顺序稳定。 - -### 1. Before status - -说明执行任何 recovery actions 前的环境访问状态。这里回答“当前 DevTools service port 是否已经可用”,不是回答“产品 smoke 是否通过”。 - -需要包含: - -- `status`: `ready`、`blocked` 或 `unknown`。 -- 关键摘要:端口是否监听、CLI 是否可用、项目路径是否有效、smoke access 是否超时。 -- 若是 blocked,说明 blocker 是环境访问层,例如服务端口不可达、CLI 不可用或项目路径无效。 - -### 2. Actions attempted/skipped - -逐项列出 action,而不是只给一个总状态。dry-run 下应清楚显示 quit、wait、open 都被 skipped。 - -需要包含: - -- action 名称,例如 `DevTools quit`、`reopen wait`、`DevTools open`。 -- `attempted` 或 `skipped`。 -- skip reason,例如 `skipped because --dry-run was requested` 或 `skipped because --quit-reopen was not provided`。 -- 只有 side-effect 模式才能显示 quit/open attempted;此时要同时展示 exitCode、timeout、stdout/stderr 摘要等结果。 - -### 3. After status - -说明 actions 之后再次检查到的环境访问状态。dry-run 的 after status 只是“再次诊断”,不是恢复结果。 - -文案原则: - -- dry-run + blocked:写成“恢复未执行,环境仍阻塞”。 -- side-effect + blocked:写成“已尝试显式恢复动作,但环境仍阻塞”。 -- ready:写成“服务端口访问已可用,可继续手动 smoke”,但不能写成“用户旅程已通过”。 - -### 4. Next steps - -给出下一步人工动作,不要把自动脚本当成验收证据。 - -建议包含: - -- 如果 dry-run 仍 blocked:提示用户先在 WeChat DevTools UI 中开启 Settings -> Security Settings -> Service Port,再重新打开项目或复跑 dry-run。 -- 如果用户接受副作用:提示显式运行带 `--quit-reopen` 的 recovery 命令。 -- 如果 after ready:提示继续执行真实 DevTools/manual smoke,并把观察结果记录到 manual evidence。 -- 如果需要 strict 门禁:提示使用 strict smoke/check 命令,而不是把 recovery dry-run 接入默认 check。 - -## 状态文案原则 - -- dry-run blocked 是“恢复未执行/环境仍阻塞”,不是“恢复失败的产品 bug”。 -- blocked 应优先归类到环境访问层,除非后续真实小程序页面操作已经证明某个产品旅程失败。 -- ready 只表示 DevTools service port 或 smoke access 可用,不代表地图、发布、详情、图片或定位流程已通过。 -- side-effect 动作必须显式标注,包括 quit/open DevTools、等待重启、打开项目、可能打断当前 DevTools 会话。 -- 报告应避免把 `before`、`after` 和 `actions` 合并成单一“成功/失败”,否则用户无法判断到底是诊断失败、恢复未执行,还是显式恢复后仍阻塞。 - -## 推荐短文案 - -1. `恢复准入 dry-run:本次没有退出或重新打开 DevTools,只检查 before/after 状态和将会跳过的动作。` -2. `status: blocked。恢复未执行,当前 DevTools service port 仍不可达;这不是产品 smoke 失败证据。` -3. `Actions skipped: DevTools quit/open skipped because --dry-run was requested.` -4. `status: ready。服务端口访问可用;请继续运行真实 DevTools smoke,并单独记录手测观察。` -5. `Side-effect mode: 将退出并重新打开 WeChat DevTools;仅在用户明确接受该动作后运行,不纳入默认 check。` - -## 避免写法 - -1. 避免:`恢复失败,产品仍不可用。` - 原因:dry-run blocked 只说明环境访问仍阻塞,不能上升为产品 bug。 - -2. 避免:`dry-run 后 DevTools 已恢复。` - 原因:dry-run 没有执行 quit/open;只能说环境状态是否 ready。 - -3. 避免:`npm run check 会自动修复 DevTools 端口。` - 原因:默认 check/readiness 不应包含恢复 side effect。 - -4. 避免:`smoke passed。` - 原因:仅当真实 DevTools/manual smoke 已运行并记录观察时,才能说对应 journey passed。 - -5. 避免:`尝试重启 DevTools。` - 原因:side-effect 动作必须写明是 quit/open DevTools,并说明它是显式 opt-in。 diff --git a/harness/devtools-recovery-command-product-brief.md b/harness/devtools-recovery-command-product-brief.md deleted file mode 100644 index 8100873..0000000 --- a/harness/devtools-recovery-command-product-brief.md +++ /dev/null @@ -1,46 +0,0 @@ -# AG 组 DevTools Recovery Dry-run 产品简报 - -## 产品目标 - -AF 已经新增 `inspect:devtools-port` 和 `check:devtools-smoke`,并把当前真实 WeChat DevTools smoke 阻塞收敛为:9420 service port 被声明但实际 blocked。AG 的目标是在这个诊断之后,提供一个清楚的、显式的 recovery dry-run 报告入口,让用户先看到恢复流程会检查什么、准备做什么、实际跳过了什么,以及下一步应该由谁手动决定。 - -推荐手动入口: - -```bash -npm run inspect:devtools-recovery -``` - -这个入口的产品价值不是“恢复 DevTools”,而是把恢复前后的状态和动作边界说清楚,避免用户误以为脚本已经自动处理了本机 DevTools。 - -## 非目标 - -- 不自动 quit/open WeChat DevTools。 -- 不恢复 9420 service port,也不承诺恢复成功。 -- 不把 recovery dry-run 加入默认 `npm run check`、`check:readiness` 或 CI。 -- 不声称真实 DevTools UI smoke、地图页、发布页、详情页或真机链路已经通过。 -- 不替代用户在 DevTools UI 中启用 Service Port 的决定。 - -## 使用场景 - -当 `npm run check:devtools-smoke` 或 strict smoke 显示 blocked 后,执行者先运行 recovery dry-run 报告,查看: - -- `Before status`:当前 9420 service port 是否仍 blocked。 -- `Actions attempted/skipped`:脚本原本包含的 quit、wait、open 恢复动作是否全部被跳过,以及跳过原因。 -- `After status`:在无副作用模式下复查端口状态是否仍 blocked。 -- `Next steps`:下一步是手动启用 Service Port,还是在用户明确同意后另行运行带副作用的 direct node 命令。 - -用户看完 dry-run 后再决定: - -- 去 WeChat DevTools UI 的安全设置中手动启用 Service Port,并重新运行诊断。 -- 或在确认不会中断其他手测现场后,显式运行带副作用的 direct node 命令,例如加 `--quit-reopen` 且不加 `--dry-run`。 - -## 当前预期 - -在当前已知环境下,dry-run recovery 报告应表现为: - -- `Before status` 仍是 `blocked`,原因指向 9420 service port 无可用 listener 或连接失败。 -- `Actions attempted/skipped` 中 DevTools quit、reopen wait、DevTools open 都应是 skipped,原因是 `--dry-run` 或未提供 `--quit-reopen`。 -- `After status` 仍是 `blocked`;这符合预期,因为 dry-run 不会改变本机 DevTools 状态。 -- `Next steps` 应指向手动启用 DevTools Service Port;必要时才在用户明确接受副作用后运行带 `--quit-reopen` 的 direct node 命令。 - -结论口径:dry-run recovery 是恢复准入报告,不是恢复动作本身;blocked 继续表示 DevTools 环境入口未恢复,不能写成 UI smoke passed。 diff --git a/harness/devtools-recovery-report-checklist.md b/harness/devtools-recovery-report-checklist.md deleted file mode 100644 index 0d67507..0000000 --- a/harness/devtools-recovery-report-checklist.md +++ /dev/null @@ -1,137 +0,0 @@ -# AH DevTools recovery dry-run report 验收清单 - -日期:2026-06-15 - -## 验收边界 - -- 本清单只评测本地 ignored recovery dry-run report 工具,不验证真实 WeChat DevTools UI smoke,也不证明 DevTools 已恢复。 -- 所有报告文件必须是本地 ignored 工件,不应进入 git 提交。 -- 当前环境若仍无法连接 WeChat DevTools service port,报告应记录 `before` 和 `after` 均为 `blocked`;这属于环境阻塞证据,不是 UI failed 或 UI passed。 -- 默认 `npm run check` 只能继续跑 JSON、harness、readiness/default preflight 等默认门禁,不应执行本地 recovery report 的 prepare 或 check 命令。 - -## 静态验收项 - -- `.gitignore` 包含本地报告模式: - - `harness/devtools-recovery-report.local*.md` -- `package.json` scripts 存在: - - `prepare:devtools-recovery-report` - - `check:devtools-recovery-report` -- `package.json` 的默认 `check` 脚本不包含以下命令或等价调用: - - `prepare:devtools-recovery-report` - - `check:devtools-recovery-report` - - `prepare-devtools-recovery-report.mjs` - - `check-devtools-recovery-report.mjs` -- `scripts/prepare-devtools-recovery-report.mjs` 只允许输出到 ignored local 路径,例如 `harness/devtools-recovery-report.local-ah.md`。 -- `scripts/prepare-devtools-recovery-report.mjs` 生成报告后必须自动运行 `scripts/check-devtools-recovery-report.mjs` 作为 guard。 -- `scripts/check-devtools-recovery-report.mjs` 必须确认报告满足: - - 是 recovery dry-run report。 - - actions 均为 skipped,原因指向 dry-run 或无副作用模式。 - - 不包含 `UI smoke passed`。 - - 不包含 `DevTools recovered`。 - - 不包含 `恢复成功`。 - - 当前 blocked 环境下,before status 和 after status 都是 `blocked`。 - -## 正向验证 - -1. 运行基线: - - ```bash - bash harness/init.sh - ``` - -2. 运行默认检查,确认不触发 recovery report: - - ```bash - npm run check - ``` - - 预期:通过;输出中不出现 `prepare-devtools-recovery-report`、`check-devtools-recovery-report` 或新生成的 `harness/devtools-recovery-report.local*.md` 路径。 - -3. 生成 ignored local dry-run 报告: - - ```bash - npm run prepare:devtools-recovery-report -- --out harness/devtools-recovery-report.local-ah.md - ``` - - 预期:退出 0;报告文件存在;prepare 输出显示已自动运行 guard;报告仍明确 dry-run、actions skipped、before blocked、after blocked。 - -4. 单独复跑 guard: - - ```bash - npm run check:devtools-recovery-report -- --report harness/devtools-recovery-report.local-ah.md - ``` - - 预期:退出 0;输出确认报告是 dry-run blocked evidence,且不是 UI passed evidence。 - -## 负向验证 - -1. 非 ignored 输出路径应失败: - - ```bash - npm run prepare:devtools-recovery-report -- --out harness/devtools-recovery-report-ah.md - ``` - - 预期:非 0 退出;错误说明 output 必须匹配 ignored local report 模式。 - -2. 把报告改成 `UI smoke passed` 应失败: - - ```bash - cp harness/devtools-recovery-report.local-ah.md harness/devtools-recovery-report.local-ah-ui-passed.md - printf '\nUI smoke passed\n' >> harness/devtools-recovery-report.local-ah-ui-passed.md - npm run check:devtools-recovery-report -- --report harness/devtools-recovery-report.local-ah-ui-passed.md - ``` - - 预期:非 0 退出;错误指出报告不能声称 UI smoke passed。 - -3. 把报告改成 `DevTools recovered` 应失败: - - ```bash - cp harness/devtools-recovery-report.local-ah.md harness/devtools-recovery-report.local-ah-recovered.md - printf '\nDevTools recovered\n' >> harness/devtools-recovery-report.local-ah-recovered.md - npm run check:devtools-recovery-report -- --report harness/devtools-recovery-report.local-ah-recovered.md - ``` - - 预期:非 0 退出;错误指出 dry-run 报告不能声称 DevTools recovered。 - -4. 把报告改成 `恢复成功` 应失败: - - ```bash - cp harness/devtools-recovery-report.local-ah.md harness/devtools-recovery-report.local-ah-success-cn.md - printf '\n恢复成功\n' >> harness/devtools-recovery-report.local-ah-success-cn.md - npm run check:devtools-recovery-report -- --report harness/devtools-recovery-report.local-ah-success-cn.md - ``` - - 预期:非 0 退出;错误指出 dry-run 报告不能声称恢复成功。 - -## 清理步骤 - -运行完正向和负向验证后,清理本地报告和临时文件: - -```bash -rm -f harness/devtools-recovery-report.local*.md -``` - -清理后确认: - -```bash -git status --short -``` - -预期:除本清单和开发 agent 负责的代码改动外,没有 local report 文件出现在 git status 中。 - -## 证据记录格式 - -可按以下格式写入 `harness/claude-progress.md`: - -```markdown -### Session 045AH - -- 日期:2026-06-15 -- 分支:`` -- 本轮目标:AH 组本地 ignored DevTools recovery dry-run report 工具验收 -- 已完成:确认 `.gitignore` 忽略 `harness/devtools-recovery-report.local*.md`;确认 package scripts 存在;确认默认 `npm run check` 不运行 recovery report prepare/check;确认 prepare 只允许 ignored local 输出并在生成后自动运行 guard;确认 guard 阻止 UI passed、DevTools recovered、恢复成功等误导性报告口径。 -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`npm run check`;`npm run prepare:devtools-recovery-report -- --out harness/devtools-recovery-report.local-ah.md`;`npm run check:devtools-recovery-report -- --report harness/devtools-recovery-report.local-ah.md`;非 ignored 输出负向;`UI smoke passed` 篡改负向;`DevTools recovered` 篡改负向;`恢复成功` 篡改负向;清理 local report 文件。 -- 已记录证据:`npm run check` 未调用 recovery report prepare/check;prepare 输出 local report 路径并自动运行 guard;guard 输出 dry-run/actions skipped/before blocked/after blocked;非 ignored 输出路径失败;三类成功口径篡改均失败。 -- 已知风险或未解决问题:AH 工具仍不恢复 DevTools service port,也不执行真实 UI smoke;before/after blocked 只能证明当前环境仍阻塞,不能写 UI passed、DevTools passed 或真机 passed。 -- 下一步最佳动作:若需要真实恢复,先由用户确认可接受退出/重开 DevTools 的副作用,再运行显式 recovery 命令并单独执行 DevTools UI smoke。 -``` diff --git a/harness/devtools-recovery-report-design-note.md b/harness/devtools-recovery-report-design-note.md deleted file mode 100644 index 9849090..0000000 --- a/harness/devtools-recovery-report-design-note.md +++ /dev/null @@ -1,143 +0,0 @@ -# AH DevTools Recovery 本地报告设计说明 - -日期:2026-06-15 - -## 设计目标 - -AH 报告的定位是把 AG 的 `inspect:devtools-recovery` dry-run 控制台输出整理成一份本地 ignored Markdown 草稿,便于交接、评审和后续人工恢复决策引用。它只说明“本次 dry-run 看到了什么”和“哪些恢复动作被跳过”,不恢复 DevTools,也不证明任何小程序页面体验已经通过。 - -报告必须始终让读者先看到三个边界: - -- 这是 local ignored 草稿,不进入提交,不作为正式 evidence 本体。 -- 这是 dry-run,无退出、无重开、无清缓存、无 preview 等副作用。 -- 真实 DevTools UI smoke 和真机验证需要另行执行并单独记录。 - -## 信息层级 - -报告建议固定为四层,从可审计元信息到下一步动作逐层展开。 - -### 1. Run metadata - -用于让 reviewer 知道这份草稿来自哪一次运行,而不是让它看起来像正式验收结果。 - -建议字段: - -- `reportType`: `DevTools recovery dry-run local draft` -- `date`: `2026-06-15` -- `worktree`: 当前 worktree 路径 -- `branch`: 当前分支 -- `command`: 生成报告所运行的 prepare 或 inspect 命令 -- `outputPath`: `harness/devtools-recovery-report.local*.md` -- `sideEffects`: `none` -- `evidenceScope`: `handoff draft only; not UI smoke evidence` - -### 2. Guard status - -用于把误读防护放在原始日志前面。读者不应先读到大段 dry-run 输出,再自行推断它是否安全。 - -建议字段: - -- `guard`: `passed` 或 `blocked` -- `ignoredLocalReport`: `yes` -- `dryRun`: `yes` -- `actionsSkipped`: `yes` -- `beforeStatus`: `blocked`、`ready` 或 `unknown` -- `afterStatus`: `blocked`、`ready` 或 `unknown` -- `forbiddenClaimsFound`: `none` -- `summary`: 一句话说明“guard 只确认报告口径安全,不确认 UI smoke 通过”。 - -### 3. Raw dry-run report - -用于保留 AG 命令的原始可追溯信息,但不要把 raw output 包装成通过结论。 - -建议顺序: - -- `Before status`: service port、CLI、项目路径、strict smoke access 等运行前状态。 -- `Actions attempted/skipped`: DevTools quit、reopen wait、DevTools open 等动作逐项列出,并明确 `skipped because --dry-run was requested`。 -- `After status`: dry-run 后复查状态。当前 blocked 环境下应继续是 `blocked`。 -- `Diagnostics note`: 若有端口不可达、timeout、ECONNREFUSED 等原因,归类为 DevTools 环境访问层阻塞。 - -### 4. Next action - -用于把“交接能做什么”和“验收还缺什么”分开。 - -推荐内容: - -- 若仍 `blocked`:请先在 WeChat DevTools UI 中确认 Settings -> Security Settings -> Service Port,再复跑 dry-run 或 strict smoke。 -- 若要执行恢复:必须由用户明确接受 quit/open DevTools 的副作用后,才运行非 dry-run 恢复命令。 -- 若 service port 变为 `ready`:继续执行真实 DevTools UI smoke,并将地图列表、发布、详情等观察写入 manual evidence。 -- 无论状态如何,本地报告只能用于交接,不能替代真实 DevTools UI smoke。 - -## Guard 保护边界 - -guard 应保护的是“读者不会把本地 dry-run 草稿误读成恢复或通过证据”,而不是判断产品体验是否好坏。 - -必须保护: - -- local report 是 ignored 草稿:路径应匹配 `harness/devtools-recovery-report.local*.md`,不应出现在可提交 evidence 中。 -- dry-run 是无副作用:报告必须明确 `dryRun: yes` 或等价口径。 -- actions must be skipped:DevTools quit、reopen wait、DevTools open 等恢复动作在 dry-run 报告中必须是 skipped。 -- blocked 不等于产品失败:before/after blocked 只说明 DevTools service port 或环境访问仍阻塞。 -- ready 不等于页面通过:service port ready 只表示可以继续手测,不代表地图、发布、详情、定位或图片流程已通过。 - -guard 必须拒绝这些通过或恢复声明: - -- `UI smoke passed` -- `DevTools passed` -- `DevTools recovered` -- `real device passed` -- `真机 passed` -- `真机通过` -- `恢复成功` -- `已恢复` -- `小程序验收通过` - -## 推荐短状态 - -可放在报告顶部或 reviewer 摘要中: - -- `Local draft only: ignored recovery dry-run report; not submitted evidence.` -- `Dry-run only: no quit/open DevTools actions were executed.` -- `Guard status: safe to hand off; not UI smoke evidence.` -- `Before/after status: blocked; environment access still needs manual recovery.` -- `Actions skipped: DevTools quit/open skipped because --dry-run was requested.` -- `Next action: run real DevTools UI smoke after service port is ready.` - -中文推荐: - -- `本地草稿:仅用于交接,不是正式验收证据。` -- `dry-run:未退出或重新打开 DevTools。` -- `guard 通过:报告口径安全,但不代表 UI smoke 通过。` -- `before/after blocked:当前仍是环境访问阻塞,不是产品体验失败。` -- `下一步:服务端口 ready 后,单独执行真实 DevTools UI smoke。` - -## 避免写法 - -以下写法容易把 dry-run 草稿误读成真实恢复或通过证据,应由 guard 拦截或在人工 review 中改写。 - -| 避免写法 | 问题 | 建议改写 | -| --- | --- | --- | -| `UI smoke passed` | 没有真实 UI smoke 证据 | `UI smoke not run; local dry-run draft only` | -| `DevTools recovered` | dry-run 没有执行恢复动作 | `Recovery not executed; actions skipped by dry-run` | -| `恢复成功` | 会被读成服务端口或页面已恢复 | `dry-run completed; before/after status recorded` | -| `真机通过` | 本报告不涉及真机 | `real-device validation not run` | -| `before/after blocked,所以产品不可用` | 把环境阻塞上升为产品失败 | `DevTools service port remains blocked; product journey unverified` | -| `service port ready,所以地图通过` | ready 只表示可开始手测 | `service port ready; run map/list/detail smoke next` | - -## 交接口径 - -这份 local recovery report 可以用于交接以下事实: - -- 本次 dry-run 的运行时间、分支、worktree 和命令。 -- recovery guard 是否确认报告仍是 ignored local draft。 -- before/after service port 状态。 -- DevTools quit/open 等动作是否被 dry-run 跳过。 -- 下一步应先做环境恢复、strict smoke,还是人工 UI smoke。 - -它不能替代以下验证: - -- WeChat DevTools 中真实打开项目、编译并观察页面。 -- 地图列表、marker/detail 链路、发布状态、详情 TrustInsight 等用户旅程 smoke。 -- 真机定位授权、图片上传、safe area、键盘遮挡和云端路径验证。 - -最终结论应保持为:本报告可降低交接误读风险,但不能把 AH dry-run 草稿当作真实 DevTools UI smoke 通过证据。 diff --git a/harness/devtools-recovery-report-preflight-checklist.md b/harness/devtools-recovery-report-preflight-checklist.md deleted file mode 100644 index fc0899f..0000000 --- a/harness/devtools-recovery-report-preflight-checklist.md +++ /dev/null @@ -1,126 +0,0 @@ -# AI DevTools recovery report preflight 验收清单 - -日期:2026-06-15 - -## 验收边界 - -- 本清单只验收 ignored local DevTools recovery report 的批量 preflight,不验证真实 WeChat DevTools UI smoke,也不证明 DevTools service port 已恢复。 -- preflight 的职责是扫描 `harness/devtools-recovery-report.local*.md`,并对每份 local report 复跑 `scripts/check-devtools-recovery-report.mjs` guard。 -- preflight passed 只表示现有 local recovery report 草稿没有越界声称成功;它不是 UI passed evidence,也不是 DevTools recovered evidence。 -- 默认 `npm run check` 不应调用该 preflight,避免把本机 ignored local report 状态带入默认 CI/check。 - -## 静态验收项 - -- 新脚本存在: - - `scripts/check-devtools-recovery-report-preflight.mjs` -- `package.json` scripts 存在: - - `check:devtools-recovery-report-preflight` -- `check:devtools-recovery-report-preflight` 调用 `scripts/check-devtools-recovery-report-preflight.mjs`。 -- `package.json` 的默认 `check` 脚本不包含以下命令或等价调用: - - `check:devtools-recovery-report-preflight` - - `check-devtools-recovery-report-preflight.mjs` -- preflight 只扫描 ignored local report 模式: - - `harness/devtools-recovery-report.local*.md` -- preflight 对扫描到的每份报告都必须调用既有单份 guard: - - `scripts/check-devtools-recovery-report.mjs` - -## 正向验证 - -1. 运行基线: - - ```bash - bash harness/init.sh - ``` - - 预期:退出 0;JSON、harness 和既有 blocked-summary preflight 通过。 - -2. 确认默认检查不触发 recovery report preflight: - - ```bash - npm run check - ``` - - 预期:退出 0;输出中不出现 `check-devtools-recovery-report-preflight`、`check:devtools-recovery-report-preflight` 或 `DevTools recovery report preflight`。 - -3. 无 local recovery report 时运行 preflight: - - ```bash - rm -f harness/devtools-recovery-report.local*.md - npm run check:devtools-recovery-report-preflight - ``` - - 预期:退出 0;输出包含 `nothing checked` 或等价文案,并明确这不是 UI passed evidence,也不是 DevTools recovered evidence。 - -4. 生成一份有效 local report 后运行 preflight: - - ```bash - npm run prepare:devtools-recovery-report -- --out harness/devtools-recovery-report.local-ai-one.md --force - npm run check:devtools-recovery-report-preflight - ``` - - 预期:退出 0;输出显示已检查 1 份报告,例如 `Checked 1 report(s).`;单份 guard 输出通过;仍提示不是 UI passed evidence。 - -5. 生成多份有效 local report 后运行 preflight: - - ```bash - npm run prepare:devtools-recovery-report -- --out harness/devtools-recovery-report.local-ai-two.md --force - npm run check:devtools-recovery-report-preflight - ``` - - 预期:退出 0;输出逐份列出或能定位被检查的 local report,并显示 checked count 为 2,例如 `Checked 2 report(s).`。 - -## 负向验证 - -1. 把任意 local report 追加 `DevTools recovered` 后,preflight 应失败: - - ```bash - cp harness/devtools-recovery-report.local-ai-one.md harness/devtools-recovery-report.local-ai-recovered.md - printf '\nDevTools recovered\n' >> harness/devtools-recovery-report.local-ai-recovered.md - npm run check:devtools-recovery-report-preflight - ``` - - 预期:非 0 退出;输出能定位坏报告,并包含单份 guard 的失败原因,例如 `DevTools recovered` 不能出现在 dry-run local report 中。 - -2. 清理上一步坏报告后,把任意 local report 追加 `UI smoke passed`,preflight 应失败: - - ```bash - rm -f harness/devtools-recovery-report.local-ai-recovered.md - cp harness/devtools-recovery-report.local-ai-one.md harness/devtools-recovery-report.local-ai-ui-passed.md - printf '\nUI smoke passed\n' >> harness/devtools-recovery-report.local-ai-ui-passed.md - npm run check:devtools-recovery-report-preflight - ``` - - 预期:非 0 退出;输出能定位坏报告,并包含单份 guard 的失败原因,例如 `UI smoke passed` 不能出现在 recovery dry-run local report 中。 - -## 清理步骤 - -运行完正向和负向验证后,必须清理 ignored local reports: - -```bash -rm -f harness/devtools-recovery-report.local*.md -``` - -清理后确认: - -```bash -git status --short -``` - -预期:除本清单和开发 agent 负责的脚本/package 改动外,没有 `harness/devtools-recovery-report.local*.md` 留在工作树。 - -## 证据记录格式 - -可按以下格式写入 `harness/claude-progress.md`: - -```markdown -### Session 046AI - -- 日期:2026-06-15 -- 分支:`` -- 本轮目标:AI 组 ignored DevTools recovery report 批量 preflight 验收 -- 已完成:确认 `scripts/check-devtools-recovery-report-preflight.mjs` 和 `check:devtools-recovery-report-preflight` 存在;确认默认 `npm run check` 不调用 preflight;确认无 local report 时 preflight 通过并提示 nothing checked;确认一个或多个有效 local reports 时 preflight 通过并显示 checked count;确认追加 `DevTools recovered` 或 `UI smoke passed` 的坏报告会导致 preflight 失败;清理所有 `harness/devtools-recovery-report.local*.md`。 -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`npm run check`;无 local report 的 `npm run check:devtools-recovery-report-preflight`;生成一份有效 report 后的 preflight;生成多份有效 reports 后的 preflight;`DevTools recovered` 篡改负向;`UI smoke passed` 篡改负向;清理 local reports。 -- 已记录证据:默认 `npm run check` 未调用 recovery report preflight;无 local report 输出 `nothing checked`;正向 preflight 输出 checked count;坏报告输出单份 guard 的失败原因;所有 local reports 已清理。 -- 已知风险或未解决问题:AI preflight 通过只证明 ignored local recovery report 草稿仍被 guard 约束,不代表 UI passed、DevTools recovered、真机 passed、地图列表视觉通过或 9420 service port 已恢复。真实 UI smoke 仍需 WeChat DevTools 或真机手测证据。 -- 下一步最佳动作:若用户恢复 WeChat DevTools service port,重新生成 recovery report 并复跑 preflight;随后单独执行真实 UI smoke,并把 UI 证据记录到对应 manual evidence,而不是把 preflight passed 当作 UI 证据。 -``` diff --git a/harness/devtools-recovery-report-preflight-design-note.md b/harness/devtools-recovery-report-preflight-design-note.md deleted file mode 100644 index b638455..0000000 --- a/harness/devtools-recovery-report-preflight-design-note.md +++ /dev/null @@ -1,166 +0,0 @@ -# DevTools Recovery Report Preflight 设计说明 - -日期:2026-06-15 - -## 设计目标 - -本 preflight 面向 AH 已有的单份 ignored local DevTools recovery dry-run report guard。它扫描所有 `harness/devtools-recovery-report.local*.md`,并对每一份报告逐个运行 guard,帮助交接前确认本地草稿仍然是 dry-run、ignored、无恢复成功声明的安全口径。 - -preflight 的结论只能说明“本地 recovery dry-run 报告草稿是否仍符合 guard 约束”。它不能说明 DevTools 已恢复,也不能说明真实 UI smoke 已执行或通过。 - -## 输出层级 - -输出建议固定为四层,让 reviewer 先理解检查边界,再看逐项结果。 - -### 1. Scan scope - -说明本次扫描了什么,不把“无文件”误写成“恢复完成”。 - -建议字段: - -- `pattern`: `harness/devtools-recovery-report.local*.md` -- `scope`: `ignored local recovery dry-run reports only` -- `count`: 匹配到的报告数量 -- `action`: `run existing report guard for each matched report` - -### 2. Per-report result - -逐份报告输出 guard 结果,失败时给出文件路径和 guard 原始失败原因,避免 reviewer 只看到聚合失败却不知道哪份报告需要修。 - -建议字段: - -- `report`: 报告路径 -- `guard`: `passed` 或 `failed` -- `reason`: 失败时引用 guard 的短错误原因 -- `meaning`: `report hygiene only; not UI smoke evidence` - -### 3. Aggregate status - -聚合状态只描述 preflight 本身,不扩大成产品验收结论。 - -建议状态: - -- `none`: 未发现 local reports,因此没有报告需要 guard。 -- `passed`: 找到的所有 local reports 都通过 guard。 -- `failed`: 至少一份 local report 未通过 guard。 - -聚合行建议同时给出数量:`checked`、`passed`、`failed`。 - -### 4. Not UI Evidence Warning - -无论 `none`、`passed` 还是 `failed`,最后都必须输出非 UI 证据警告。失败表示报告口径或草稿一致性有问题,不表示小程序 UI 失败;通过表示报告草稿安全,不表示 UI 通过。 - -## 推荐输出文案 - -### 无 local reports - -```text -No local DevTools recovery reports found; nothing checked. -Scan scope: harness/devtools-recovery-report.local*.md -Preflight status: none. This does not mean DevTools was recovered or UI smoke was run. -Preflight is not UI evidence; run real DevTools UI smoke separately. -``` - -中文口径: - -```text -未发现本地 DevTools recovery report;本次没有报告需要检查。 -扫描范围:harness/devtools-recovery-report.local*.md -preflight 状态:none。该状态不代表 DevTools 已恢复,也不代表 UI smoke 已执行。 -preflight 不是 UI 证据;真实 DevTools UI smoke 需要单独执行。 -``` - -### 检查 N 份 - -```text -Found 3 local DevTools recovery report(s). -Checking harness/devtools-recovery-report.local-a.md ... passed. -Checking harness/devtools-recovery-report.local-b.md ... passed. -Checking harness/devtools-recovery-report.local-c.md ... passed. -Preflight status: passed. Checked 3 report(s), 3 passed, 0 failed. -Preflight passed, but this is not UI evidence and does not prove DevTools recovery. -``` - -中文口径: - -```text -发现 3 份本地 DevTools recovery report。 -检查 harness/devtools-recovery-report.local-a.md ... passed。 -检查 harness/devtools-recovery-report.local-b.md ... passed。 -检查 harness/devtools-recovery-report.local-c.md ... passed。 -preflight 状态:passed。共检查 3 份,3 份通过,0 份失败。 -preflight passed 只代表报告 guard 通过,不是 UI 证据,也不证明 DevTools 已恢复。 -``` - -### 某份失败 - -```text -Found 3 local DevTools recovery report(s). -Checking harness/devtools-recovery-report.local-a.md ... passed. -Checking harness/devtools-recovery-report.local-b.md ... failed: Report must not claim unverified success. -Checking harness/devtools-recovery-report.local-c.md ... passed. -Preflight status: failed. Checked 3 report(s), 2 passed, 1 failed. -Fix or remove the failed local report before citing recovery handoff notes. -Failure is report-guard evidence only; it is not a DevTools UI smoke result. -``` - -中文口径: - -```text -发现 3 份本地 DevTools recovery report。 -检查 harness/devtools-recovery-report.local-a.md ... passed。 -检查 harness/devtools-recovery-report.local-b.md ... failed:报告包含未验证通过声明。 -检查 harness/devtools-recovery-report.local-c.md ... passed。 -preflight 状态:failed。共检查 3 份,2 份通过,1 份失败。 -引用 recovery 交接说明前,请先修复或移除失败的本地报告。 -失败只说明报告 guard 未通过,不是 DevTools UI smoke 结果。 -``` - -### Preflight passed 但不是 UI evidence - -```text -DevTools recovery report preflight passed. Checked 3 report(s). -This only means every matched local dry-run report passed its guard. -It is not UI evidence, not real-device evidence, and not proof that DevTools recovered. -Run real DevTools UI smoke separately before recording user-visible passing evidence. -``` - -中文口径: - -```text -DevTools recovery report preflight passed,共检查 3 份报告。 -这只代表所有匹配到的本地 dry-run report 都通过了 guard。 -它不是 UI 证据,不是真机证据,也不证明 DevTools 已恢复。 -记录用户可见 passing evidence 前,仍需单独执行真实 DevTools UI smoke。 -``` - -## 避免写法 - -以下写法会让 reviewer 把 preflight 误读成真实恢复或 UI 通过,应在输出和报告摘要中避免: - -| 避免写法 | 问题 | 推荐改写 | -| --- | --- | --- | -| `DevTools recovered` | preflight 没有执行恢复动作 | `DevTools recovery not verified; local reports only passed guard` | -| `UI smoke passed` | preflight 没有打开页面做 UI smoke | `UI smoke not run; preflight checked local reports only` | -| `all recovery complete` | 会被读成恢复流程已完成 | `all matched local reports passed guard` | -| `恢复成功` | 会被读成服务端口或页面已恢复 | `恢复未验证;本次仅检查本地 dry-run report` | -| `页面通过` | 没有真实页面观察证据 | `页面未手测;需要另行执行 DevTools UI smoke` | -| `可作为验收证据` | 本地 ignored 草稿不能替代验收 | `可作为交接前复核,不是验收证据` | - -## 交接使用边界 - -preflight 可以用于交接前复核: - -- 检查是否存在多份 ignored local recovery report。 -- 逐份确认 report guard 仍能拦截恢复成功或 UI passed 误写。 -- 在交接摘要中说明哪些本地报告可以被安全引用为 dry-run handoff notes。 -- 在失败时定位需要修复或删除的本地草稿。 - -preflight 不能替代真实 DevTools UI smoke: - -- 不会启动、恢复、退出或重新打开 WeChat DevTools。 -- 不会确认 DevTools service port 是否已经恢复可用。 -- 不会编译小程序、打开地图页、发布页或详情页。 -- 不会验证地图列表、定位授权、发布闭环、详情信任说明、图片上传或真机 safe area。 - -最终报告口径应保持为:preflight 是交接前的本地报告守门检查;只有真实 DevTools UI smoke 或真机操作记录,才能作为用户可见体验通过证据。 diff --git a/harness/devtools-recovery-report-preflight-product-brief.md b/harness/devtools-recovery-report-preflight-product-brief.md deleted file mode 100644 index 19468f7..0000000 --- a/harness/devtools-recovery-report-preflight-product-brief.md +++ /dev/null @@ -1,46 +0,0 @@ -# AI 组 DevTools Recovery Report Preflight 产品简报 - -日期:2026-06-15 - -## 产品目标 - -AH 已经新增 ignored local DevTools recovery dry-run report 生成能力,并提供单文件 guard,防止某一份 local report 被写成 `DevTools recovered`、`UI smoke passed` 或其他未经验证的恢复成功证据。 - -本轮 AI 的目标是在不加入默认 `npm run check` 的前提下,新增一个评审前手动 preflight:扫描当前 worktree 下所有 `harness/devtools-recovery-report.local*.md`,并对每一份 ignored local recovery report 逐个运行 AH 的单报告 guard。这样 reviewer 或交接人引用 recovery report 前,可以一次确认所有本地草稿仍然只是 dry-run blocked/next-step 记录,而不是被后编辑成恢复成功或 UI passed。 - -## 非目标 - -- 不恢复 9420 service port。 -- 不 quit/open WeChat DevTools。 -- 不执行真实 UI smoke、地图页 smoke、发布页 smoke、详情页 smoke 或真机验证。 -- 不把 preflight 接入默认 `npm run check`、readiness 或 CI。 -- 不提交 `harness/devtools-recovery-report.local*.md` 或其他 ignored local report。 -- 不把 guard 通过解释成 DevTools recovered、UI passed、DevTools passed、真机 passed 或地图列表视觉通过。 - -## 使用场景 - -评审、交接或引用 recovery reports 前,执行者手动运行 recovery report preflight。它只检查当前 worktree 中已经存在的 ignored local recovery reports,不生成新报告,也不改变 DevTools 状态。 - -预期行为: - -- 当前 worktree 没有 `harness/devtools-recovery-report.local*.md` 时,preflight 应通过,并清楚输出类似 `nothing checked` 的口径,表示没有本地 recovery report 需要检查。 -- 当前 worktree 有一份或多份 local recovery report 时,preflight 应逐份调用单报告 guard,并报告实际 checked 数量。 -- 只要任意一份 report 缺少 dry-run blocked 关键结构,或被后编辑成恢复成功、UI passed、DevTools passed 等未经验证结论,preflight 就应失败。 -- preflight 失败时,执行者不能继续引用这些 local reports 作为交接证据;需要修正或删除坏报告后重新运行。 - -## 用户价值 - -AH 的单文件 guard 能保护“正在查看的那一份报告”。AI 的 preflight 进一步保护“评审前当前 worktree 里的所有报告”,降低两类风险: - -- 多份 local recovery report 并存时,只检查其中一份,漏掉另一份已坏报告。 -- 某份 report 在 guard 通过后被手工编辑成“恢复成功”或“UI passed”,但交接时仍被继续引用。 - -这个能力的价值是提高交接和评审引用证据的可信度:它不让 blocked 变成 passed,也不替代真实 UI smoke,只是在引用前确认所有本地 recovery dry-run 草稿仍保持正确边界。 - -## 验收口径 - -- preflight 扫描范围限定为当前 worktree 的 `harness/devtools-recovery-report.local*.md`。 -- 没有匹配文件时应退出成功,并明确说明 nothing checked。 -- 有匹配文件时应逐个运行 AH 单报告 guard;所有报告通过时,preflight 才通过。 -- 任意报告包含恢复成功、UI passed、DevTools passed、真机 passed 等未经验证结论时,preflight 必须非零退出。 -- 默认 `npm run check`、readiness 和 CI 不应调用该 preflight;它是评审前的显式手动检查。 diff --git a/harness/devtools-recovery-report-product-brief.md b/harness/devtools-recovery-report-product-brief.md deleted file mode 100644 index ab1476b..0000000 --- a/harness/devtools-recovery-report-product-brief.md +++ /dev/null @@ -1,44 +0,0 @@ -# AH 组 DevTools Recovery 报告草稿产品简报 - -## 产品目标 - -AG 已新增 `inspect:devtools-recovery`,可以无副作用展示 WeChat DevTools service port recovery 的 dry-run 控制台报告。AH 的目标是把这份 dry-run 输出保存成 ignored local Markdown 报告草稿,方便交接、评审和后续恢复决策引用,但不提交本地证据。 - -这份草稿只记录“当前仍被阻塞”和“恢复动作被 dry-run 跳过”的事实。它的价值是让 reviewer 不必翻控制台,也能看到同一轮诊断的 before status、actions skipped、after status 和 next steps。 - -## 非目标 - -- 不恢复 9420 service port。 -- 不 quit/open WeChat DevTools。 -- 不运行带副作用的 `--quit-reopen` 恢复流程。 -- 不把报告生成或 guard 加入默认 `npm run check`、readiness 或 CI。 -- 不声称 UI smoke passed、DevTools passed、真机通过或恢复成功。 -- 不提交 ignored local Markdown 草稿或其他本地 evidence。 - -## 报告价值 - -报告草稿应保留四类信息: - -- `Before status`:运行 dry-run 前的 9420 状态和诊断原因。 -- `Actions skipped`:DevTools quit、reopen wait、DevTools open 等动作均未执行,并记录 `skipped because --dry-run was requested`。 -- `After status`:dry-run 后状态复查结果。当前预期仍为 `blocked`,因为 dry-run 不改变本机 DevTools。 -- `Next steps`:下一步应由用户在 DevTools UI 中启用 Service Port,或在明确接受副作用后另行执行非 dry-run 恢复;真实 UI journey 仍需单独手测。 - -## Guard 要求 - -guard 的职责是确认报告仍是 dry-run blocked 草稿,而不是恢复证据: - -- 报告路径必须是 ignored local Markdown 草稿;可提交改动中不能包含本地报告本体。 -- 报告必须包含 dry-run 口径、before status、actions skipped、after status 和 next steps。 -- 当前 blocked 场景下,before/after 都应记录为 `blocked`。 -- DevTools quit/open 相关动作必须记录为 skipped,原因是 `--dry-run`。 -- 拒绝把草稿写成 `UI smoke passed`、`DevTools passed`、`恢复成功`、`9420 restored` 或等价夸大结论。 - -## 当前 blocked 预期 - -在当前环境下,报告应清楚记录: - -- `Before status: status: blocked`。 -- `Actions skipped` 中 DevTools quit、reopen wait、DevTools open 均为 `skipped because --dry-run was requested`。 -- `After status: status: blocked`。 -- `Next steps` 指向手动启用 Service Port 或经用户确认后再执行带副作用恢复,并明确这不是 UI smoke passed。 diff --git a/harness/devtools-service-port-config-forensics-checklist.md b/harness/devtools-service-port-config-forensics-checklist.md deleted file mode 100644 index a4c00b9..0000000 --- a/harness/devtools-service-port-config-forensics-checklist.md +++ /dev/null @@ -1,148 +0,0 @@ -# AB DevTools Service Port Config Forensics Checklist - -日期:2026-06-17 - -范围:用于 AB 组 QA / 设计 agent 定义 WeChat DevTools Service Port 配置“只读取证”口径。本文档只定义可采集摘要、隐私边界、只读命令边界、blocked / diagnosis 映射和静态 guard 期望;不执行恢复,不打开、退出、预览、上传或修改 DevTools。 - -## 0. 总原则 - -- [ ] 本清单只用于判断“配置证据是否足以解释 service port blocker”,不能替代 DevTools smoke、DevTools UI、分享 payload、CloudBase、窄屏或真机 evidence。 -- [ ] 证据必须是摘要化、脱敏、可复核的配置状态,不提交原始配置文件、完整日志、完整 JSON、截图原图或用户数据。 -- [ ] 所有结论都必须写清 `status`、`diagnosis`、`confidence`、`conflictCount` 和 `nextHumanConfirmation`。 -- [ ] 若只读配置证据与端口探针冲突,以“需要人工确认”收束;不得把配置 `enabled` 写成端口 ready。 -- [ ] 若配置路径、文件内容或用户数据会泄露隐私,只记录来源类别、数量、时间 bucket 和 key 状态。 - -## 1. Allowed Evidence - -只允许提交以下配置摘要字段: - -- [ ] 配置文件类别:`DevTools preference`、`CLI preference`、`workspace metadata`、`recent launch metadata`、`log-derived config hint`、`unknown config source`。 -- [ ] key 名:例如 `servicePort`、`enableServicePort`、`ideHttpPort`、`enableCLI`、`security.servicePort`;只写 key 名,不写完整配置内容。 -- [ ] 开关状态:`enabled`、`disabled`、`unconfirmed`。 -- [ ] 端口号:目标端口和发现端口,例如 `9420`;未知时写 `unconfirmed`。 -- [ ] 多个来源数量:候选配置文件数量、可读来源数量、包含 service-port-like key 的来源数量。 -- [ ] 最近修改时间 bucket:`<1d`、`<7d`、`<30d`、`>=30d`、`unknown`;不写精确文件时间戳。 -- [ ] 冲突计数:`conflictCount=0..n`,并用短标签说明冲突类型,例如 `enabled-vs-disabled`、`port-mismatch`、`multiple-active-sources`。 -- [ ] confidence:`high`、`medium`、`low`;必须说明 confidence 只针对配置解释力,不代表产品验证通过。 -- [ ] 下一步人工确认:需要用户在 DevTools UI 中确认 service port 开关、显示端口、当前项目、DevTools 版本或多实例归属。 - -推荐摘要形态: - -```json -{ - "status": "blocked", - "diagnosis": "mismatch", - "configSources": { - "candidateCount": 3, - "readableCount": 2, - "servicePortLikeKeyCount": 2, - "modifiedBuckets": ["<1d", ">=30d"] - }, - "observedKeys": ["enableServicePort", "ideHttpPort"], - "configState": "enabled", - "configuredPort": 9420, - "observedProbePort": 9530, - "conflictCount": 1, - "confidence": "medium", - "nextHumanConfirmation": [ - "Confirm the Service Port toggle in DevTools UI.", - "Confirm the displayed service port and rerun read-only port probe." - ], - "notClaimed": [ - "DevTools smoke passed", - "DevTools UI journey passed", - "real-device journey passed" - ] -} -``` - -## 2. Forbidden Evidence - -以下内容不得提交、不得出现在可评审摘要中: - -- [ ] 完整路径:用户目录、DevTools user data 子路径、项目绝对路径、日志绝对路径、配置文件绝对路径。 -- [ ] 文件内容:完整配置文件、完整 JSON、完整 plist、完整 sqlite dump、完整日志片段或完整命令输出。 -- [ ] 浏览器 / WebView 存储:localStorage、cookie、session、IndexedDB、缓存目录内容。 -- [ ] 用户身份:openid、unionid、nickname、头像 URL、手机号、微信号、联系人、群名。 -- [ ] 项目历史:最近打开项目列表、真实项目路径、项目名历史、工作区历史。 -- [ ] 凭据:token、Authorization、Bearer、refresh token、access token、client secret、AppSecret、私有 AppID、数据库密码。 -- [ ] 数据库内容:sqlite 表内容、CloudBase 数据、评论正文、反馈正文、post 原文、图片 URL。 -- [ ] 截图原图、录屏原件、二维码、系统分享面板原图;只允许写脱敏文字摘要或本地附件编号。 - -## 3. 只读命令边界 - -允许的只读动作: - -- [ ] `stat` / 元数据读取:只用于文件类别、大小段、修改时间 bucket、权限摘要。 -- [ ] `read`:只读取必要配置文件,并在脚本内提取 key 名、布尔状态、端口号和计数;不得输出原文。 -- [ ] `search limited text`:只搜索 service-port-like key 和短错误关键词,并输出命中计数或脱敏 key 名。 -- [ ] `find` / `rg`:限制目录、深度、文件类型和匹配词;输出必须经过路径脱敏和 raw content suppression。 -- [ ] 端口探针:只做 `LISTEN` / `connect_refused` / `timeout` / `non-business response` 摘要。 - -禁止的动作: - -- [ ] write、append、delete、move、chmod/chown、压缩、复制 user data、导出配置。 -- [ ] open、quit、preview、upload、login、logout、recovery、relaunch、reset。 -- [ ] kill、pkill、killall、launchctl 操作、清缓存、清 storage、清 DevTools 设置。 -- [ ] 修改 Service Port 开关、修改端口号、修改 DevTools security settings。 -- [ ] 打开 DevTools GUI、导入项目、扫码预览、上传小程序、触发系统分享菜单。 - -## 4. Blocked / Diagnosis Mapping - -所有 diagnosis 都只描述配置证据和端口入口状态,不代表产品功能通过。 - -- [ ] `enabled_match_no_listener` - - 配置摘要:service port-like key 为 `enabled`,配置端口与目标端口一致。 - - 端口摘要:无 listener 或探针 `connect_refused` / `timeout`。 - - 写法:`blocked`,说明“配置看起来启用且端口匹配,但当前没有可连接 listener”。 - - 下一步:人工确认 DevTools UI 开关和当前实例;再跑端口探针和 smoke。 - -- [ ] `disabled` - - 配置摘要:可信来源显示 service port-like key 为 `disabled`。 - - 写法:`blocked`,说明“配置证据指向服务端口未开启”。 - - 下一步:由用户在 DevTools UI 中手动确认并开启;agent 不得改设置。 - -- [ ] `mismatch` - - 配置摘要:配置端口与目标探针端口不同,或多个来源端口不同。 - - 写法:`blocked` 或 `unknown`,取决于是否能确认当前 DevTools 实例。 - - 下一步:人工确认 DevTools UI 显示端口;后续命令统一传入实际端口。 - -- [ ] `unknown` - - 配置摘要:配置不可读、key 不存在、来源不可信、权限不足或证据过旧。 - - 写法:`unknown`,不得推断 enabled / disabled。 - - 下一步:人工确认 UI 设置、版本、多实例和当前项目。 - -- [ ] `conflict` - - 配置摘要:多个来源之间出现 enabled/disabled、端口号、最近修改时间或实例归属冲突。 - - 写法:`blocked` 或 `unknown`,必须写 `conflictCount` 和冲突标签。 - - 下一步:人工确认哪个 DevTools 实例和配置 profile 正在生效。 - -所有映射都必须带 `notClaimed`: - -- [ ] 不声称 DevTools smoke / open passed。 -- [ ] 不声称 DevTools UI 编译、地图、发布、详情或分享 journey passed。 -- [ ] 不声称真机、窄屏、系统分享、CloudBase readback 或真实 payload passed。 -- [ ] 不声称配置 enabled 等于端口 ready;端口 ready 也不能替代 UI / real-device evidence。 - -## 5. Static Guard 期望 - -后续脚本或 readiness guard 应静态检查以下约束: - -- [ ] Side-effect negative checks:脚本源码不得包含文件写入、删除、移动、kill、open、quit、preview、upload、清缓存、修改 DevTools 设置等副作用路径。 -- [ ] Redaction / sanitization:必须存在路径脱敏、用户标识脱敏、token-like 字段过滤和敏感 key 拒绝输出。 -- [ ] Raw content suppression:不得打印完整配置文件、完整 JSON、完整 plist、完整 sqlite dump、完整日志行、完整 `ps` 输出或完整 HTTP body。 -- [ ] Readiness no-side-effect wording:readiness 输出必须明说该检查是 static / read-only / no-side-effect,不会 quit/open/preview/upload/kill DevTools,也不会替代 smoke、DevTools UI 或真机 evidence。 -- [ ] Diagnosis vocabulary guard:脚本输出只能使用本清单定义的 `enabled_match_no_listener`、`disabled`、`mismatch`、`unknown`、`conflict` 或明确扩展后的新 code。 -- [ ] Evidence schema guard:脚本输出必须包含 `configState`、`configuredPort`、`sourceCount` 或等价计数、`modifiedBucket`、`conflictCount`、`confidence`、`nextHumanConfirmation` 和 `notClaimed`。 - -## 6. 收尾验证 - -- [ ] 修改本清单后运行: - - ```bash - node harness/check-harness.mjs - git diff --check - ``` - -- [ ] 确认本轮新增产品 brief/checklist,并仅修改配置取证、readiness guard 与 harness evidence 相关文件。 -- [ ] 确认没有运行 recovery、quit、open、preview、upload、清缓存或 kill。 diff --git a/harness/devtools-service-port-config-forensics-product-brief.md b/harness/devtools-service-port-config-forensics-product-brief.md deleted file mode 100644 index 9c8a6cd..0000000 --- a/harness/devtools-service-port-config-forensics-product-brief.md +++ /dev/null @@ -1,101 +0,0 @@ -# AB 组 DevTools Service Port 配置取证产品 Brief - -日期:2026-06-17 - -分支:`codex/iter-devtools-service-port-config-forensics` - -工作目录:`/tmp/street-tasks-iter-worktrees/devtools-service-port-config-forensics` - -角色:AB 组产品 agent - -## AB 目标 - -AB 轮接在 AA 轮 `service_port_flag_mixed_recent_evidence` 之后,目标是继续判断 WeChat DevTools Service Port 的配置层是否能从本机只读摘要里确认: - -- 配置层是否指向 `enabled`、`disabled` 或 `unconfirmed`。 -- 配置层声明的端口是否匹配当前目标端口 `9420`。 -- 是否存在多个配置源互相冲突,例如用户级、profile 级、workspace 或历史迁移残留配置给出不同启用状态或端口。 - -AA 已经确认过 log、user-data、CLI gate 和历史启动线索:当前仍不能把 DevTools service port 写成 ready,也不能进入真实 DevTools、朋友圈、系统分享、归因或真机 journey passed。AB 只补齐“设置/配置层是否能给出更明确方向”这一块,不替代 listener、HTTP smoke 或人工 UI 确认。 - -## 只读边界 - -AB 配置取证必须保持无副作用: - -- 不修改 WeChat DevTools 设置,不启用或关闭 Service Port。 -- 不运行 DevTools `open`、`quit`、`preview`、`upload`、recovery、清缓存、kill 或任何会改变 IDE/模拟器状态的动作。 -- 不删除、不移动、不 touch 配置、user-data、profile、lock、storage、cookie、session 或项目历史文件。 -- 不读取或输出 raw local storage、cookie、账号信息、登录态、完整项目历史、完整日志、完整路径树或完整配置文件内容。 -- 不把配置摘要写成 service port ready、DevTools UI passed、系统分享 passed、朋友圈 passed、归因 passed 或真机 passed。 - -如果判断必须依赖 UI 开关、账号安全弹窗、换 profile、换端口或重启 DevTools,本轮结论应保持 `blocked` 或 `unconfirmed`,并把下一步交给人工确认。 - -## 允许输出语义 - -配置取证的输出必须是脱敏摘要,只允许包含以下类型: - -- 相关 key 名摘要,例如 `servicePort.enabled`、`servicePort.port`、`security.servicePort` 这类键名或归一化键名,不输出原始文件内容。 -- 布尔或枚举状态:`enabled`、`disabled`、`unconfirmed`、`conflict`、`unknown`。 -- 端口号:仅输出与 Service Port 判断相关的数字端口,例如 `9420` 或检测到的候选端口。 -- 文件类别计数和 mtime bucket,例如 `global_settings: count=1, mtime=recent`、`profile_settings: count=2, mtime=stale`,不输出用户目录完整树。 -- 置信度:`high`、`medium`、`low`,并说明置信度来自配置一致性、mtime 新鲜度或多源冲突。 -- `nextHumanConfirmation`:下一步人工确认建议,例如检查 DevTools UI Service Port 开关、改用明确端口、换 profile,或在配置 enabled 后确认 listener。 - -输出可以说明“发现了哪些类别的配置源”和“这些源是否一致”,但不能泄露账号、本地项目列表、历史打开路径、CloudBase 标识、token、cookie、storage 值或设备个人信息。 - -## 诊断码建议 - -AB 轮建议使用以下配置层诊断码: - -- `service_port_config_enabled_port_match`:只读配置摘要显示 Service Port 处于 enabled,且端口匹配 `9420`。 -- `service_port_config_enabled_port_mismatch`:配置显示 enabled,但端口不是 `9420`,或多个 enabled 配置源指向不同端口。 -- `service_port_config_disabled`:配置显示 Service Port 处于 disabled。 -- `service_port_config_unconfirmed`:没有足够配置摘要确认 enabled/disabled,或无法安全读取相关键。 -- `service_port_config_conflict`:多个配置源在启用状态、端口或最近活跃 profile 上互相冲突。 - -这些诊断码都不能等于 service port ready。即使得到 `service_port_config_enabled_port_match`,也只说明配置层看起来正确;仍必须另有 listener / HTTP smoke / DevTools UI 或真机证据,才能推进真实运行判断。 - -## 与产品裂变目标的关系 - -AB 不新增用户侧裂变功能,也不改变发布、分享、朋友圈、系统分享、评论接力、归因或落地页体验。 - -AB 的产品价值是降低真实 evidence unblock 的盲目性:如果配置层显示 disabled 或 unknown,下一步应先做人工 UI 设置确认;如果配置层 enabled 但没有 listener,下一步应更聚焦 DevTools UI、端口、profile、daemon/helper 或工具版本问题;如果配置源冲突,下一步应先厘清当前 DevTools 实际使用的 profile 和端口。这样后续真实 DevTools、朋友圈、系统分享和归因 evidence 才有更明确的恢复路径。 - -## 评测口径 - -配置取证结果只能影响“下一步怎么排查”,不能直接升级用户旅程状态: - -- 如果只确认配置 `unknown`、`unconfirmed` 或 `disabled`,仍不能打 UI passed,也不能写 viral journey passed。 -- 如果配置 `enabled` 且端口匹配 `9420`,但没有 listener,结论仍是 blocked;建议人工 UI 复核、换端口、换 profile,或继续只读检查 profile/daemon/helper。 -- 如果配置 `enabled` 但端口不匹配 `9420`,应建议人工确认 DevTools UI 中实际 Service Port 端口,并用匹配端口重新做只读 listener/smoke 检查。 -- 如果存在多配置源冲突,应优先建议人工确认当前活跃 profile 和 UI 设置,不要自动清理或覆盖配置。 -- 如果配置显示 disabled,应建议人工在 DevTools UI 中确认是否允许开启 Service Port;AB 不自动开启。 - -可接受的 AB 结论示例: - -```text -status: blocked -diagnosis: service_port_config_enabled_port_match -configState: enabled -portState: matches_9420 -sourceSummary: user_settings=1 recent, profile_settings=1 stale, conflicts=0 -confidence: medium -nextHumanConfirmation: DevTools UI must confirm Service Port is enabled, then verify a listener on 9420 before any smoke or share evidence can be marked passed. -``` - -不可接受的 AB 结论: - -- “配置 enabled,所以 service port ready。” -- “配置端口是 9420,所以朋友圈/系统分享/归因 evidence 已通过。” -- “配置 unknown,但假设 UI 已打开。” -- “为了解决冲突,自动删除旧 profile 或改写设置。” - -## 下一步分叉 - -- `service_port_config_enabled_port_match` + no listener:人工检查 UI 开关是否真的生效,考虑换端口或换 profile,再复跑只读 listener/smoke。 -- `service_port_config_enabled_port_mismatch`:人工统一 DevTools UI 端口与诊断端口,避免继续用错误端口判断。 -- `service_port_config_disabled`:人工决定是否开启 Service Port;开启后重新记录配置摘要和 listener 状态。 -- `service_port_config_unconfirmed`:记录缺失配置层,继续用 UI 人工确认或只读日志/profile 摘要补证。 -- `service_port_config_conflict`:先确认活跃 profile 和当前 UI 设置,再讨论是否需要人工清理或切换 profile。 - -只有在 Service Port 形成可连接 listener,并且真实 DevTools 或真机步骤留下脱敏 evidence 后,才可以继续判断地图、发布、详情、系统分享、朋友圈或 attribution journey 是否通过。 diff --git a/harness/devtools-service-port-ui-confirmation-checklist.md b/harness/devtools-service-port-ui-confirmation-checklist.md deleted file mode 100644 index 9b088e9..0000000 --- a/harness/devtools-service-port-ui-confirmation-checklist.md +++ /dev/null @@ -1,205 +0,0 @@ -# AC DevTools Service Port UI 确认 QA Checklist - -日期:2026-06-17 - -范围:用于 AC 组设计 / QA agent 验收用户手动打开 WeChat DevTools `Settings -> Security Settings -> Service Port` 后的最小复测证据包。本文档只定义人工 UI 确认、只读复测、传播链手测准备和证据边界;不自动修改 DevTools,不执行真实传播 journey,也不声称 journey passed。 - -## 0. 验收原则 - -- [ ] AC 的目标是把 `service_port_config_disabled` 的 blocker 转成可复核的人工 UI 确认步骤,再用只读命令复核端口入口是否可继续。 -- [ ] 当前背景应记录为:配置摘要 `configState=disabled`,`portState=matches_9420`,`conflictCount=0`,目标端口 `9420` 无 listener 或 smoke blocked。 -- [ ] 用户手动开启 Service Port 只表示“人工设置动作完成”,不等于 listener ready、smoke ready、DevTools UI passed、真机 passed 或 viral journey passed。 -- [ ] 任何自动工具输出都只能给出 `blocked`、`ready_for_manual_journey` 或等待人工 evidence 的语义;工具本身不得声明传播 journey 已通过。 -- [ ] 所有证据必须脱敏、摘要化、可复核;不得提交 raw config、截图原图、完整路径、文件名、账号、登录态、token、cookie、openid、项目历史或 DevTools 自动设置修改记录。 - -## 1. 手动 UI 确认步骤 - -以下步骤必须由用户在 WeChat DevTools UI 中手动执行,agent 只记录用户返回的脱敏摘要。 - -- [ ] 用户打开当前项目对应的 WeChat DevTools。 -- [ ] 用户进入 `Settings`。 -- [ ] 用户进入 `Security Settings`。 -- [ ] 用户找到 `Service Port`。 -- [ ] 用户记录开关当前状态:`enabled`、`disabled`、`not_found`、`unavailable` 或 `not_confirmed`。 -- [ ] 如果用户愿意继续,由用户手动开启 `Service Port`;agent 不得通过 CLI、脚本、自动化 UI 或系统命令修改该设置。 -- [ ] 用户记录 UI 显示端口:`9420`、`other_port:` 或 `unconfirmed`。 -- [ ] 用户记录是否出现安全提示或重启提示:只写 `prompt_seen=yes/no/unconfirmed`,不得提交截图原图或提示全文。 -- [ ] 用户返回本对话后,agent 才能运行只读端口 / smoke / 手测准备复核命令。 - -## 2. 记录字段 - -最小记录字段: - -```text -status: <见第 6 节> -testedAt: <日期、时区即可> -actor: user_manual_ui_confirmation -targetPort: 9420 -uiServicePortState: enabled | disabled | not_found | unavailable | not_confirmed -uiPortState: matches_9420 | mismatch | unconfirmed -configState: disabled | enabled | conflict | unconfirmed -portState: matches_9420 | mismatch | conflict | unconfirmed -conflictCount: -listenerState: listening | no_listener | refused | timeout | unknown | not_run -smokeState: ready | blocked | unknown | not_run -viralJourneyPreparation: ready | blocked | unknown | not_run -manualJourneyStatus: unverified | blocked | externally_recorded_not_claimed_by_this_tool -commandsRun: <只列命令名、退出码、归一化 status,不粘贴 raw output> -nextStep: <人工下一步> -notClaimed: no DevTools UI journey passed; no real-device journey passed; no viral journey passed -``` - -字段约束: - -- [ ] `actor` 必须能区分用户手动 UI 操作和 agent 只读复核。 -- [ ] `configState`、`portState`、`conflictCount` 必须来自只读配置摘要或后续只读 inspect 摘要,不得靠猜测填写。 -- [ ] `listenerState` 必须来自端口监听或连接摘要,不得用“用户说已开启”替代。 -- [ ] `smokeState` 必须来自只读 smoke access 摘要,不得用 listener ready 替代。 -- [ ] `viralJourneyPreparation` 只能说明手测包是否可准备,不得说明 journey 是否通过。 -- [ ] `manualJourneyStatus` 默认为 `unverified`;只有外部真实手测结果已由人工填报并通过独立复核时,才可写 `externally_recorded_not_claimed_by_this_tool`。 - -## 3. 允许的证据 - -- [ ] 脱敏文字摘要:用户确认的 UI 开关状态、端口是否为目标端口、是否出现安全提示。 -- [ ] 端口状态:`port=9420`,`listener=yes/no/unknown`,`connect=connected/refused/timeout/unknown`。 -- [ ] 配置摘要:`configState`、`portState`、`conflictCount`、诊断码、confidence 和 `nextHumanConfirmation`。 -- [ ] listener / smoke 状态:只记录 `status`、退出码、第一层 blocker 或 ready 摘要。 -- [ ] 运行命令和结果:只记录命令入口、退出码、归一化 `status`、关键 blocker / ready 语义;不要粘贴完整 stdout / stderr。 -- [ ] 传播链准备摘要:required journeys 是否列出、准备命令是否保持 no-side-effect、是否仍 blocked。 -- [ ] next step:例如继续人工开启、改用 UI 显示端口复测、检查 DevTools 实例、进入真实手测或保持 blocked。 - -推荐证据摘要: - -```text -status: blocked_no_listener -userManualConfirmation: Service Port enabled in UI; displayed port matches target -configState: disabled -portState: matches_9420 -conflictCount: 0 -listenerState: no_listener -smokeState: blocked -viralJourneyPreparation: blocked -commandsRun: inspect devtools port exit=0 status=blocked; devtools smoke exit=nonzero status=blocked -nextStep: 用户复核 DevTools 实例、端口和 IDE 状态后再次只读复测 -notClaimed: no DevTools UI journey passed; no real-device journey passed; no viral journey passed -``` - -## 4. 禁止的证据 - -- [ ] raw config、完整 JSON、完整 plist、完整 sqlite dump、完整日志、完整 stdout / stderr。 -- [ ] 截图原图、录屏原件、二维码、系统分享面板原图;若必须说明 UI,只写脱敏文字摘要。 -- [ ] 完整路径、用户目录、DevTools user-data 子路径、日志路径、配置路径、证据附件路径。 -- [ ] 文件名:尤其是配置、日志、缓存、local evidence、截图、录屏或用户数据文件名。命令入口可用 npm script 名称代替。 -- [ ] 账号、登录态、token、cookie、openid、unionid、微信号、昵称、头像、手机号、邮箱、设备唯一标识。 -- [ ] 项目历史、最近打开项目、真实项目名、真实 AppID、CloudBase 环境标识、post 原文、评论正文、反馈正文、图片 URL。 -- [ ] DevTools 自动设置修改记录,包括脚本改开关、改端口、改配置、清缓存、重启或恢复命令的输出。 -- [ ] 任何把准备、blocked draft、readiness、端口 ready 或 smoke ready 写成 journey passed 的证据。 - -## 5. 命令边界 - -默认只读允许: - -```bash -npm run inspect:devtools-port -- --port 9420 -npm run check:devtools-smoke -- --port 9420 -npm run prepare:viral-journey-run -- --port 9420 -node --no-warnings scripts/check-devtools-readiness.mjs -``` - -记录要求: - -- [ ] 如果命令输出完整路径、文件名或本地草稿名,汇报时必须改写成 ``、`` 或直接省略。 -- [ ] 如果 strict smoke 非零退出,记录为 `blocked`,不得用 `|| true` 后写成通过。 -- [ ] `prepare:viral-journey-run` 只说明手测准备状态;即使命令成功,也不能写成七条 journey passed。 - -始终禁止: - -- [ ] quit、open、preview、upload、login、logout。 -- [ ] kill、pkill、killall、launchctl、重启 IDE、清缓存、清 storage。 -- [ ] 修改 settings、Service Port 开关、端口号、配置文件、user data、local evidence。 -- [ ] 自动化点击 DevTools 设置 UI。 -- [ ] 生成、提交或移动截图、录屏、payload、raw log、raw config 或 local evidence 文件。 - -## 6. Status Mapping - -- [ ] `pre_manual_confirmation` - - 语义:尚未获得用户对 DevTools UI Service Port 的人工确认。 - - 可接受 evidence:AB/AC 只读摘要、当前端口 blocked 摘要、请求用户手动确认的 next step。 - - 不可写:listener ready、smoke ready、journey passed。 - -- [ ] `blocked_config_disabled` - - 语义:只读配置摘要或用户 UI 确认显示 Service Port 仍关闭。 - - 必填:`configState=disabled` 或 `uiServicePortState=disabled`,`nextStep=用户决定是否手动开启`。 - - 不可写:agent 已开启设置、端口 ready。 - -- [ ] `blocked_port_mismatch` - - 语义:用户 UI 显示端口不是目标端口,或配置 `portState=mismatch/conflict`。 - - 必填:目标端口、脱敏 UI 端口摘要、是否需要用 UI 端口复测。 - - 不可写:继续用 `9420` 断言 ready,或自动改端口。 - -- [ ] `blocked_no_listener` - - 语义:用户确认已开启或配置看似 enabled,但目标端口仍无 listener、连接 refused / timeout,或 smoke blocked。 - - 必填:`listenerState`、`smokeState`、运行命令、退出码和下一步人工复核项。 - - 不可写:设置已开所以 smoke passed。 - -- [ ] `blocked_smoke_access` - - 语义:端口有 listener 或连接信号,但 smoke access 仍无法确认可用 DevTools 入口。 - - 必填:listener 摘要、smoke blocker、下一步人工确认。 - - 不可写:listener ready 等于页面或传播链通过。 - -- [ ] `ready_for_manual_journey` - - 语义:用户已人工确认 UI 设置,端口 / listener / smoke 只读复核均 ready,传播链手测准备可继续。 - - 必填:`uiServicePortState=enabled`、`uiPortState=matches_9420`、`listenerState=listening` 或等价连接成功、`smokeState=ready`、`viralJourneyPreparation=ready`、`strictSubcommandNonzero=no`。 - - 必须写:`notClaimed: no DevTools UI journey passed; no real-device journey passed; no viral journey passed`,下一步是真实 DevTools 或真机手测。 - -- [ ] `manual_journey_passed_not_claimed_by_this_tool` - - 语义:外部人工结果文件或评审记录声称某些真实 journey 已通过;AC 工具只承认“存在外部人工证据待复核”,不生成通过结论。 - - 必填:外部证据状态摘要、独立 checker 是否通过、人工 reviewer 是否复核。 - - 必须写:`claimedBy=external_manual_evidence`,`notClaimedBy=AC_UI_confirmation_tool`。 - - 不可写:AC 工具自动判定 viral journey passed。 - -## 7. Static Guard 期望 - -后续如新增 AC 复核脚本或 readiness guard,应覆盖以下静态规则: - -- [ ] 副作用拒绝:源码不得包含写文件、删文件、移动文件、chmod/chown、settings 修改、Service Port 修改、quit/open/preview/upload、kill、清缓存或自动化 UI 点击。 -- [ ] 隐私拒绝:输出层必须有路径脱敏、文件名抑制、token / cookie / openid / session / account-like 字段过滤。 -- [ ] raw content suppression:不得打印 raw config、raw log、完整 stdout / stderr、完整 HTTP body、完整进程命令行。 -- [ ] 状态词汇 guard:只允许第 6 节定义的 AC 状态,或明确扩展并同步文档;不得新增 `ui_passed`、`journey_passed_by_tool`、`devtools_recovered` 这类误导状态。 -- [ ] ready 不等于 passed:脚本必须包含并输出 `port ready is not viral journey passed` 或等价语义。 -- [ ] blocked 必须有 next step:任何 `blocked_*` 输出必须带 `nextStep` 或 `nextHumanConfirmation`。 -- [ ] manual journey 外部化:若未来读取到人工结果中有 `passed`,外部评审记录可使用 `manual_journey_passed_not_claimed_by_this_tool`;当前 AC prepare 工具只输出 `manualJourneyStatus: unverified` 或复核待定,不得改写成工具自己的 passed。 - -## 8. 负向用例 - -- [ ] 用户未确认 UI,却输出 `ready_for_manual_journey`:应失败。 -- [ ] `configState=disabled` 却输出端口 ready 或 smoke ready:应失败,除非后续只读复测明确覆盖并记录了人工 UI 操作。 -- [ ] listener 仍为 `no_listener/refused/timeout`,却输出 `ready_for_manual_journey`:应失败。 -- [ ] strict smoke 非零退出,却把状态写成 ready:应失败。 -- [ ] `prepare:viral-journey-run` 成功后写成七条 journey passed:应失败。 -- [ ] local manual evidence、blocked draft、readiness 或 schema checker 通过后写成真实 UI passed:应失败。 -- [ ] 证据中出现 raw config、完整路径、文件名、账号、登录态、token、cookie、openid 或项目历史:应失败。 -- [ ] 证据中出现截图原图、录屏原件、二维码或系统分享面板原图:应失败。 -- [ ] 命令或脚本执行了 quit/open/preview/upload/kill/cache/settings 修改:应失败并停止复测。 -- [ ] AC 工具声称 `manual_journey_passed` 而没有 `not_claimed_by_this_tool` 限定:应失败。 - -## 9. 收尾验证命令 - -本 checklist 修改后运行: - -```bash -bash harness/init.sh -node scripts/check-json.mjs -node harness/check-harness.mjs -npm run check:devtools-port-forensics -node --no-warnings scripts/check-devtools-readiness.mjs -git diff --check -git status --short -``` - -期望: - -- [ ] 基础 harness、JSON、DevTools UI confirmation static guard、readiness 和 diff whitespace 检查通过。 -- [ ] `git status --short` 中可提交改动只包含 AC 文档、只读 prepare/static guard 脚本、readiness/package/harness 记录。 -- [ ] 没有运行任何 quit/open/preview/upload/kill/cache/settings 修改命令。 -- [ ] 没有生成或提交 local evidence、截图、录屏、raw config、raw log 或 DevTools 用户数据。 diff --git a/harness/devtools-service-port-ui-confirmation-product-brief.md b/harness/devtools-service-port-ui-confirmation-product-brief.md deleted file mode 100644 index fbca1a6..0000000 --- a/harness/devtools-service-port-ui-confirmation-product-brief.md +++ /dev/null @@ -1,169 +0,0 @@ -# AC 组 DevTools Service Port UI 确认产品 Brief - -日期:2026-06-17 - -角色:AC 组产品 agent - -## 产品目标 - -AC 组承接前序只读配置取证结论:当前状态仍为 `blocked`,诊断包含 `service_port_config_disabled`;配置层显示 `disabled`,目标端口状态为 `matches_9420`,冲突数量为 `0`,启用状态摘要为 `false`,端口值摘要为 `9420`。 - -本轮产品目标是把下一步从“继续由 agent 猜测或恢复环境”收束为“用户在 WeChat DevTools UI 中人工确认并决定是否开启 Service Port”,再由 agent 只读复核端口状态、smoke 入口和传播链手测准备状态。 - -AC 的交付不是让传播链通过,而是让评测者清楚区分三件事: - -- 当前仍是 DevTools 环境入口 `blocked`。 -- 用户人工开启或确认 UI 设置后,只能进入端口复核和手测准备。 -- 只有真实 DevTools 或真机旅程产生脱敏证据后,传播链 journey 才能被判断为 `passed`。 - -## 非目标 - -- 不自动修改 WeChat DevTools 设置,不替用户启用或关闭 Service Port。 -- 不执行 quit、open、preview、upload、kill、清缓存、重启 IDE、切换项目、修改本地配置或清理用户数据。 -- 不新增或修改小程序业务能力、传播链 UI、分享策略、归因逻辑或证据模板。 -- 不把配置 disabled、用户手动开启、端口 ready、smoke access ready、准备命令完成或草稿生成写成 viral journey passed。 -- 不采集或输出本机 raw config、完整路径、文件名、账号、登录态、token、cookie、完整日志或项目历史。 - -## 用户与评测价值 - -用户价值是把阻塞动作交还给真正有 UI 控制权的人,避免 agent 在本机环境中做越权恢复。评测价值是降低误判:`service_port_config_disabled` 指向明确的人工作业,但它本身不是产品失败,也不是传播链通过。 - -对评测者来说,AC brief 应提供一套低歧义口径: - -- 只读诊断可以说明“为什么不能继续”。 -- 用户 UI 确认可以说明“设置是否已由人处理”。 -- 端口监听和 smoke access 可以说明“是否能开始手测”。 -- 真实 journey evidence 才能说明“用户侧传播链是否通过”。 - -## 只读边界 - -agent 允许做的事情: - -- 读取项目内文档和已脱敏的诊断摘要。 -- 运行不会改变 IDE、模拟器、配置、缓存、进程或用户数据的只读检查。 -- 在用户完成 UI 操作后,复跑只读端口 inspect、只读 smoke access 检查和传播链手测准备检查。 -- 输出脱敏状态摘要,例如 `blocked_config_disabled`、`blocked_no_listener`、`ready_for_manual_journey`、`unknown`、诊断码、端口是否监听、连接是否成功、是否仍需人工确认。 - -agent 禁止做的事情: - -- 通过脚本、CLI、系统命令或自动化 UI 去开启、关闭或修改 Service Port。 -- 退出、打开、预览、上传、终止、重启、清缓存或刷新 WeChat DevTools。 -- 读取、复制、提交或展示本机 raw config、完整日志、完整路径、文件名、账号、登录态、token、cookie 或项目历史。 -- 将任何环境检查通过写成 DevTools UI passed、真机 passed、朋友圈 passed、系统分享 passed、归因 passed 或 viral journey passed。 - -一旦继续判断必须依赖 UI 开关变化,agent 应停在 `blocked`,请求用户手动确认。 - -## 人工确认步骤 - -以下步骤只应由用户在 WeChat DevTools UI 中手动执行: - -1. 打开 WeChat DevTools 的 Settings。 -2. 进入 Security Settings。 -3. 找到 Service Port。 -4. 人工确认开关当前是否开启。 -5. 如果用户愿意继续,手动开启 Service Port。 -6. 人工确认 UI 显示的端口是否为 `9420`;如果不是,只记录“端口与目标不一致”或提供脱敏端口值。 -7. 返回本对话,告知 UI 状态:已开启、仍关闭、端口不一致、找不到设置项,或无法操作。 - -用户完成上述步骤后,agent 只复跑只读检查。复跑结果只能说明“端口入口是否可继续”,不能自动宣称传播链通过。 - -## 允许与禁止的证据 - -允许的证据: - -- 脱敏诊断摘要:状态、诊断码、目标端口匹配情况、冲突数量、启用状态摘要、端口值摘要。 -- 只读 listener / connection 摘要:有无监听、连接成功或拒绝、IPv4/IPv6 的归一化结果。 -- 用户人工确认结果:例如“用户确认 UI 中 Service Port 已开启,端口为目标端口”。 -- 经裁剪且不含账号、路径、项目名、登录态或个人信息的 UI 截图;截图只用于证明用户看到的开关状态。 -- 手测准备结果:模板完整性、必跑 journey 是否列出、本地证据是否存在、是否仍为 blocked 或 not covered。 - -禁止的证据: - -- raw config、完整日志、完整路径、文件名、账号、登录态、token、cookie、项目历史或本地配置内容。 -- 自动执行设置修改、quit、open、preview、upload、kill、清缓存或重启的输出。 -- 只有配置 disabled、用户手动开启、端口 ready、smoke ready、准备命令通过或 blocked draft 的“通过”结论。 -- 缺少真实 UI 观察、payload 说明或脱敏证据的 viral journey passed。 - -## 成功与 Blocked 输出语义 - -AC 文档完成的成功语义: - -```text -status: product_brief_ready -meaning: AC 已定义 UI 人工确认边界、证据口径和下一步分叉;没有执行 DevTools 恢复或传播链手测。 -``` - -当前环境仍阻塞时的输出语义: - -```text -status: blocked -diagnosis: service_port_config_disabled -meaning: 只读配置摘要显示 Service Port 未开启;需要用户在 UI 中人工确认并决定是否开启。 -journeyStatus: unverified -``` - -用户已人工开启但端口仍不可连接时: - -```text -status: blocked -diagnosis: service_port_not_listening_after_manual_confirmation -meaning: 用户已处理 UI 设置,但只读复核仍未看到可用端口;继续保持环境 blocked。 -journeyStatus: unverified -``` - -端口可连接后的输出语义: - -```text -status: ready_for_manual_journey -meaning: Service Port 入口可用于继续 DevTools smoke 或传播链手测准备;这不是 viral journey passed。 -journeyStatus: pending_real_evidence -``` - -传播链通过只能在以下条件同时满足时出现:真实 DevTools 或真机 journey 已执行、每条目标 journey 有具体实际观察、必要 payload 或限制说明齐备、脱敏 evidence 非空、评测口径没有把环境 ready 替代为产品 passed。 - -## 与用户侧裂变目标的关系 - -用户侧裂变目标包括分享入口、接收者确认或评论后的二跳引导、系统分享面板、朋友圈渠道、风险态 gating 和 attribution 记录。AC 不新增这些能力,也不证明这些能力通过。 - -AC 与裂变目标的关系是“解除真实手测前的环境阻塞歧义”: - -- 如果 Service Port 仍关闭或不可连接,裂变 journey 只能保持 `blocked` 或 `unverified`。 -- 如果 Service Port ready,只能说明可以继续准备和执行真实手测。 -- 如果真实系统分享、朋友圈或 attribution 没有被观察并记录,不能把任何传播链状态写成 `passed`。 - -## 评测口径 - -评测时必须分层判断: - -- `config_disabled`:配置层说明下一步需要人工 UI 确认;不是产品失败。 -- `manual_enabled`:用户完成 UI 操作;不是端口 ready,也不是 smoke passed。 -- `port_ready`:端口检查可连接;只能进入真实 smoke 或准备手测。 -- `smoke_ready`:DevTools 入口可用于检查页面;不是传播链 journey passed。 -- `journey_passed`:只来自真实 UI 或真机观察,并包含对应 journey 的脱敏证据。 - -评测不得接受以下表述: - -- “配置已改成开启,所以传播链通过。” -- “端口是 `9420`,所以朋友圈 passed。” -- “准备命令通过,所以七条 journey passed。” -- “用户说已经打开开关,所以 payload 已验证。” -- “端口 ready 后无需继续手测。” - -## 下一步分叉 - -- 用户未确认 UI:保持 `blocked`,请用户按 UI 步骤确认 Service Port。 -- 用户确认仍关闭:保持 `blocked`,等待用户决定是否手动开启。 -- 用户确认已开启且端口为 `9420`:复跑只读端口 inspect 和只读 smoke access;结果仍不得写成 viral journey passed。 -- 用户确认已开启但端口不是 `9420`:记录端口不一致,使用用户确认的脱敏端口值复核只读状态,或继续保持 `blocked`。 -- 复核后仍无 listener 或连接失败:保持 `blocked`,说明端口入口仍不可用,建议用户人工检查 IDE 状态、端口设置或换环境。 -- 复核后端口、listener、smoke 和手测准备都 ready:输出 `ready_for_manual_journey`,继续执行真实 DevTools 或真机手测,再进入传播链 evidence 采集。 -- 真实 journey 执行后失败:记录为产品或体验 `failed`,必须带具体实际观察和下一步修复建议。 -- 真实 journey 执行后通过:仅对应已观察、有脱敏证据的 journey 写 `passed`;未覆盖项保持 `blocked`、`not_covered` 或 `pending`。 - -## 建议的脚本/Guard 需求 - -- 增加一个只读 UI-confirmation 后复核入口:读取用户声明的 UI 状态参数,只运行端口 inspect、listener/connection 检查和手测准备检查,不执行任何设置修改或 IDE 控制动作。 -- 增加输出语义 guard:禁止把 `manual_enabled`、`port_ready`、`smoke_ready`、准备完成、草稿生成或 blocked summary 写成 viral journey passed。 -- 增加敏感信息 guard:扫描输出和提交内容,拒绝 raw config、完整路径、文件名、账号、登录态、token、cookie、完整日志和项目历史。 -- 增加副作用 guard:默认路径和 AC 复核入口中禁止出现设置修改、quit、open、preview、upload、kill、清缓存、重启或自动 UI 操作。 -- 增加下一步分叉 guard:当状态仍为 `blocked_*` 时必须带人工 follow-up;当状态为 `ready_for_manual_journey` 时必须明确“端口 / smoke ready 不是 journey passed,下一步仍需真实手测”。 diff --git a/harness/devtools-service-recovery-checklist.md b/harness/devtools-service-recovery-checklist.md deleted file mode 100644 index cb070dc..0000000 --- a/harness/devtools-service-recovery-checklist.md +++ /dev/null @@ -1,239 +0,0 @@ -# DevTools 服务端口受控恢复 QA 清单 - -日期:2026-06-14 - -范围:用于 `codex/iter-devtools-service-recovery` 分支在 `/tmp/street-tasks-iter-worktrees/devtools-recovery` 上执行 WeChat DevTools 服务端口受控恢复。本清单定义执行前确认、退出/重启风险、恢复命令、恢复后判断、blocked 记录、ready 后最小 smoke 和证据脱敏;它不是已完成的手测记录。 - -已知背景:M 组诊断到 DevTools-like 进程声明 `--ide-http-port 9420`,但 `9420` 没有监听,`curl` connection refused,CLI `open` 超时。N 组恢复的目标是先保留 blocked 证据,再以最小影响退出或重启 DevTools,重新打开当前项目,并用 `check-devtools-smoke-access` 判断入口是否 ready。 - -## 0. 恢复前检查 - -- [ ] 确认当前工作树、分支和提交。 - - ```bash - pwd - git branch --show-current - git rev-parse --short HEAD - git status --short - ``` - - 期望:工作目录为 `/tmp/street-tasks-iter-worktrees/devtools-recovery`,分支为 `codex/iter-devtools-service-recovery`;工作区只包含本轮预期文件,不包含脚本或业务代码改动。 - -- [ ] 跑基础 harness,确认不是仓库基础状态异常。 - - ```bash - bash harness/init.sh - ``` - - 期望:JSON 检查和 harness 自检通过。若失败,先记录 baseline 异常,不继续把 DevTools 入口状态写成 ready。 - -- [ ] 记录恢复前 blocked 现状。 - - ```bash - node scripts/check-devtools-smoke-access.mjs \ - --project /tmp/street-tasks-iter-worktrees/devtools-recovery \ - --port 9420 - ``` - - 期望:如果仍是 M 组现象,报告应为 `status: blocked`,且摘要包含服务端口未监听或 `ide-http-port` 进程声明。只保存脱敏摘要,不提交完整原始日志。 - -- [ ] 确认 DevTools 进程、端口和项目归属。 - - ```bash - ps aux | rg -i "wechat|微信|devtools|ide-http-port" - lsof -nP -iTCP:9420 -sTCP:LISTEN - nc -vz 127.0.0.1 9420 - curl -sS --max-time 3 http://127.0.0.1:9420/ || true - ``` - - 期望:能判断是否存在多个 DevTools 实例、多个 `--ide-http-port` 或不同 worktree 项目。不要把其他 worktree 的可用端口误记为当前分支 ready。 - -- [ ] 确认退出 DevTools 的影响已被接受。 - - 询问或确认当前没有其他 agent 正在依赖同一个 DevTools 实例调试。 - - 保存当前 DevTools 控制台关键 blocked 摘要或截图编号。 - - 记录 DevTools UI 中“设置 -> 安全设置 -> 服务端口”的状态:开启 / 未开启 / 无法确认。 - - 若 DevTools 里有未保存配置、正在上传、预览二维码、真机调试或云开发面板操作,先停止恢复并记录风险。 - -## 1. 退出 / 重启风险说明 - -- 正常退出或 CLI `quit` 会关闭 DevTools IDE,可能中断模拟器、真机调试、预览二维码、当前控制台日志和其他已打开项目。 -- 退出后重新 `open` 可能刷新 DevTools 内部缓存、重新加载基础库或重新触发登录 / AppID / 服务端口设置;这类变化要记录为环境变化,而不是产品行为变化。 -- 不默认执行强制杀进程、清缓存、删除配置或改端口。只有正常退出和 CLI `quit` 都失败,并且执行者明确接受影响时,才升级到人工处理。 -- 如果恢复动作让状态更差,例如 DevTools 无法启动、服务端口设置消失、项目路径打开错误,立即停止后续 smoke,把本轮写成 `blocked`,并记录最后一个成功观察点和失败命令。 - -## 2. 恢复命令 - -先准备变量,避免误敲到其他项目: - -```bash -PROJECT=/tmp/street-tasks-iter-worktrees/devtools-recovery -PORT=9420 -DEVTOOLS_CLI=${WECHAT_DEVTOOLS_CLI:-/Applications/wechatwebdevtools.app/Contents/MacOS/cli} -``` - -- [ ] 确认 CLI 可用。 - - ```bash - "$DEVTOOLS_CLI" --help - "$DEVTOOLS_CLI" quit --help - "$DEVTOOLS_CLI" open --help - ``` - - 期望:能看到 `quit` 和 `open` 命令帮助。若 CLI 路径不同,只在本机 shell 变量中调整,不写入仓库。 - -- [ ] 首选从 DevTools UI 正常退出。 - - 菜单退出整个微信开发者工具,而不是只关闭模拟器窗口。 - - 等待 10 到 30 秒后检查旧进程是否退出。 - - ```bash - ps aux | rg -i "wechat|微信|devtools|ide-http-port" - ``` - -- [ ] 如果 UI 无法正常退出,执行 CLI `quit`。 - - ```bash - "$DEVTOOLS_CLI" quit --port "$PORT" - ``` - - 期望:IDE 退出或 CLI 返回可判断结果。若返回 timeout、connection refused 或无响应,记录命令、端口、耗时和摘要,不要继续叠加强制动作。 - -- [ ] 重新打开当前项目。 - - ```bash - "$DEVTOOLS_CLI" open --project "$PROJECT" --port "$PORT" - ``` - - 期望:DevTools 打开当前 project,并启动或连接 `PORT=9420` 的服务端口。若 `open` 超时,继续用下一步诊断脚本记录,不把项目 UI 短暂闪现写成 ready。 - -- [ ] 如果需要由脚本托管 `open` 超时和脱敏输出,执行: - - ```bash - node scripts/check-devtools-smoke-access.mjs \ - --project "$PROJECT" \ - --port "$PORT" \ - --attempt-open \ - --timeout-ms 20000 - ``` - - 期望:脚本输出 `status: ready` 或明确 blocked 原因。该脚本不会退出 DevTools、清缓存、写项目文件或启动 preview。 - -## 3. 恢复后判断 - -- [ ] 执行无副作用 ready 检查。 - - ```bash - node scripts/check-devtools-smoke-access.mjs \ - --project /tmp/street-tasks-iter-worktrees/devtools-recovery \ - --port 9420 - ``` - -- [ ] 判定为 `ready` 的最低条件: - - `check-devtools-smoke-access` 输出 `status: ready`。 - - `9420` 不再是 connection refused;`lsof` 或脚本能确认端口监听。 - - DevTools 打开的是 `/tmp/street-tasks-iter-worktrees/devtools-recovery`,不是其他 worktree。 - - `git branch --show-current` 仍是 `codex/iter-devtools-service-recovery`。 - -- [ ] 判定为 `blocked` 的条件: - - `9420` 仍无监听或仍 connection refused。 - - CLI `open` / `attempt-open` 继续 timeout。 - - DevTools UI 无法确认服务端口开启。 - - DevTools 打开了错误项目、错误端口或多个实例状态无法区分。 - - 项目能手动打开但无法编译进入模拟器,且原因属于工具入口或环境权限。 - -- [ ] ready 只表示 DevTools smoke access 恢复,不代表任何产品旅程已通过。只有执行第 5 节真实操作并记录证据后,才能给用户旅程写 `passed`、`failed` 或 `blocked`。 - -## 4. 如果仍 blocked:记录字段 - -blocked 记录至少包含: - -- `blockedAt`:日期和时间,带时区。 -- `branch`:`codex/iter-devtools-service-recovery`。 -- `commit`:`git rev-parse --short HEAD`。 -- `worktree`:`/tmp/street-tasks-iter-worktrees/devtools-recovery`。 -- `devtoolsVersion`:DevTools 版本;无法打开时写“无法确认”。 -- `baseLibVersion`:调试基础库版本;无法确认时写“无法确认”。 -- `ideHttpPort`:`9420` 或实际检查端口。 -- `servicePortSetting`:开启 / 未开启 / 无法确认。 -- `processEvidence`:DevTools-like 进程是否存在、是否声明 `--ide-http-port`,只写摘要。 -- `listenEvidence`:`lsof` / `nc` / `curl` / 诊断脚本的脱敏结论。 -- `recoveryAttempted`:UI 正常退出、CLI `quit`、CLI `open`、`attempt-open` 中实际执行了哪些。 -- `actualResult`:connection refused、wait IDE port timeout、打开错误项目、无法确认服务端口等摘要。 -- `rollbackOrStopPoint`:在哪一步停止,是否保留旧 blocked 状态,是否需要人工 UI 权限。 -- `manualJourneyImpact`:哪些最小 smoke 旅程因此未执行。 -- `nextAction`:人工开启服务端口、换端口、换机器、升级 / 重装 DevTools、补真实 AppID / 登录态或改用 UI 手测。 -- `evidenceLocation`:本地附件编号或安全外部位置,不写原始绝对路径。 - -示例摘要: - -```text -blockedAt: 2026-06-14 11:20 CST -branch: codex/iter-devtools-service-recovery -commit: -worktree: /tmp/street-tasks-iter-worktrees/devtools-recovery -ideHttpPort: 9420 -servicePortSetting: UI 显示已开启 -processEvidence: DevTools-like 进程存在,命令声明 --ide-http-port 9420 -listenEvidence: 9420 无监听,nc/curl 为 connection refused -recoveryAttempted: UI 正常退出未成功;CLI quit 返回 timeout;attempt-open 20s timeout -actualResult: DevTools 服务端口仍不可连接,未能确认当前项目 ready -rollbackOrStopPoint: 未强制杀进程,保持 blocked,等待有 UI 权限执行者处理 -manualJourneyImpact: DevTools 编译、地图首屏、发布状态、详情信任区和真机预览均未执行 -nextAction: 人工重新开启服务端口或换机验证,再重跑 check-devtools-smoke-access -evidenceLocation: 本地附件 S-recovery-01,原始日志不提交 -``` - -## 5. 如果 ready:最小 smoke 旅程 - -端口和项目 ready 后,先跑最小闭环;不要直接扩大到完整回归。 - -- [ ] DevTools 编译和地图首屏。 - - 打开当前项目并普通编译。 - - 通过标准:进入 `pages/map/map`,地图、marker、底部 tabBar 和列表入口可见;Console 没有阻断运行的首条红色错误。 - - 失败记录:首条错误摘要、页面路径、是否白屏、是否仍有 `WAServiceMainContext timeout`。 - -- [ ] 地图列表或 marker 到详情。 - - 从列表或 marker 进入一条任务详情。 - - 通过标准:详情页展示同一任务标题、正文、状态、发布者信息、TrustInsight、信任动作和评论入口;不出现“任务不存在”。 - - 失败记录:入口方式、任务别名、post id 摘要和详情页实际状态。 - -- [ ] 发布状态和定位恢复路径。 - - 进入发布页,至少覆盖游客态或登录态一种;点击当前位置确认,覆盖允许、拒绝或失败重试中的一种真实分支。 - - 通过标准:定位块文案、底部主按钮、定位中 / 失败 / 重试状态随真实权限变化;失败不清空已填内容。 - - 失败记录:权限初始状态、点击步骤、toast / 文案、是否卡住 loading。 - -- [ ] 详情信任动作和评论入口。 - - 对 active 任务执行一次确认或过时,并打开评论入口。 - - 通过标准:TrustInsight / 数字刷新,重复动作被阻止;评论入口在游客或登录态下给出正确引导。 - - 失败记录:动作前后数量、toast、评论弹窗或登录引导状态。 - -- [ ] 发布成功到详情。 - - 已登录且环境允许时,填写最小有效表单并提交。 - - 通过标准:只创建一条任务,成功后跳转新任务详情;返回地图后新任务可见。 - - 若缺真实登录、AppID、CloudBase 或 Storage,写 `blocked`,不要写 `passed`。 - -- [ ] 图片或上传失败最小覆盖。 - - 选择 1 张图片;无法发布时至少验证缩略图、删除和上传失败提示。 - - 通过标准:图片状态不破坏表单;上传失败有提示;成功发布后详情可见图片或云端引用。 - - 失败记录:图片数量、大小档位、上传错误摘要和表单是否保留。 - -- [ ] 真机预览或真机调试。 - - CLI `preview` 成功或 UI 可生成二维码后,用真机打开。 - - 通过标准:地图、发布定位、键盘安全区、详情信任区在真机上不遮挡、不白屏。 - - 若 preview 无法生成,记录 preview blocker;DevTools 模拟器不能替代真机结论。 - -## 6. 证据脱敏 - -- [ ] 原始截图、录屏、二维码、完整 Console、Network、云函数日志、数据库截图和 `check-devtools-smoke-access --attempt-open` 原始输出只放 ignored 本地附件目录或外部安全位置。 -- [ ] 可提交文档只写脱敏摘要:角色用“用户 A / 游客态 / 管理员账号”,地点用“测试 POI / 默认中心附近”,云端资源用“fileID 已生成,原值本地留存”。 -- [ ] 不提交真实 AppID、openId、unionId、头像 URL、昵称、手机号、精确经纬度、CloudBase 环境 ID、requestId、完整 `cloud://`、token、cookie、二维码或个人设备标识。 -- [ ] CLI 和端口日志只摘录最小必要错误类型,例如 connection refused、wait IDE port timeout、端口未监听、打开错误项目;不要粘贴完整堆栈、header、环境变量或本机用户路径。 -- [ ] 本清单允许写当前测试 worktree 路径;其他本机路径一律泛化为 ``、`` 或本地附件编号。 -- [ ] 写入任何可提交报告前运行并人工复核: - - ```bash - git status --short --ignored - git diff -- harness '*.md' '*.json' - rg --no-ignore -n -i "(api[_-]?key|secret|token|password|passwd|pwd|private[_-]?key|session|cookie|authorization|bearer|access[_-]?token|refresh[_-]?token|client[_-]?secret|appsecret|wx[0-9a-f]{16,}|sk-[A-Za-z0-9_-]{20,}|AKIA[0-9A-Z]{16})" . - ``` - - 期望:真实附件和 local 结果仍是 ignored;secret scan 没有新增真实敏感值。若命中的是清单里的规则说明,也要人工确认不是实际凭据。 diff --git a/harness/devtools-service-recovery-product-brief.md b/harness/devtools-service-recovery-product-brief.md deleted file mode 100644 index 676949c..0000000 --- a/harness/devtools-service-recovery-product-brief.md +++ /dev/null @@ -1,102 +0,0 @@ -# N 组 DevTools 服务端口受控恢复产品简报 - -分支:`codex/iter-devtools-service-recovery` - -工作目录:`/tmp/street-tasks-iter-worktrees/devtools-recovery` - -对象:继续推进真实 WeChat DevTools smoke 的产品、QA 和开发 agent。 - -## 问题 - -M 组已经把当前阻塞从“还没手测”收敛为更具体的 DevTools 服务端口故障:本机存在声明 `--ide-http-port 9420` 的 DevTools-like 进程,但 9420 对 `127.0.0.1` 和 `::1` 都返回 `ECONNREFUSED`,CLI `open` 只能 timeout。也就是说,当前不是产品旅程已经失败,而是尚未稳定进入真实 DevTools smoke。 - -如果继续只新增文档、清单或外围校验,会延长“自动脚本都绿但真实 smoke 仍没入口”的状态。N 组需要定义一个受控恢复口径:在明确记录风险和前置证据后,允许有 UI 权限的执行者退出或重启 WeChat DevTools,重新打开当前 worktree,并按结果进入最小 smoke 或记录新的 blocked 证据。 - -## 价值 - -- 把端口恢复从临时人工动作变成可审计、可复现的产品验证前置步骤。 -- 降低误判风险:服务端口恢复只说明 smoke 入口恢复,不说明地图、发布、详情或云端路径通过。 -- 给后续 agent 一个明确分叉:恢复成功就进入最小 smoke;恢复失败就留下新的 blocked 证据,而不是继续猜测 9420 状态。 -- 避免无限追加 harness 能力,却没有推进真实用户旅程验证。 - -## 范围内 - -- 记录恢复前状态:当前分支、commit、worktree 路径、DevTools 进程声明、9420 监听结果、CLI open timeout 或 connection refused 摘要。 -- 允许在明确记录风险后,通过 WeChat DevTools UI 正常退出,或在确认没有未保存 DevTools 操作后重启 DevTools。 -- 重新打开当前 worktree:`/tmp/street-tasks-iter-worktrees/devtools-recovery`。 -- 复查服务端口:确认 9420 是否监听,`scripts/check-devtools-smoke-access.mjs` 是否从 `blocked` 转为可开始 smoke 的状态。 -- 端口恢复后执行最小 smoke:打开项目、完成一次编译、确认地图首屏可见,再进入发布页和任一详情页做只读检查。 -- 端口仍失败时,记录新的 blocked 证据,包括恢复动作、端口结果、CLI 结果、DevTools UI 观察和下一步建议。 - -## 非目标 - -- 不声明真实 WeChat DevTools 或真机 smoke 已经通过。 -- 不新增产品功能,不修改地图、发布、详情、云函数或存储逻辑。 -- 不用脚本检查替代用户旅程手测。 -- 不处理所有 DevTools 内部异常,只聚焦当前服务端口声明与实际监听不一致的问题。 -- 不提交本地截图、录屏、完整路径、账号信息、CloudBase fileID 或其他敏感原始证据。 - -## 恢复动作边界 - -允许动作: - -- 在记录恢复前证据后,通过 DevTools UI 正常退出当前 DevTools 进程。 -- 如果 UI 退出不可用,允许重启明确识别为 WeChat DevTools 的进程;执行前必须记录进程匹配依据和风险。 -- 重启后只打开当前 worktree,不切换到其他实验目录。 -- 重新检查 9420 监听、CLI open、DevTools 编译结果和控制台首条错误。 -- 如端口恢复但 CLI 仍异常,可改用 DevTools UI 手动打开项目,并把 CLI 失败记为环境限制。 - -禁止动作: - -- 不使用宽泛进程清理命令误杀无关应用。 -- 不清除用户全局配置、登录态、CloudBase 数据库、Storage 文件或小程序项目配置。 -- 不把 `project.private.config.json`、本地 DevTools 缓存、真实截图或录屏加入版本控制。 -- 不在未记录风险和恢复前状态的情况下直接重启 DevTools。 -- 不把“端口恢复”“项目打开”“自动脚本通过”写成“产品通过”。 - -恢复前需要确认的风险: - -- 退出或重启 DevTools 可能中断其他 worktree 的手测现场。 -- DevTools 本地缓存、模拟器状态和控制台上下文可能丢失。 -- 如果当前有未记录的手测观察,应先写入 ignored local 结果文件或脱敏摘要草稿,再执行恢复。 - -## 成功标准 - -服务端口恢复成功需要同时满足: - -- 9420 对本机地址不再是 `ECONNREFUSED`,诊断脚本不再报告 `service port 9420` blocked。 -- DevTools 能打开 `/tmp/street-tasks-iter-worktrees/devtools-recovery`,项目路径和分支记录一致。 -- 普通编译可以完成,若有错误或警告,已记录首条错误和影响范围。 -- 最小 smoke 至少完成到“地图首屏可见、发布页可打开、任一详情页可打开”的层级,并留下脱敏证据。 - -注意:即使以上全部满足,也只能说明“最小 smoke 入口和只读主路径初步可用”。发布定位授权、图片上传、发布后跳转、信任动作刷新、评论、跨用户和云端路径仍需要后续完整手测。 - -## 失败标准 - -以下任一情况应记录为新的 `blocked`,而不是继续标记产品通过: - -- 重启后 9420 仍未监听,或 `127.0.0.1` / `::1` 仍为 `ECONNREFUSED`。 -- CLI `open` 继续 timeout,且 DevTools UI 也无法稳定打开当前 worktree。 -- DevTools 能打开但编译失败,或首屏停留在白屏/运行时错误,无法进入最小 smoke。 -- 执行者无法确认是否可以安全退出或重启 DevTools。 -- 观察到的错误需要产品代码、项目配置或云端资源介入,已经超出服务端口恢复范围。 - -失败记录至少包括: - -- 恢复前诊断摘要。 -- 执行过的恢复动作和时间。 -- 恢复后端口、CLI、DevTools UI、编译或首屏结果。 -- 当前判断:`blocked`、阻塞阶段、影响范围、下一步负责人或建议。 - -## 如何避免误判为产品通过 - -- 把状态拆开记录:`service_recovered`、`minimal_smoke_started`、`minimal_smoke_passed`、`full_product_passed` 不能互相替代。 -- 端口恢复只升级“可开始真实 smoke”,不能升级地图、发布、详情、云端或跨用户功能状态。 -- 自动脚本输出通过只说明结构、逻辑或端口检查通过;用户可见功能仍以 DevTools 或真机观察为准。 -- 最小 smoke 只覆盖打开、编译和只读浏览,不覆盖完整发布闭环。 -- 没有具体 DevTools 版本、基础库版本、设备/模拟器信息、实际步骤、实际结果和脱敏证据的条目,不能写 `passed`。 -- 如果结果来自 example JSON、mock 数据、自动脚本或占位描述,必须标记为 `not_covered` 或 `blocked`。 - -## 下一步口径 - -N 组建议下一位有 UI 权限的执行者先运行 M 组诊断脚本确认仍 blocked,再按本文档执行受控退出/重启 DevTools。恢复成功后,立即执行最小 smoke 并把结果写入 ignored local 手测结果;恢复失败后,不再新增外围文档,直接把新的 blocked 证据写入 harness 记录或交给能操作 DevTools 环境的人继续处理。 diff --git a/harness/devtools-smoke-checklist.md b/harness/devtools-smoke-checklist.md deleted file mode 100644 index 7a16303..0000000 --- a/harness/devtools-smoke-checklist.md +++ /dev/null @@ -1,202 +0,0 @@ -# WeChat DevTools Smoke Access 排查与执行清单 - -日期:2026-06-14 - -范围:用于 `codex/iter-devtools-smoke-unblock` 分支恢复真实 WeChat DevTools / 真机 smoke access。此清单只定义排查、blocked 记录和端口恢复后的最小 smoke 旅程;它不是已完成的手测记录。 - -## 0. 进入前检查 - -- [ ] 确认当前工作树和分支。 - - ```bash - pwd - git branch --show-current - git rev-parse --short HEAD - git status --short - ``` - - 期望:工作目录为 `/tmp/street-tasks-iter-worktrees/devtools-smoke`,分支为 `codex/iter-devtools-smoke-unblock`;工作区只包含本轮预期文件。 - -- [ ] 跑基础 harness。 - - ```bash - bash harness/init.sh - ``` - - 期望:JSON 检查和 harness 自检通过。若失败,先记录基础状态异常,不继续把 DevTools smoke 写成 blocked 或 passed。 - -- [ ] 确认本轮不修改真实配置。 - - `project.config.json` 继续使用公开占位 `touristappid`。 - - `project.private.config.json`、真实 AppID、账号信息、二维码、截图和日志不提交。 - - 如需真实 AppID 或登录态,只在本机 DevTools / ignored 文件中处理。 - -- [ ] 确认已有手测工具链位置。 - - 结果模板:`harness/manual-test-results.example.json` - - 本地结果文件:`harness/manual-test-results.local*.json` - - 本地附件目录:`harness/manual-evidence-artifacts/` - - 摘要草稿:`harness/manual-test-summary.local*.md` - -## 1. 端口与进程检查 - -当前阻塞迹象:DevTools 进程命令包含 `--ide-http-port 9420`,但 9420 没有监听;`curl` connection refused;CLI `open` 20s timeout。 - -- [ ] 查 DevTools 进程和启动参数。 - - ```bash - ps aux | rg -i "wechat|微信|devtools|ide-http-port" - ``` - - 期望:能看到 WeChat DevTools 主进程;若命令行有 `--ide-http-port 9420`,记录该端口。 - -- [ ] 检查端口是否实际监听。 - - ```bash - lsof -nP -iTCP:9420 -sTCP:LISTEN - nc -vz 127.0.0.1 9420 - curl -sS --max-time 3 http://127.0.0.1:9420/ || true - ``` - - 期望:至少 `lsof` 能看到监听进程,`nc` 不应 connection refused。`curl` 返回 404、鉴权错误或非业务响应都比 connection refused 更接近可用;完整返回不要提交,只记录脱敏摘要。 - -- [ ] 若 9420 不监听,先在 DevTools UI 检查安全设置。 - - 打开微信开发者工具。 - - 进入“设置 -> 安全设置”。 - - 确认“服务端口”已开启。 - - 关闭设置窗口后重新执行端口监听检查。 - -- [ ] 若 UI 显示已开启但端口仍不监听,退出并重启 DevTools。 - - 先正常退出 DevTools,避免只关模拟器窗口。 - - 确认旧进程退出后再重新打开 DevTools。 - - 重新打开本 worktree 项目:`/tmp/street-tasks-iter-worktrees/devtools-smoke`。 - - 再次检查 `ps`、`lsof`、`nc` 和 `curl`。 - -- [ ] 若 DevTools 开了多个项目或多个版本,确认 CLI 连接的是同一个实例。 - - 记录进程命令中的项目路径、端口和 DevTools 应用路径。 - - 若存在多个 `--ide-http-port`,逐个检查监听状态。 - - 不要把另一个 worktree 的 CLI 成功误记成本分支通过。 - -## 2. CLI open / preview 检查 - -- [ ] 端口监听后再执行 CLI `open`。 - - ```bash - /Applications/wechatwebdevtools.app/Contents/MacOS/cli open --project /tmp/street-tasks-iter-worktrees/devtools-smoke - ``` - - 期望:CLI 能连接已开启服务端口,并在 DevTools 中打开当前项目。若本机 CLI 路径不同,记录实际 CLI 路径,但不要提交含用户名的绝对路径。 - -- [ ] 若 `open` 仍 20s timeout,记录为 access blocker。 - - 记录命令类型:`open` - - 记录端口:例如 `9420` - - 记录现象:`wait IDE port timeout` 或 connection refused - - 记录 DevTools UI 服务端口状态:开启 / 未开启 / 无法确认 - - 记录项目路径:只写 `/tmp/street-tasks-iter-worktrees/devtools-smoke` - -- [ ] `open` 成功后再执行 CLI `preview`。 - - ```bash - /Applications/wechatwebdevtools.app/Contents/MacOS/cli preview --project /tmp/street-tasks-iter-worktrees/devtools-smoke - ``` - - 期望:能生成预览结果或二维码。二维码、真实 AppID、上传结果和完整 CLI 日志只本地保存;可提交记录只写“preview 生成成功 / 失败摘要”。 - -- [ ] 若 CLI 仍不可用但 DevTools UI 可操作,允许转为 UI 手动 smoke。 - - 必须记录“CLI blocked,但 UI smoke 可执行”。 - - UI smoke 的每个 journey 仍要按真实执行结果填 `passed`、`failed`、`blocked` 或 `not_covered`。 - - 不因 UI 可打开就把 CLI access 问题标为 resolved。 - -## 3. Blocked 记录字段 - -当满足以下任一条件时,本轮可标记 DevTools smoke access blocked:端口服务无法开启、CLI 连接持续 timeout、项目无法在 DevTools 中打开、预览无法生成且没有 UI 替代路径、登录/真实 AppID/设备权限缺失导致目标 journey 无法执行。 - -blocked 记录至少包含: - -- `blockedAt`:日期和时间。 -- `branch`:`codex/iter-devtools-smoke-unblock`。 -- `commit`:`git rev-parse --short HEAD`。 -- `worktree`:`/tmp/street-tasks-iter-worktrees/devtools-smoke`。 -- `devtoolsVersion`:DevTools 版本;无法打开时写“无法确认”。 -- `baseLibVersion`:调试基础库版本;无法确认时写“无法确认”。 -- `ideHttpPort`:例如 `9420`。 -- `servicePortSetting`:开启 / 未开启 / 无法确认。 -- `processEvidence`:进程是否存在、命令是否包含 `--ide-http-port`,只写摘要。 -- `listenEvidence`:`lsof` / `nc` / `curl` 的脱敏结论。 -- `cliCommand`:`open` 或 `preview`。 -- `actualResult`:实际错误摘要,例如 connection refused、wait IDE port timeout。 -- `attemptedFixes`:已尝试开启服务端口、退出重启 IDE、重新打开项目、逐端口检查等。 -- `nextAction`:需要人工打开安全设置、重装/升级 DevTools、切换端口、换机器、补真实 AppID 或补设备。 -- `manualJourneyImpact`:哪些 smoke journey 因此未执行。 -- `evidenceLocation`:本地附件编号或外部安全位置,不写原始绝对路径。 - -示例摘要: - -```text -blockedAt: 2026-06-14 10:30 CST -branch: codex/iter-devtools-smoke-unblock -commit: -worktree: /tmp/street-tasks-iter-worktrees/devtools-smoke -ideHttpPort: 9420 -servicePortSetting: UI 显示已开启 -processEvidence: DevTools 进程存在,命令包含 --ide-http-port 9420 -listenEvidence: 9420 无监听,nc connection refused,curl connection refused -cliCommand: open -actualResult: CLI 20s timeout,未打开项目 -attemptedFixes: 重新检查安全设置、退出重启 DevTools、重新打开当前 worktree -manualJourneyImpact: DevTools / 真机 smoke 未执行 -nextAction: 由有本机 UI 权限的执行者重启 IDE 或换机验证服务端口 -evidenceLocation: 本地附件 S-port-01,原始日志不提交 -``` - -## 4. 端口恢复后的最小 Smoke 旅程 - -端口恢复或 UI 可操作后,优先跑最小闭环,不先扩展到完整回归。 - -- [ ] DevTools 编译和首屏。 - - 打开当前 worktree 项目并普通编译。 - - 通过标准:进入地图页,地图、marker、底部 tabBar 和列表入口可见;Console 没有阻断运行的首条红色错误。 - - 失败记录:首条错误、页面路径、是否白屏、是否仍有 `WAServiceMainContext timeout`。 - -- [ ] 地图到详情。 - - 打开地图列表或点击 marker,进入一条任务详情。 - - 通过标准:详情页展示同一任务标题、正文、状态、发布者信息、TrustInsight、信任动作和评论入口;不出现“任务不存在”。 - - 失败记录:列表任务标题、详情实际标题、post id 摘要和跳转方式。 - -- [ ] 发布状态和定位失败重试。 - - 进入发布页,游客态和登录态至少覆盖一种;点击使用当前位置并覆盖允许或拒绝中的一种。 - - 通过标准:定位块文案、底部主按钮、定位中 / 失败 / 重试状态随真实权限变化;失败不清空已填内容。 - - 失败记录:权限初始状态、点击步骤、toast / 文案和是否卡住 loading。 - -- [ ] 发布成功到详情。 - - 已登录且环境允许时,填写最小有效表单并提交。 - - 通过标准:只创建一条任务,成功后跳转新任务详情;返回地图后新任务可见。 - - 若没有真实登录、CloudBase 或 AppID,写 `blocked`,不要写 `passed`。 - -- [ ] 图片 smoke。 - - 选择 1 张图片,验证缩略图、删除和发布链路;无法发布时至少验证选择和过大图片失败提示。 - - 通过标准:图片状态不破坏表单;上传失败有提示;成功发布后详情可见图片或云端引用。 - - 失败记录:图片数量、大小档位、上传错误摘要和表单是否保留。 - -- [ ] 详情信任动作和评论入口。 - - 对 active 任务执行一次确认或过时;打开评论入口。 - - 通过标准:TrustInsight / 数字刷新,重复动作被阻止;评论入口在游客或登录态下给出正确引导。 - - 失败记录:动作前后数量、toast、评论弹窗或登录引导状态。 - -- [ ] 真机预览或真机调试。 - - CLI `preview` 成功或 UI 可生成二维码后,用真机打开。 - - 通过标准:地图、发布定位、键盘安全区、详情信任区在真机上不遮挡、不白屏。 - - 若 preview 无法生成,记录 preview blocker;DevTools 模拟器结果不能替代真机结论。 - -## 5. 证据脱敏注意事项 - -- [ ] 原始截图、录屏、二维码、控制台完整日志、Network 详情、云函数日志和数据库截图只放 ignored 本地附件目录或外部安全位置。 -- [ ] 可提交文档只写脱敏摘要:角色用“用户 A / 游客态 / 管理员账号”,地点用“测试 POI / 默认中心附近”,云端资源用“fileID 已生成,原值本地留存”。 -- [ ] 不提交真实 AppID、openId、unionId、头像 URL、昵称、手机号、精确经纬度、CloudBase 环境 ID、requestId、完整 `cloud://`、token、cookie 或本机绝对路径。 -- [ ] CLI 和端口日志只摘录最小必要错误类型,例如 connection refused、wait IDE port timeout、端口未监听。 -- [ ] 每次写入可提交报告前运行: - - ```bash - git status --short --ignored - rg --no-ignore -n -i "(api[_-]?key|secret|token|password|passwd|pwd|private[_-]?key|session|cookie|authorization|bearer|access[_-]?token|refresh[_-]?token|client[_-]?secret|appsecret|wx[0-9a-f]{16,}|sk-[A-Za-z0-9_-]{20,}|AKIA[0-9A-Z]{16})" . - ``` - - 期望:真实附件和 local 结果仍是 ignored;secret scan 没有新增真实敏感值。若命中的是清单里的规则说明,也要人工确认不是实际凭据。 diff --git a/harness/devtools-smoke-command-checklist.md b/harness/devtools-smoke-command-checklist.md deleted file mode 100644 index e69dcb5..0000000 --- a/harness/devtools-smoke-command-checklist.md +++ /dev/null @@ -1,90 +0,0 @@ -# DevTools Smoke 命令验收清单 - -日期:2026-06-14 - -范围:用于 AF 轮验证 DevTools smoke 的 npm 手动命令是否清楚表达当前 9420 service port blocker。此清单不是 UI smoke 通过记录;在端口恢复前,严格 smoke 失败只能记为 blocker。 - -## 0. 进入前确认 - -- [ ] 工作目录为 `/tmp/street-tasks-iter-worktrees/devtools-smoke-command`;macOS 显示 `/private/tmp/...` 时按同一 worktree 记录。 -- [ ] 已运行 `bash harness/init.sh`,且 JSON、harness、blocked-summary preflight 基线通过。 -- [ ] 只验证命令和 blocked 口径;不退出 DevTools、不清缓存、不改真实 AppID、不提交 local 证据。 - -## 1. AF 命令验收 - -- [ ] `package.json` 保留默认入口: - - `npm run check` - - `npm run check:json` - - `npm run check:harness` - - `npm run check:readiness` -- [ ] `package.json` 新增手动 DevTools 命令: - - `npm run inspect:devtools-port`:调用 `node scripts/inspect-devtools-port-state.mjs`。 - - `npm run check:devtools-smoke`:调用 `node scripts/check-devtools-smoke-access.mjs --strict`,或等价 strict smoke。 -- [ ] 默认 `npm run check` 不调用 `check:devtools-smoke`,不调用 `check-devtools-smoke-access.mjs --strict`,不执行 DevTools CLI `open` / `preview`,不要求 9420 service port 可用才能通过。 -- [ ] `npm run inspect:devtools-port` 是只读诊断:可输出当前端口状态、诊断标签、DevTools-like 进程和 listener 摘要;不打开、关闭、重启或写入项目文件。 -- [ ] 当前 blocked 环境下,`npm run check:devtools-smoke` 应非零退出,并在输出中显示: - - `status: blocked` - - `service port 9420` 或实际检查端口 - - 端口未监听 / connection refused / requested DevTools service port is not listening - - 如存在,记录 `--ide-http-port 9420` 进程声明 -- [ ] strict smoke 的失败记录为 `DevTools service port blocker`;不得写成地图、列表、发布、详情或真机 UI 通过。 - -## 2. 当前 Blocked 记录 - -当前本机预期仍是 9420 blocker: - -```bash -npm run inspect:devtools-port -npm run check:devtools-smoke -``` - -- [ ] `inspect:devtools-port` 若输出 `status: blocked`,摘录 `diagnosis`,优先记录 `declared_without_listener`、`connect_refused`、`no listener rows` 等摘要。 -- [ ] `check:devtools-smoke` 若因 strict 非零退出,保留失败状态;不要追加 `|| true` 后写成通过。 -- [ ] 如果命令缺失,记录为 `package script missing`,不是 DevTools blocker。 -- [ ] 如果脚本输出与预期不一致,记录实际 `status`、端口、退出码和第一条 blocker;不要自行推断 UI 结果。 - -## 3. 恢复后复测 - -当执行者在 WeChat DevTools 中打开当前项目,并在“设置 -> 安全设置”启用 Service Port 后,再执行以下复测: - -```bash -npm run inspect:devtools-port -npm run check:devtools-smoke -``` - -- [ ] `inspect:devtools-port` 应从 `blocked` 转为 `ready`,或至少显示 9420 有 listener 且可连接。 -- [ ] `check:devtools-smoke` strict 模式应退出码为 0,并显示 `status: ready`。 -- [ ] 只在 strict smoke ready 后进入小程序 UI smoke:编译项目,进入地图页,确认地图、marker、底部 tabBar 和列表入口可见。 -- [ ] 点击地图页列表入口,验证地图 / 列表切换:列表可打开、可滚动、可关闭或返回地图视图;切换过程中无白屏和阻断性红色错误。 -- [ ] UI 观察必须按真实结果记录为 `passed`、`failed` 或 `blocked`。命令 ready 只表示入口可用,不等于地图 / 列表 UI 通过。 - -## 4. 证据记录格式 - -建议写入 `harness/claude-progress.md` 的摘要格式: - -```text -### Session 0XXAF - -- 日期:2026-06-14 -- 分支: -- 本轮目标:验证 AF DevTools smoke npm 手动命令和 blocked 口径 -- 运行过的验证:`pwd`;读取 `harness/claude-progress.md` 和 `harness/feature_list.json`;`git log --oneline -5`;`bash harness/init.sh`;`npm run inspect:devtools-port`;`npm run check:devtools-smoke` -- 已记录证据:package scripts 包含 `inspect:devtools-port` 和 `check:devtools-smoke`;`npm run check` 未调用 strict DevTools smoke;`inspect:devtools-port` 输出 status=、diagnosis=<...>、port=<9420>;`check:devtools-smoke` 退出码=<0|非零>、status=、blocker= -- UI smoke 结果:;若端口 blocked,写 `not_covered` 或 `blocked`,不得写 passed -- 已知风险或未解决问题:<例如 DevTools service port 9420 仍无 listener,地图 / 列表真实 UI smoke 未执行> -- 下一步最佳动作:<开启 Service Port 后复跑 strict smoke;ready 后验证地图 / 列表切换> -``` - -最小 blocked 摘要字段: - -```text -status: BLOCKED -worktree: /tmp/street-tasks-iter-worktrees/devtools-smoke-command -command: npm run check:devtools-smoke -exitCode: -ideHttpPort: 9420 -portStatus: no listener / connection refused -blocker: DevTools service port blocker -manualJourneyImpact: DevTools UI smoke 未执行;地图 / 列表切换不可记为 passed -nextAction: 用户打开 WeChat DevTools 并启用 Service Port 后复跑 strict smoke -``` diff --git a/harness/devtools-smoke-command-design-note.md b/harness/devtools-smoke-command-design-note.md deleted file mode 100644 index e3da0f8..0000000 --- a/harness/devtools-smoke-command-design-note.md +++ /dev/null @@ -1,59 +0,0 @@ -# DevTools Smoke 命令设计说明 - -日期:2026-06-14 - -## 目标 - -AF 轮只为 DevTools smoke 增加清晰的手动命令入口和报告口径。当前 9420 service port blocker 仍阻止真实 WeChat DevTools UI smoke 执行;报告应说明“环境阻塞”,不能暗示 Street Tasks 用户界面失败,也不能把未执行项写成通过。 - -## 状态边界 - -- `blocked`:有具体环境阻塞证据,导致目标 smoke 无法执行。例如 9420 service port 无 listener、connection refused、DevTools CLI timeout。结论是“入口被环境挡住”,不是“地图/列表/发布/详情失败”。 -- `unverified`:本轮没有执行,也没有足够 blocker 证据。适用于未打开 DevTools、未跑对应 journey、未观察真机或 UI 页面。不能因为静态检查、readiness 或 helper 通过而升级。 -- `passing`:真实检查已经执行并通过,且有命令输出、退出码、页面观察或截图/记录等证据。strict smoke passing 只说明 DevTools smoke 入口可用;UI journey passing 还必须来自真实 DevTools UI 或真机观察。 - -## 命名体验原则 - -- `inspect:*` 表示只读诊断。`inspect:devtools-port` 只能读取端口、进程和连接状态,用来回答“现在为什么不能 smoke”,不应作为验收门禁。 -- `check:*` 表示可作为验收门禁。`check:devtools-smoke` 是 strict smoke 入口,失败时应让执行者看到明确环境恢复动作,而不是把失败埋进普通 preflight。 -- 默认 `npm run check` 不应包含真实 DevTools strict smoke。它可以保证 JSON、harness、readiness 和 blocked-summary preflight,但不能证明 WeChat DevTools UI 已通过。 - -## 推荐报告短文案 - -| 场景 | 推荐短状态 | 避免写法 | -| --- | --- | --- | -| 9420 无监听或 connection refused | `DevTools 端口阻塞,UI smoke 未执行` | `UI smoke failed` | -| strict smoke 因 service port blocker 非零退出 | `blocked: DevTools service port 9420 不可达` | `地图页失败` | -| 只跑了 readiness / static guard | `preflight passing,真实 UI smoke unverified` | `DevTools passed` | -| 没有执行目标 journey | `unverified: 本轮未执行该 journey` | `默认视为通过` | -| 端口 ready 但未看 UI | `strict smoke passing,UI journey unverified` | `地图列表通过` | -| DevTools/真机真实观察通过 | `passing: 已在 DevTools/真机完成 观察` | `脚本通过所以 UI 通过` | - -## 报告字段建议 - -最小摘要应分开写三层: - -```text -portStatus: blocked | ready | unknown -strictSmoke: blocked | unverified | passing -uiSmoke: blocked | unverified | passing | failed -``` - -当前 AF blocked 推荐摘要: - -```text -Status: blocked -Summary: DevTools 端口阻塞,UI smoke 未执行 -Evidence: inspect:devtools-port 显示 9420 无 listener / connection refused;check:devtools-smoke strict 非零退出 -Impact: 未进入 WeChat DevTools UI journey;不能声明地图、列表、发布或详情失败 -Next action: 在 WeChat DevTools 启用 Service Port 并确认 9420 可监听后,复跑 check:devtools-smoke;strict passing 后再执行真实 UI smoke -``` - -## 严格 smoke 失败的呈现 - -`check:devtools-smoke` 的 strict 失败应突出“环境恢复动作”: - -- 标题或首行使用 `blocked`,不要只写 `failed`。 -- 摘要包含端口号、连接结果和进程声明,例如 `9420 declared_without_listener / connect_refused`。 -- 下一步指向用户可执行恢复:打开 WeChat DevTools,启用 Service Port,重开当前 worktree,再复跑 strict smoke。 -- 恢复前,所有 UI journey 保持 `blocked` 或 `unverified`;恢复后也只有真实观察过的 journey 才能写 `passing`。 diff --git a/harness/devtools-smoke-command-product-brief.md b/harness/devtools-smoke-command-product-brief.md deleted file mode 100644 index 312ea46..0000000 --- a/harness/devtools-smoke-command-product-brief.md +++ /dev/null @@ -1,28 +0,0 @@ -# DevTools Smoke Command Product Brief - -## 本轮 AF 产品目标 - -把真实 WeChat DevTools UI smoke 的本机 blocker,从零散脚本诊断变成一个明确、可引用的手动命令入口。开发者和评测员在准备手动 smoke 前,可以先运行端口诊断,清楚知道当前是否被 DevTools service port 环境挡住。 - -这轮关注的是“把阻塞证据说清楚”,不是证明地图、发布、详情等真实 UI smoke 已经通过。 - -## 非目标 - -- 不把 GUI 或本机 WeChat DevTools 依赖加入默认 CI。 -- 不把真实 DevTools smoke 接入 `npm run check`。 -- 不声称 UI smoke 通过;当前只能记录被本机 9420 service port 阻塞。 - -## 使用场景 - -- 开发者准备打开 WeChat DevTools 做手动 smoke 前,先运行端口诊断,确认 service port 是否真的可连。 -- 评测员看到 strict smoke 被标记为 blocked 时,应检查诊断输出。如果输出指向本机端口无监听,则判定为“环境阻塞证据”,而不是产品功能失败。 -- 如果后续本机 DevTools 端口恢复,再重新运行同一入口,才进入真实 UI smoke 判断。 - -## 当前预期 - -在当前环境下: - -- `node scripts/inspect-devtools-port-state.mjs` 应输出 `status: blocked`,诊断为 `declared_without_listener` / `connect_refused`。 -- `npm run check:devtools-smoke` 的 strict smoke 访问应失败,并说明 service port `9420` 没有 listener,同时存在 1 个 `ide-http-port` 进程声明端口。 - -这个失败是 DevTools 本机服务端口没有实际监听的环境阻塞证据,不是 Street Tasks 产品功能失败,也不能替代真实 WeChat DevTools UI smoke 通过证据。 diff --git a/harness/devtools-smoke-product-brief.md b/harness/devtools-smoke-product-brief.md deleted file mode 100644 index 4c162a1..0000000 --- a/harness/devtools-smoke-product-brief.md +++ /dev/null @@ -1,95 +0,0 @@ -# M 组 DevTools Smoke 阻塞排查产品简报 - -日期:2026-06-14 - -分支:`codex/iter-devtools-smoke-unblock` - -对象:继续推进真实 WeChat DevTools smoke access 的产品、QA 和开发 agent。 - -## 1. 问题 - -本轮不再继续扩展 harness 文档或证据模板,而是转向真实 WeChat DevTools smoke access 的阻塞排查。原因是 L 组已经补齐了手测 local JSON、脱敏 summary 草稿和 evidence hygiene,继续增加外围规范的边际价值下降;当前真正阻断发布判断的是:真实 DevTools/真机手测仍未执行,且本机进入 DevTools smoke 的端口和 CLI 链路不可用。 - -当前观察到的阻塞现象: - -- WeChat DevTools 进程存在,启动命令包含 `--ide-http-port 9420`。 -- `lsof -iTCP:9420 -sTCP:LISTEN` 未发现监听。 -- `curl http://127.0.0.1:9420/` 返回 connection refused。 -- `cli open --project ... --port 9420` 被 20s timeout 截断,输出包含 `IDE may already started at port 9420, trying to connect`。 - -这些现象说明:当前不能把自动脚本、local JSON 或文档校验当作真实 smoke 通过。M 组要把“为什么进不了真实 smoke”变成可复现诊断、明确恢复步骤和可继续执行的 blocked 记录。 - -## 2. 用户/发布价值 - -真实 DevTools/真机 smoke 是判断街区任务能否进入候选发布的关键门槛。发布状态、定位授权、地图首屏、详情信任动作、评论入口、图片和云端路径,都有只能在 DevTools 或真机中观察的风险。 - -本轮聚焦 smoke access unblock 的价值是: - -- 避免把 harness 完整误读成用户旅程通过。 -- 让后续 agent 可以稳定复现 9420 端口与 CLI 连接问题,而不是重复猜测。 -- 明确何时可以开始真实 smoke,何时必须继续标记为 blocked。 -- 把发布准入讨论从“文档是否齐”推进到“真实运行入口是否可用”。 - -## 3. 范围内 - -- 记录 DevTools 进程、端口监听、CLI open/preview/curl/lsof 的诊断口径。 -- 说明 9420 端口未监听、connection refused、CLI timeout 对真实 smoke 的影响。 -- 定义恢复步骤:确认安全设置服务端口、重启 DevTools、重新打开项目、检查端口监听、再尝试 CLI 或手动 smoke。 -- 定义 blocked 状态如何记录到后续手测证据或 summary 中。 -- 明确真实 smoke 开始前必须满足的最小条件。 - -## 4. 非目标 - -- 不声明真实 WeChat DevTools 或真机手测已经通过。 -- 不把本地 JSON、脱敏 summary、evidence hygiene 或自动脚本结果升级为用户旅程通过。 -- 不修改小程序功能代码、页面样式、云函数、脚本或现有 harness 状态文件。 -- 不解决所有 DevTools 内部问题,只把当前端口/CLI 阻塞变成可执行排查路径。 -- 不要求本轮提交代码。 - -## 5. 成功标准 - -本 brief 完成后,后续 agent 应能回答: - -- 为什么本轮从继续扩展 harness 转向 DevTools smoke access 阻塞排查。 -- 当前端口与 CLI 阻塞的已知证据是什么。 -- 阻塞时应该如何记录状态、影响和下一步,而不是写成 passed。 -- 真实 smoke 何时可以开始,开始后需要至少覆盖哪些用户旅程。 - -真正的产品成功不是“写完本文档”,而是后续能恢复 DevTools 入口,并在真实 DevTools 或真机中留下可复核 smoke 证据。 - -## 6. Blocked 记录口径 - -当 9420 服务端口没有监听或 CLI 连接失败时,记录为: - -- 状态:`blocked` -- 类型:`DevTools smoke access blocked` -- 阶段:`open project`、`connect service port` 或 `start smoke` -- 证据:记录命令、时间、超时秒数、关键输出和当前项目路径 -- 当前观察:进程存在但端口未监听,curl connection refused,CLI 提示已有 IDE 后继续连接但超时 -- 影响:无法通过 CLI 打开、预览或稳定触发真实 smoke;不能判断产品旅程通过或失败 -- 已尝试动作:列出是否检查过进程、端口、curl、CLI open、DevTools 安全设置、重启和重新打开项目 -- 下一步:恢复服务端口监听,或改用 DevTools UI 手动打开项目并记录环境与截图 - -不要把 blocked 写成: - -- `passed`:因为没有进入真实用户旅程。 -- `failed`:除非已经能稳定进入产品行为并复现产品缺陷。 -- `not_covered`:因为当前不是主动未测,而是入口被工具/环境阻断。 - -## 7. 下一步真正手测条件 - -只有满足以下条件之一,才可以开始记录真实 DevTools smoke: - -1. DevTools 服务端口恢复:`lsof -iTCP:9420 -sTCP:LISTEN` 能看到监听,`curl http://127.0.0.1:9420/` 不再是 connection refused,CLI open 或 preview 能返回可判断结果。 -2. DevTools UI 可用:在 WeChat DevTools 中手动打开 `/tmp/street-tasks-iter-worktrees/devtools-smoke`,能编译并进入模拟器页面,且记录 DevTools 版本、基础库、项目路径、分支和当前提交。 -3. 真机链路可用:能从 DevTools 预览或真机调试进入小程序,并记录设备、微信版本、网络、定位授权和登录状态。 - -开始真实 smoke 后,最低覆盖: - -- 地图首屏:打开项目、编译、进入地图页,记录控制台是否有阻断错误。 -- 地图到详情:从地图 marker 或列表进入详情,确认不是空白页或任务不存在。 -- 发布状态:打开发布页,观察定位确认、必填状态和主按钮文案。 -- 定位分支:至少记录允许、拒绝或失败重试中的一个真实分支;未覆盖的分支必须写明。 -- 详情信任入口:进入 active 详情,观察 TrustInsight、评论入口和信任动作区域是否可见。 - -若以上条件仍不满足,结论必须保持 `blocked`,并把新的命令输出、截图或日志追加到手测证据中。M 组不为真实 DevTools/真机 smoke 背书,直到真实运行入口恢复并完成上述旅程。 diff --git a/harness/evaluator-rubric.md b/harness/evaluator-rubric.md deleted file mode 100644 index cfb589c..0000000 --- a/harness/evaluator-rubric.md +++ /dev/null @@ -1,24 +0,0 @@ -# 评审评分表 - -在实现完成后、正式验收前,用这张表做一次评审。每个维度 0-2 分。 - -| 维度 | 通过标准 | 分数 (0-2) | 备注 | -| --- | --- | --- | --- | -| 正确性 | 行为符合 `harness/feature_list.json` 中的目标功能,没有破坏现有微信小程序约定 | | | -| 验证 | 要求的自动化或 WeChat DevTools 手动检查真的跑过,并留下证据 | | | -| 范围纪律 | 这一轮只处理选中的功能或窄范围 blocker,没有顺手扩大范围 | | | -| 可靠性 | 重启小程序、刷新本地 storage 或云端 fallback 后仍能维持正确状态 | | | -| 可维护性 | 共享逻辑仍在 `utils/*` 或云函数边界内,页面层没有不必要复制 | | | -| 交接准备度 | 新会话只靠 `AGENTS.md` 和 `harness/*` 能继续推进 | | | - -## 结论 - -- Accept -- Revise -- Block - -## 后续动作 - -- 缺失的证据: -- 必须补的修复: -- 下次复审触发条件: diff --git a/harness/evidence-hygiene-product-brief.md b/harness/evidence-hygiene-product-brief.md deleted file mode 100644 index 19918f7..0000000 --- a/harness/evidence-hygiene-product-brief.md +++ /dev/null @@ -1,96 +0,0 @@ -# J 组手测证据卫生产品简报 - -日期:2026-06-14 - -分支:`codex/iter-evidence-hygiene` - -对象:整理、复核、提交 WeChat DevTools、真机或 CloudBase 相关手测证据的 agent。 - -## 1. J 组目标 - -- 建立手测证据进入仓库前的卫生原则,区分“可提交摘要”和“本地附件/敏感原始证据”。 -- 降低真实手测中截图、录屏、日志、CloudBase 标识、设备信息和用户数据被误提交的风险。 -- 给 I 组机器可读证据包补充产品侧提交边界,确保完整性检查不会诱导提交敏感原始材料。 -- 让后续 agent 能在不接触敏感附件的情况下,复核手测结论、覆盖范围和剩余风险。 - -## 2. J 组非目标 - -- J 组不代表真实 WeChat DevTools、真机或 CloudBase 手测已经完成。 -- 不替代 I 组的证据完整性检查,也不改变 `scripts/check-manual-evidence.mjs` 的当前校验规则。 -- 不新增功能、不修改小程序代码,也不提交任何真实手测原始证据。 -- 不要求把截图、录屏、完整日志或真实云资源标识提交到仓库。 -- 不为生产环境或真实用户数据建立可公开披露的例外流程;默认应先脱敏、摘要化或留在本地。 - -## 3. 为什么要区分可提交摘要和本地原始证据 - -真实手测证据通常同时包含两类信息:一类是支撑发布判断的产品事实,另一类是复核过程中的原始材料。前者需要进入仓库,方便后续 agent 和 reviewer 判断“测了什么、结果如何、还有什么风险”;后者可能包含真实用户身份、精确位置、云资源路径、请求追踪信息、设备痕迹或本地机器路径,直接提交会扩大泄漏面。 - -证据卫生的产品原则是:仓库只保存足以复核结论的脱敏摘要,本地或受控系统保存原始附件。摘要应能说明状态、步骤、期望、实际、环境类别和脱敏标识;原始截图、录屏、日志、CloudBase 文件路径、request id 和本地绝对路径默认不进入仓库。 - -这样做可以同时满足两个目标: - -- 让发布准入判断可追溯,不退回到“口头说测过”。 -- 避免为了证明“测过”而把敏感证据永久写入 Git 历史。 - -## 4. 可提交字段 - -以下字段可以进入仓库中的机器可读证据摘要或 Markdown 手测摘要,但应保持最小必要原则: - -- 脱敏后的旅程状态:`passed`、`failed`、`blocked`、`not_covered`。 -- 脱敏后的测试步骤:入口、操作顺序、授权选择、页面跳转和关键输入类型,不包含真实隐私内容。 -- 期望结果:页面、文案、数据状态、错误提示、按钮状态、云端能力是否应成功等产品判断。 -- 实际结果:观察到的页面状态、文案差异、数据变化、失败类型和是否阻断发布。 -- 泛化环境信息:DevTools/真机、iOS/Android/模拟器、基础库版本、微信版本、网络类别、定位授权状态、CloudBase 是否启用、云函数或 Storage 是否可用。 -- 脱敏任务 id 或数据标识:例如 `post_test_001`、`journey-map-detail-20260614-a`、`redacted-request-a`,只用于关联同一轮摘要。 -- 附件摘要:例如“截图已本地留存,显示发布后进入详情页”“控制台日志摘要为 Storage 权限拒绝”,不包含原始文件名、绝对路径或可反查云资源的标识。 -- 风险与下一步:未覆盖范围、阻塞原因、复测条件、是否影响发布候选。 - -可提交字段的判断标准是:离开本地环境后,读者仍能理解结论;但无法据此定位真实用户、真实住址、生产资源、私有文件或认证凭据。 - -## 5. 默认不提交或必须脱敏的字段 - -以下字段默认不得直接进入仓库。确需引用时,只能提交脱敏摘要或不可反查的占位符: - -- 真实用户身份:openid、unionid、手机号、微信昵称、头像 URL、真实姓名、聊天记录、通讯录信息。 -- 精确位置:详细住址、门牌号、经纬度原值、可识别个人住处或工作地点的地图截图。 -- 生产云信息:生产 CloudBase 环境 id、数据库集合真实记录 id、云函数真实部署详情、可定位生产资源的项目标识。 -- 云文件与请求追踪:`cloud://` 文件 id、CloudBase Storage fileID、request id、trace id、完整云函数日志查询链接。 -- 认证与会话:cookie、token、session、Authorization/Bearer 头、refresh token、client secret、AppSecret、私钥。 -- 本地机器痕迹:完整本地文件路径、用户目录名、临时目录原路径、IDE 或 DevTools 本地缓存路径。 -- 原始附件:截图、录屏、完整控制台日志、Network HAR、云开发日志导出、数据库截图、Storage 权限截图原文件。 -- 设备敏感信息:可唯一定位个人设备的序列号、UDID、IMEI、完整系统账户名、完整 IP 地址。 - -推荐脱敏方式: - -- 用稳定但不可反查的占位符替代真实 id,例如 ``、`post_test_001`、`request_redacted_a`。 -- 只保留环境类别和版本级信息,例如“Android 真机,微信 8.x,基础库 3.x”,不保留唯一设备标识。 -- 对地址只保留必要粒度,例如“定位授权允许,城市级位置可用”,不保留门牌、楼栋或精确坐标。 -- 对附件只记录证据类型、观察结论和本地留存状态,不记录原文件路径。 - -## 6. `.gitignore` 应保护的本地结果和附件 - -`.gitignore` 应覆盖真实手测产生的本地结果、附件和敏感导出,避免误提交。建议保护范围包括: - -- 真实手测结果文件:`harness/manual-test-results.local.json`、`harness/manual-test-results.*.local.json`、`harness/manual-test-results.private.json`。 -- 手测附件目录:`harness/manual-evidence/`、`harness/evidence-artifacts/`、`harness/screenshots/`、`harness/recordings/`、`harness/logs/`。 -- DevTools 或真机导出:截图、录屏、控制台日志、Network/HAR、云开发日志、数据库导出、Storage 导出。 -- 临时和本机路径映射:`*.local.log`、`*.har`、`*.mp4`、`*.mov`、`*.png`、`*.jpg`、`*.jpeg`、`*.webm` 中属于手测证据的文件。 -- 云资源敏感清单:包含 CloudBase env id、fileID、request id、访问链接、上传路径或权限截图的本地记录。 - -本 brief 定义应保护的产品边界;如果落地 `.gitignore` 规则,应保持范围具体,并通过 evidence hygiene 检查验证。 - -## 7. 与 I 组的关系 - -I 组检查证据完整性,回答“这条手测结论是否有足够字段支撑”。它关注状态、环境、步骤、期望、实际、附件和标识是否齐全。 - -J 组检查证据是否适合进入仓库,回答“这些字段和附件是否已经足够脱敏,是否会把敏感原始证据写入 Git 历史”。它关注提交边界、脱敏原则、本地附件留存和误提交风险。 - -两组应串联使用: - -- I 组通过不代表证据可以原样提交;完整证据仍可能包含敏感字段。 -- J 组通过不代表真实手测已经完成;它只说明提交内容符合证据卫生原则。 -- 最理想的记录方式是:I 组要求的完整信息被摘要化保留,J 组要求的敏感原始材料留在本地或受控系统。 - -## 8. J 组核心结论 - -J 组的核心结论是:真实手测证据要先摘要化、脱敏化,再进入仓库。仓库应保存可复核的产品结论,不保存可定位真实用户、生产云资源、认证会话、本地机器或原始附件的敏感材料。J 组补的是证据卫生边界,不声明当前候选已经完成真实手测。 diff --git a/harness/evidence-redaction-checklist.md b/harness/evidence-redaction-checklist.md deleted file mode 100644 index 7de54fd..0000000 --- a/harness/evidence-redaction-checklist.md +++ /dev/null @@ -1,68 +0,0 @@ -# 手测证据提交前脱敏清单 - -本清单用于后续手测记录者在提交 `harness/manual-test-results*.json`、报告或证据摘要前执行自查。它不是已完成的脱敏审查记录;只有实际逐项检查并处理后,才能在评审或交接中说明已执行。 - -## 提交前检查项 - -- 运行 `git status --short --ignored`,确认待提交文件只包含预期的报告或清单文件;截图、录屏、原始日志、本地配置和临时附件不应出现在待提交列表中。 -- 运行 `git diff -- harness '*.md' '*.json'`,逐行检查新增证据文本,确认没有真实用户、真实地点、云端标识、设备唯一信息、本地路径或原始日志。 -- 运行仓库约定的 secret scan: - - ```bash - rg --no-ignore -n -i "(api[_-]?key|secret|token|password|passwd|pwd|private[_-]?key|session|cookie|authorization|bearer|access[_-]?token|refresh[_-]?token|client[_-]?secret|appsecret|wx[0-9a-f]{16,}|sk-[A-Za-z0-9_-]{20,}|AKIA[0-9A-Z]{16})" . - ``` - -- 运行 `npm run check:json`,确认 JSON 报告仍是严格 JSON;如写入手测结果,再运行 `node scripts/check-manual-evidence.mjs <结果文件路径>`。 -- 检查忽略文件状态:`git status --short --ignored` 中本地附件目录、真实截图/录屏、DevTools 缓存、`project.private.config.json`、日志文件和 `aaa` 等敏感本地文件应保持 ignored 或未跟踪且不提交。 -- 对 `git diff --cached` 再做一次同样检查;提交前以已暂存内容为准,不以工作区记忆为准。 - -## 字段脱敏规则 - -- 用户标识:不要写真实姓名、微信号、手机号、openId、unionId、头像 URL、昵称截图或可反查账号的 handle。报告中只写角色化标识,例如“用户 A”“管理员账号”“游客态”。 -- 地点:不要写真实家庭、公司、学校、门牌号、精确经纬度或常去地点。报告中可写“默认中心附近”“测试 POI”“约 200m 距离”这类模糊描述;必要时保留城市级或功能性位置。 -- 云端 `fileID` / `requestID` / 环境 ID:不要提交生产或准生产的完整 `cloud://`、`envId`、`requestId`、对象存储路径。报告中只写模式和结果,例如“生成了 cloud:// fileID,已脱敏”“requestID 已本地留存”。 -- 设备信息:不要写序列号、UDID、设备名称中包含个人姓名的字符串、IP、MAC、真实运营商账号信息。可写“iPhone 真机”“Android 真机”“微信开发者工具模拟器”和基础库版本。 -- 本地路径:不要提交 `/Users//...`、临时目录中带用户名或项目私有路径的完整路径。可写仓库相对路径、`/xxx.png` 或“本地附件目录”。 -- 日志片段:不要粘贴完整控制台、网络请求、堆栈或云函数日志。只摘录最小必要错误类型、状态码、页面路由和脱敏后的 message;删除 header、cookie、token、session、文件路径和用户数据。 -- 截图/录屏:不要提交真实截图或录屏文件,尤其包含头像、昵称、地图、照片、二维码、云控制台、DevTools 账号或本地路径的画面。报告中只记录截图编号、观察结论和本地附件位置。 - -## 可写入报告的内容 - -- 手测分支、被测 commit、测试时间、测试者的非个人化 handle。 -- WeChat DevTools 版本、基础库版本、设备类型或模拟器类型、网络档位、定位权限状态。 -- 旅程状态:`passed`、`failed`、`blocked`、`not_covered`。 -- 复现步骤、预期结果、实际结果的摘要。 -- 脱敏后的 post id 或测试对象别名,例如 `post_001`、`测试任务 A`;若 id 来自真实云端且可反查,应改写为别名。 -- 脱敏后的错误摘要,例如“DevTools CLI 等待服务端口超时”“CloudBase 上传失败并提示用户重试”。 -- 本地附件索引,例如“截图 S1 展示发布按钮未被 tabBar 遮挡,本地保存于附件目录”,但不要写真实绝对路径。 - -## 只能本地保存的内容 - -- 原始截图、录屏、二维码、地图画面、用户头像或昵称画面。 -- 完整云端 `fileID`、`requestID`、环境 ID、集合记录 ID、对象存储路径。 -- 完整控制台日志、云函数日志、网络请求/响应、DevTools 导出文件。 -- 真实账号、真实地点、精确经纬度、个人设备名和本机绝对路径。 -- 任何需要团队内人工确认是否可公开的证据原文。 - -本地附件应放在已忽略目录或提交外部的安全位置,并在报告中只保留摘要、编号和脱敏后的结论。 - -## 发现疑似敏感证据时怎么处理 - -- 先停止暂存或提交,保留当前工作区,确认敏感内容出现在哪个文件和哪一行。 -- 能用摘要表达的,改写为结论:把原始日志改成错误类型、触发步骤、影响范围和下一步。 -- 必须保留原件供排查的,移到本地附件目录;报告里写“原件本地留存,提交内容为脱敏摘要”。 -- 对标识符做稳定别名映射,例如 `真实 openId -> 用户 A`、`真实 fileID -> 图片文件 1`,并确保映射表不提交。 -- 对截图/录屏做本地保存,不提交二进制文件;如确需共享,先裁剪或打码,再由 reviewer 单独确认可提交。 -- 如果内容已经暂存,先取消暂存对应文件,再改写或移走敏感证据;不要通过补充说明来“解释”敏感内容。 -- 如果不确定是否敏感,按敏感处理,并在报告中记录“证据已本地保留,提交摘要待 reviewer 判断”。 - -## 给后续 reviewer 的审查问题 - -- `git diff --cached` 中是否只包含预期报告或清单文件,没有真实附件、日志、截图或配置文件? -- 每个 `passed` 旅程是否有足够的脱敏证据,而不是只写“看起来正常”? -- 报告里的用户、地点、设备、云端资源和本地路径是否都已泛化或别名化? -- 是否出现完整 `cloud://` fileID、CloudBase 环境 ID、requestID、openId、手机号、微信号、cookie、token、绝对路径或精确经纬度? -- 截图/录屏是否只以本地附件编号出现,没有被加入仓库或写入真实路径? -- 失败或 blocked 记录是否保留了可复查的原因摘要,同时没有粘贴原始敏感日志? -- secret scan、JSON 检查和手测证据检查是否在提交前实际运行,且输出没有新增风险? -- 报告是否避免声称“已完成真实脱敏审查”,除非提交者能指出逐项执行记录? diff --git a/harness/feature_list.json b/harness/feature_list.json deleted file mode 100644 index 33aa8de..0000000 --- a/harness/feature_list.json +++ /dev/null @@ -1,427 +0,0 @@ -{ - "project": "Street Tasks / 街区任务", - "last_updated": "2026-07-14", - "rules": { - "single_active_feature": true, - "passing_requires_evidence": true, - "do_not_skip_verification": true, - "manual_wechat_devtools_required_for_user_visible_passing": true - }, - "status_legend": { - "not_started": "还没有按 harness 验证流程开始推进。代码里可能已有实现,但不能因此视为 passing。", - "in_progress": "这个功能是当前唯一正在进行的任务。", - "blocked": "因为已记录的阻塞问题,当前无法继续推进。", - "passing": "要求的验证已经通过,并且证据已经记录。" - }, - "verification_levels": { - "baseline": [ - "npm run check:json", - "node harness/check-harness.mjs" - ], - "manual_product": [ - "在 WeChat DevTools 中打开项目。", - "执行编译,确认无配置或运行时报错。", - "按目标功能逐步操作并记录结果。" - ] - }, - "features": [ - { - "id": "harness-001", - "priority": 0, - "area": "harness", - "title": "仓库 agent harness 初始化", - "user_visible_behavior": "后续 agent 可以从 AGENTS.md 和 harness/ 下的状态文件恢复上下文、运行基础验证并记录证据。", - "status": "passing", - "verification": [ - "从根目录运行 npm run check:json。", - "从根目录运行 node harness/check-harness.mjs。", - "确认根 AGENTS.md 指向 harness/init.sh、feature_list.json 和 claude-progress.md。", - "确认除根 AGENTS.md 外,新增 harness 文件都位于 harness/。" - ], - "evidence": [ - "2026-05-13: npm run check:json passed, output: Checked 11 JSON files.", - "2026-05-13: node harness/check-harness.mjs passed, output: Harness OK: 6 features checked.", - "2026-05-13: bash harness/init.sh passed; npm ci --ignore-scripts reported up to date, then check:json and check-harness both passed.", - "2026-05-13: chmod +x harness/init.sh completed; root AGENTS.md references harness/init.sh, harness/feature_list.json, and harness/claude-progress.md." - ], - "notes": "初始 harness 已建立。产品功能仍需 WeChat DevTools 手动验证后才能标记 passing。" - }, - { - "id": "map-feed-001", - "priority": 1, - "area": "map", - "title": "地图首页附近任务浏览", - "user_visible_behavior": "用户进入地图页后可以看到地图、附近任务标记、底部列表,并可定位或查找一条附近任务。", - "status": "in_progress", - "verification": [ - "在 WeChat DevTools 中编译项目。", - "打开地图 tab。", - "允许或拒绝定位各验证一次,确认默认中心和定位回退正常。", - "点击任务标记,确认进入对应详情页。", - "点击查找附近任务,确认地图移动到有效任务。" - ], - "evidence": [ - "2026-05-18: Added DESIGN_SYSTEM.md with TDesign-style native mini program UI rules, semantic tokens, typography, layout rules, and QA checklist.", - "2026-05-18: Refreshed global WXSS, custom tabBar, map overlays, selected post card, list drawer, and post cards around the new local-life visual system.", - "2026-05-18: Synchronized primary visual treatment across detail, publish, feedback, admin, me, my-posts, and activities pages.", - "2026-05-18: node scripts/check-json.mjs passed, output: Checked 11 JSON files.", - "2026-05-18: git diff --check passed with no output.", - "2026-05-18: node harness/check-harness.mjs passed, output: Harness OK: 6 features checked.", - "2026-05-18: bash harness/init.sh passed after the UI refresh; npm-free fallback skipped install, then JSON and harness checks passed.", - "2026-05-18: Docked the custom tabBar to the bottom with an opaque surface so normal page content no longer shows through under the navigation.", - "2026-05-18: Adjusted map floating controls, selected-post card, and list drawer bottom offsets to align with the docked tabBar.", - "2026-05-18: Added a design-system rule that the custom tabBar should avoid floating gaps on form and list pages.", - "2026-05-19: Added a global BottomAction component pattern and moved the publish submit action into a fixed bottom action zone above the custom tabBar.", - "2026-05-19: Reworked the publish form into grouped FormSection blocks and changed task category selection from a picker to direct two-column option buttons.", - "2026-05-19: Added a DrawerCounter to the map list drawer and changed task-card signals into three SignalPill metrics for confirmation, stale count, and expiry.", - "2026-05-19: node --check pages/publish/publish.js passed.", - "2026-05-19: node scripts/check-json.mjs passed, output: Checked 11 JSON files.", - "2026-05-19: git diff --check passed with no output.", - "2026-05-19: node harness/check-harness.mjs passed, output: Harness OK: 6 features checked.", - "2026-05-19: bash harness/init.sh passed after the UI landing pass; npm-free fallback skipped install, then JSON and harness checks passed.", - "2026-05-21: Removed the extra empty cover-view indicator and absolute-position/cropping styles from the custom tabBar to reduce cover-view rendering compatibility risk after a white-screen report.", - "2026-05-21: WeChat DevTools local WXML compiler wcc passed for all WXML files with no output.", - "2026-05-21: WeChat DevTools local WXSS compiler wcsc -lc passed for all WXSS files with no output.", - "2026-05-21: node --check passed for pages/map/map.js and pages/publish/publish.js.", - "2026-05-21: node scripts/check-json.mjs passed, output: Checked 11 JSON files.", - "2026-05-21: git diff --check passed with no output.", - "2026-05-21: bash harness/init.sh passed; npm install reported up to date, then check:json and check-harness both passed.", - "2026-05-21: Confirmed in WeChat DevTools that the simulator startup failure was `TypeError: Cannot read property 'subPackages' of undefined` from DevTools hot reload/getAppConfig, not a WXML/WXSS syntax error.", - "2026-05-21: Added runtime diagnostics storage, App-level startup guards, cloud read fallback, and a map-page cover-view diagnostic panel so startup/runtime failures can surface on screen.", - "2026-05-21: Cleared DevTools compile cache through the UI, terminated the simulator, and recompiled; the map page rendered again at pages/map/map with map tiles, bottom tabBar, and list entry visible.", - "2026-05-21: After clearing the DevTools console and recompiling, the debugger panel showed Errors: 0 and Warnings: 0.", - "2026-05-21: Re-ran node --check for app.js, utils/diagnostics.js, utils/store.js, and pages/map/map.js; node scripts/check-json.mjs; git diff --check; full WXML/WXSS DevTools compiler checks; node harness/check-harness.mjs; and bash harness/init.sh. All passed.", - "2026-05-21: Changed the map first paint to use default center plus local-only posts, so startup no longer waits on wx.getLocation, CloudBase listPosts, or mapContext.getRegion.", - "2026-05-21: Changed map show-location to remain off until a user-initiated location request succeeds, reducing startup native API timeout risk.", - "2026-05-21: Re-ran node --check for utils/store.js and pages/map/map.js; node scripts/check-json.mjs; git diff --check; full WXML/WXSS DevTools compiler checks; node harness/check-harness.mjs; and bash harness/init.sh. All passed.", - "2026-05-21: Cleared the DevTools console, recompiled, and confirmed the map page remained visible with markers, tabBar, and list entry; debugger showed Errors: 0 and no new Error: timeout.", - "2026-05-21: Fixed detail lookup mismatch for local-first map posts by falling back to same-id local storage/mock posts when CloudBase get returns empty or NOT_FOUND.", - "2026-05-21: Re-ran node --check for utils/store.js and pages/detail/detail.js; node scripts/check-json.mjs; git diff --check; and bash harness/init.sh. All passed.", - "2026-05-21: In WeChat DevTools, opened the map list and tapped a local `测试` task detail; the detail page showed the task content instead of the `任务不存在` state.", - "2026-05-21: Reworked the map information list cards: full-height drawer layout, category/status chips moved beside the title, body shown under the title, simple bottom counts on the left, created time and distance on the right, and a lighter detail chevron.", - "2026-05-21: Re-ran node --check pages/map/map.js; node scripts/check-json.mjs; git diff --check; full WXML/WXSS DevTools compiler checks; node harness/check-harness.mjs; bash harness/init.sh; and WeChat DevTools visual check for the open list drawer. All passed except the known non-blocking native WAServiceMainContext timeout remains visible in DevTools console.", - "2026-06-12: A group map iteration added a NearbyPreview first-screen overlay for the nearest active/stale tasks, selected marker/list highlighting through decorateMapPost, and selected marker callout styling.", - "2026-06-12: A group added harness/check-map-feed.mjs to verify nearest-open sorting, stale hinting, selected preview state, selected marker callout styling, and previewTitle truncation for long task titles.", - "2026-06-12: A group verification passed node --check for pages/map/map.js, utils/post-presenter.js, and utils/geo.js; node harness/check-map-feed.mjs passed with Map feed checks passed; local wcc/wcsc compilers passed for WXML/WXSS.", - "2026-06-15: Fixed the NearbyPreview visibility regression after user feedback: the preview strip now remains visible whenever the list drawer is closed, and selected task cards reserve vertical space instead of overlapping it.", - "2026-06-15: Extended harness/check-map-feed.mjs to guard that NearbyPreview is not hidden by selectedPost state and that selected-card spacing is preserved while the preview strip is visible.", - "2026-06-15: After user feedback that the bottom task list entry and drawer were still invisible, changed the collapsed map task entry to a cover-view bottom dock and shrank the native map to 38vh while the drawer is open so normal task cards render below the native map instead of over it.", - "2026-06-15: Updated scripts/check-map-list-resilience.mjs and harness/check-map-feed.mjs to guard the cover-view list dock, list-open map state, native map height handoff, and drawer placement below the shrunken map.", - "2026-06-15: After user screenshot showed the list dock overlapping the locate control and hiding the discover control, moved the cover-view map tool row above the dock, kept locate/discover side by side, and changed the discover control to a solid cover-view-safe orange background.", - "2026-06-15: Extended map feed and map list resilience guards to require the discoverNearby cover-view button, the elevated map tool row, and the non-overlapping tool/dock placement.", - "2026-06-15: After user feedback that '附近优先' was unclear and selected task cards still overlapped map tool icons, renamed the dock title to '附近任务', removed instructional dock copy, and moved the locate/discover tool row to the top-right of the map.", - "2026-06-15: Extended harness/check-map-feed.mjs to reject the old '附近优先' and '点开看任务卡' copy and to guard the top-right map tool row position.", - "2026-06-15: After user preference feedback, replaced the bottom list dock with a compact top-right cover-view list entry labeled '列表' plus task count, and moved locate/discover controls back to the lower-right above the custom tabBar.", - "2026-06-15: Lifted the selected task card above the lower-right map tool row, removed the stale selected-card preview offset class, and updated DESIGN_SYSTEM plus map static guards to preserve this layout preference.", - "2026-06-15: After user clarified the top-right list button problem was alignment, not color, restored it to a white cover-view button and rendered `列表 {{visiblePosts.length}}` on one centered line.", - "2026-06-15: Updated map feed and map list resilience guards to require the one-line centered list label/count instead of separate baseline-sensitive label and count nodes.", - "2026-06-22: Added map launch focus support so pages/map/map can accept focusPostId/postId/id plus showList=1, center on the requested post, select it, and highlight it in the home list for DevTools verification flows.", - "2026-06-22: Share receiver detail now shows an explicit '回首页查这条' CTA; it uses wx.reLaunch with focusPostId and showList=1 so users return to the map homepage with the current task located and the list open, while the receiver action strip static guard covers the CTA and focused map URL.", - "2026-06-22: Fixed the focused return-home path so the startup diagnostics panel no longer covers the map when showList=1 opens the task drawer; map feed guard now requires diagnostics to be suppressed while the list is open and hidden immediately after the focused task is ready.", - "2026-06-22: Refreshed the manual share demo data so post_001 stays active for 30 days, old local storage copies are refreshed before they expire, and the share receiver action guard verifies the long-lived fixture and local preference path; DevTools showed the share page with `30天后过期` and the `回首页查这条` button visible.", - "2026-06-22: Moved the focused home-return CTA out of the active-only receiver action strip and into the share receiver guide, so expired share pages still show `回首页查这条`; the share receiver action guard now verifies expired share context exists and the CTA no longer depends on receiver actions.", - "2026-06-14: Added scripts/check-map-list-resilience.mjs on the P iteration to statically guard the map list drawer, card, title/tag row, summary, thumbnail, footer, counts, and detail affordance WXML/WXSS structure.", - "2026-06-14: Q iteration wired the map list static guard into scripts/check-devtools-readiness.mjs so preflight now runs publish flow, trust insight, candidate flow, and map list resilience checks before manual DevTools or real-device acceptance.", - "2026-06-14: R iteration made scripts/prepare-manual-test-run.mjs explicitly announce that manual run preparation first runs readiness preflight, including the map list static guard, and that this still does not prove DevTools or real-device visual acceptance.", - "2026-06-14: S iteration added a dedicated map-list-visual-smoke journey to harness/manual-test-results.example.json plus product and QA guidance for recording long-title, long-body, image/no-image, safe-area, native map layer, scroll, marker/list, and detail-link observations without marking unrun visual checks as passed.", - "2026-06-14: T iteration updated scripts/check-manual-evidence.mjs so manual evidence files must contain exactly one map-list-visual-smoke journey, preventing the map list visual evidence slot from being deleted or duplicated silently.", - "2026-06-14: U iteration added scripts/prepare-map-list-blocked-evidence.mjs to create an ignored local blocked draft for map-list-visual-smoke when DevTools or device access prevents real visual smoke, while keeping passed=0 and warning that blocked evidence is not UI passed or failed evidence.", - "2026-06-14: V iteration added scripts/prepare-map-list-blocked-summary.mjs to chain the blocked local JSON helper with the sanitized manual summary generator, producing ignored local reviewer-readable blocked summaries while preserving map-list-visual-smoke=blocked, passed=0, and the warning that summaries are not UI passed evidence.", - "2026-06-14: W iteration added scripts/check-map-list-blocked-summary.mjs to verify ignored blocked result JSON and ignored manual summary markdown stay aligned on overallStatus=blocked, map-list-visual-smoke=blocked, passed=0, and target evidenceCount=0.", - "2026-06-14: X iteration enhanced scripts/check-map-list-blocked-summary.mjs to also verify blocked summary branch, commit, target actual, target followUp, and target blocker/risk content still trace back to the same ignored blocked result JSON.", - "2026-06-14: Y iteration wired scripts/check-map-list-blocked-summary.mjs into scripts/prepare-map-list-blocked-summary.mjs so blocked JSON and sanitized summary generation now automatically runs the status and source-integrity guard before reporting success.", - "2026-06-14: Z iteration updated scripts/prepare-map-list-blocked-summary.mjs to print a post-edit rerun guard command after wrapper success, so edited local blocked JSON/summary files must be rechecked with scripts/check-map-list-blocked-summary.mjs before review citation.", - "2026-06-14: AA iteration added scripts/check-map-list-blocked-summary-preflight.mjs to scan ignored local blocked summaries before review and rerun the blocked summary guard for each matching results/summary pair, while warning that preflight is not UI passed evidence.", - "2026-06-14: AB iteration wired scripts/check-map-list-blocked-summary-preflight.mjs into harness/init.sh and scripts/check-devtools-readiness.mjs so default startup/readiness checks now rerun ignored local blocked summary guards without implying UI passed evidence.", - "2026-06-14: AC iteration added npm-level check scripts so npm run check now runs JSON, harness, and readiness gates, with readiness carrying the ignored local blocked summary preflight while still warning that npm checks are not UI passed evidence.", - "2026-06-14: AD iteration added .github/workflows/readiness.yml so push and pull_request automation can run npm run check, bringing JSON, harness, readiness, and ignored local blocked-summary preflight gates into a minimal CI path without claiming UI passed evidence.", - "2026-06-14: AE iteration added a CI required-check runbook and stable workflow job display name so remote Actions triggering and branch protection required status checks can be verified without claiming they are already configured or that UI passed.", - "2026-06-14: AF iteration added explicit npm DevTools smoke and port diagnostic commands so the current 9420 service-port blocker can be checked manually without adding GUI-dependent checks to CI/default npm check; current strict smoke remains blocked with no listener and UI smoke was not executed.", - "2026-06-14: AG iteration added an explicit npm recovery dry-run command so DevTools service-port recovery can be previewed with before/actions/after/next-step evidence without quitting or reopening DevTools, while default npm check still avoids GUI side effects.", - "2026-06-15: AH iteration added ignored local DevTools recovery dry-run report generation plus a guard so before/actions/after/next-step recovery drafts can be handed off without claiming DevTools recovered or UI smoke passed.", - "2026-06-15: AI iteration added a manual preflight to scan ignored local DevTools recovery dry-run reports and rerun the report guard before handoff, without adding local report checks to default npm check or claiming UI passed.", - "2026-06-17: Z viral real-evidence recovery iteration added --app-quit-reopen to scripts/recover-devtools-service-port.mjs as a mutually exclusive opt-in app-level quit plus existing CLI open recovery mode; default and --dry-run diagnostics still skip recovery side effects.", - "2026-06-17: Z added scripts/check-devtools-recovery.mjs plus npm scripts check:devtools-recovery and recover:devtools-app-quit; scripts/check-devtools-readiness.mjs runs the static guard and requires the viral-real-evidence-recovery product/QA docs without executing app quit.", - "2026-06-17: Z verification passed node --check for recovery/readiness scripts, node scripts/check-devtools-recovery.mjs, dry-run app recovery, mutual-exclusion argument rejection, node --no-warnings scripts/check-devtools-readiness.mjs, JSON/harness checks, and git diff --check; an explicit npm run recover:devtools-app-quit attempt completed app-level quit from the DevTools bundle id but CLI open still timed out with IDE may already started at port 9420, and after status remained DevTools 9420 connect_refused/smoke blocked with no real DevTools or device passed evidence.", - "2026-06-17: AA DevTools port deep-forensics iteration enhanced scripts/inspect-devtools-port-state.mjs with read-only DevTools user-data root counts, sanitized WeappLog tail counters, recent launch target lines with/without --enable-service-port, historical cli server started signals, bundled CLI global.enableCLI gate detection, and conservative diagnosis codes service_port_flag_missing, service_port_flag_mixed_recent_evidence, service_port_flag_gate_detected, and historical_service_port_success.", - "2026-06-17: AA added scripts/check-devtools-port-forensics.mjs plus npm script check:devtools-port-forensics; scripts/check-devtools-readiness.mjs now runs that no-side-effect static/read-only guard and requires the devtools-port-deep-forensics product/QA docs without quitting/opening DevTools.", - "2026-06-17: AA verification passed node --check for inspect/port-forensics/readiness scripts, node scripts/check-devtools-port-forensics.mjs, npm run check:devtools-port-forensics, node scripts/inspect-devtools-port-state.mjs --project /tmp/street-tasks-iter-worktrees/devtools-port-deep-forensics --port 9420, node --no-warnings scripts/check-devtools-readiness.mjs, JSON/harness checks, and git diff --check; the read-only report still shows status blocked with declared_without_listener, connect_refused, service_port_flag_mixed_recent_evidence, historical_service_port_success, and service_port_flag_gate_detected, so DevTools UI/device evidence remains unpassed.", - "2026-06-17: AB DevTools service-port config forensics iteration enhanced scripts/inspect-devtools-port-state.mjs with read-only config scanning over DevTools vendor/local-data JSON candidates, reporting only category counts, mtime buckets, service-port key summaries, enabled states, port values, confidence, and next human confirmation while suppressing raw config content and config file names.", - "2026-06-17: AB added devtools-service-port-config-forensics product/QA docs and extended scripts/check-devtools-port-forensics.mjs plus scripts/check-devtools-readiness.mjs so readiness requires the config-forensics docs and statically guards config diagnosis codes, enabledStates/portValues summaries, side-effect bans, and raw config suppression.", - "2026-06-17: AB verification passed node --check for inspect/port-forensics/readiness scripts, node scripts/check-devtools-port-forensics.mjs, node scripts/inspect-devtools-port-state.mjs --project /tmp/street-tasks-iter-worktrees/devtools-service-port-config-forensics --port 9420, and node --no-warnings scripts/check-devtools-readiness.mjs; the read-only report shows status blocked with service_port_config_disabled, config state disabled, port state matches_9420, conflict count 0, not-claimed smoke/UI/real-device evidence, no listener, and connect_refused, so DevTools UI/device evidence remains unpassed and the next step is manual UI Service Port enablement.", - "2026-06-17: AB post-review fix strengthened service-port config forensics so port conflicts are diagnosed before enabled target-port matches, sensitive config keys are denied before matching/recording, numeric port summaries only come from an explicit service/ide/http/cli/security port allowlist, and the static guard now checks conflict ordering, sensitive denylist coverage, documented diagnosis vocabulary, and broader side-effect API/command bans.", - "2026-06-17: AC DevTools Service Port UI confirmation iteration added a read-only npm/node entry for user-manual Service Port UI confirmation rechecks plus a static guard for UI state parameters, AC status vocabulary, no-side-effect boundaries, sensitive output suppression, blocked nextStep, and the reminder that port/smoke ready is not viral journey passed.", - "2026-06-17: AC verification passed node --check for the UI confirmation prepare/static guard scripts, node scripts/check-devtools-ui-confirmation.mjs, blocked outputs for not_confirmed and disabled UI states, enabled+matches_9420 output as blocked_no_listener with no journey passed claim, strict blocked nonzero behavior, node --no-warnings scripts/check-devtools-readiness.mjs, JSON/harness checks, and git diff --check.", - "2026-06-17: AC post-review fix redacted the existing readiness DevTools manual-run preparation output before forwarding it, aligned checklist notClaimed wording to the exact no-journey-passed phrase, and documented ready_for_manual_journey as requiring uiPortState=matches_9420 plus strictSubcommandNonzero=no.", - "2026-06-17: AD viral manual journey evidence packet iteration added scripts/prepare-viral-journey-evidence-packet.mjs as a read-only packet entry that runs only the AC UI confirmation subcommand, gates ready_to_execute_manual_journey on AC ready_for_manual_journey, suppresses raw child output/local paths/sensitive values, and derives the seven manual journeys from harness/viral-journey-manual-results.example.json.", - "2026-06-17: AD added scripts/check-viral-journey-evidence-packet.mjs plus npm scripts prepare:viral-journey-evidence-packet and check:viral-journey-evidence-packet; scripts/check-devtools-readiness.mjs now requires the AD packet docs/scripts and runs only the static guard, not the UI-state-dependent packet prepare command.", - "2026-06-17: AD verification passed node --check for the two packet scripts and readiness, node scripts/check-viral-journey-evidence-packet.mjs, a disabled UI blocked packet output with status not_ready_for_manual_journey and exact notClaimed text, strict blocked exit 1, node --no-warnings scripts/check-devtools-readiness.mjs, node scripts/check-json.mjs, node harness/check-harness.mjs, git diff --check, and bash harness/init.sh; this produced no DevTools UI, real-device, or viral journey passed evidence.", - "2026-06-17: AE viral manual summary integrity iteration added scripts/check-viral-manual-summary-integrity.mjs and scripts/check-viral-manual-summary-integrity-preflight.mjs to compare ignored local viral journey JSON/Markdown pairs on branch, commit, overallStatus, seven journey id/title/status/actual/evidenceCount/blocker/followUp fields, while rejecting raw evidence content, sensitive values, and misleading UI-passed claims.", - "2026-06-17: AE wired the viral summary integrity preflight into package.json and scripts/check-devtools-readiness.mjs, required the viral-manual-summary-integrity product/QA docs, routed create-manual-summary.mjs to the viral manual evidence checker for viral local result files, and narrowed map-list blocked summary preflight so viral summaries are not misclassified as map-list blocked summaries.", - "2026-06-17: AE verification covered syntax checks for the new scripts, empty viral summary preflight, an ignored blocked viral draft plus generated Markdown summary positive guard, an intentionally bad branch summary rejected by the guard, npm run check:viral-manual-summary-integrity, node --no-warnings scripts/check-devtools-readiness.mjs, JSON/harness checks, git diff --check, npm run check, and bash harness/init.sh; the local draft remains blocked and does not provide DevTools UI, real-device, or viral journey passed evidence.", - "2026-06-17: AF viral manual artifact manifest iteration added scripts/check-viral-manual-artifact-manifest.mjs and scripts/check-viral-manual-artifact-manifest-preflight.mjs to verify ignored local artifact manifests against viral journey results, requiring exact seven journey coverage, status/evidence-count alignment, required/conditional artifact slots, redacted artifactRef values, payload/cloud field-name-only observations, exact notClaimed language, and sensitive/raw value rejection.", - "2026-06-17: AF wired the artifact manifest preflight into package.json and scripts/check-devtools-readiness.mjs, required the viral-manual-artifact-manifest product/QA docs, and added harness/manual-artifact-manifest.local*.json to ignored local evidence patterns so attachment manifests are never committed as product evidence.", - "2026-06-17: AF review follow-up made the artifact manifest guard scan raw manifest file text before JSON.parse, reject duplicate JSON keys, align required/conditional slots with the QA checklist including risk-state-note and conditional readback/payload slots, ban coordinate/environment/identity keys, require stronger timeline payload and CloudBase readback fields, and require blocked/failed manifest sourceBlocker/sourceFollowUp to match source results.", - "2026-06-17: AF second review follow-up disallowed blocked conditional artifact slots when the source journey is passed, while permitting tightly justified not_applicable, and added a fieldsObserved sensitive-name denylist so coordinate, environment, identity, credential, raw payload, and raw CloudBase field names cannot be cited as safe field-only observations.", - "2026-06-17: AF third review follow-up derives conditional slot triggers from source results and environment: CloudBase enabled/deployed makes cloud-readback triggered, inspectable payload fields make payload-sample triggered, and triggered conditional slots on passed journeys must be present rather than not_applicable; snake_case identity/env aliases are now blocked in both object keys and fieldsObserved.", - "2026-06-17: AF final review follow-up added raw/cloud/file/event canonical aliases such as rawpayload, rawquery, clouduri, cloudfileid, fileid, and eventid to the shared sensitive field-name set, closing raw_payload object-key and cloud_file_id fieldsObserved bypasses; final review returned No Critical/Important findings and Ready yes.", - "2026-06-17: AF verification covered node --check for the new artifact manifest scripts and readiness, a blocked ignored local viral results/manifest positive guard, preflight and npm run check:viral-manual-artifact-manifest positive runs, and negative cases for duplicate-key raw path leakage, missing risk-state-note, sensitive latitude key, source blocker mismatch, timeline payload missing title/imageUrl, weak CloudBase readback fields, raw camelCase payload leakage, sensitive fieldsObserved latitude/env_id, snake_case identity keys, passed source journey with blocked conditional payload slot, triggered CloudBase readback not_applicable, raw_payload object key, and cloud_file_id fieldsObserved; final npm run check and bash harness/init.sh returned 0, but the local fixture remains blocked and does not provide DevTools UI, real-device, or viral journey passed evidence.", - "2026-06-22: Multi-agent optimization round 4 hardened map lifecycle races: first onShow skips duplicate onLoad refresh, hide/unload deactivate invalidates posts/location/discovery request generations, in-flight refresh/location/discovery callbacks drop stale results, and diagnostic timers are cleared before inactive updates.", - "2026-06-22: harness/check-map-feed.mjs now guards map inactive/stale callback paths, diagnostic timer cleanup, focused launch callback guards, and discovery request invalidation; round 4 reviewers Parfit, Lorentz, and Plato all voted PASS.", - "2026-06-22: Performance round 1 kept pages/map buildPosts on raw localOnly posts and built NearbyPreview from baseVisiblePosts to avoid duplicate decorateMapPost work; after a reviewer caught pre-decoration and raw selectedPost edge cases, corrected guards passed re-review.", - "2026-06-22: Performance round 2 collapsed map viewport filtering, category counts, and open-post count into one posts.forEach pass; after fixing the discovery selectedPost decoration regression, reviewers Galileo, Kant, and Faraday voted PASS.", - "2026-06-22: Performance round 3 added source-aware short-TTL posts caching for localOnly listPosts to avoid repeated wx storage reads while preventing local/cloud cache pollution; after multiple reviewer-caught source and cloud-mutator fixes, reviewers Hilbert, Peirce, and Godel voted PASS.", - "2026-06-22: Performance round 4 prefiltered hidden raw posts before derivePost/sort while forcing normal listPosts callers to includeHidden=false and preserving listAllPosts hidden visibility; after Banach caught includeHidden leakage, reviewers Pascal, Gibbs, and Popper voted PASS.", - "2026-07-09: Reviewed the current local main dirty visual refresh and fixed map cover-view overlay regressions by keeping selected task card/list/tool/diagnostic cover-view styles hard-coded instead of CSS-variable based; scripts/check-map-list-resilience.mjs now guards this.", - "2026-07-10: WeChat DevTools Stable v2.01.2510290 direct UI smoke rendered map tiles, the collapsed list entry, locate/discover controls, selected-task cover-view card, expanded task drawer, and custom tabBar; the existing WAServiceMainContext timeout remained and no real-device claim was made.", - "2026-07-10: Fixed the screenshot-reported blank strip above the home map and map bleed around the tabBar bottom edge. The map viewport is now explicitly pinned to top:0 and ends above the 116rpx tabBar plus safe area, while the fixed map-page background stays white behind device corners; DevTools confirmed both collapsed and expanded list states.", - "2026-07-10: Replaced the custom tabBar cover-view/cover-image tree with normal view/image nodes after the map stopped above the dock. DevTools confirmed the map tiles and the single custom tabBar remain visible together.", - "2026-07-14: Moved raw map posts from Page.data to controller-only this.posts, removed unrendered NearbyPreview/open-count work, and skipped no-selection region-end rebuilds. A 100-post synthetic benchmark reduced average refresh setData payload from 183,539 to 119,848 bytes (-34.7%); static/readiness checks passed, while DevTools/real-device performance remains unverified." - ], - "notes": "已有实现未等于 harness passing;需要手动小程序验证证据。2026-05-18 UI 刷新采用 TDesign 式设计 token 和原生小程序控件落地,未引入 tdesign-miniprogram npm 依赖,避免在当前无小程序 npm 构建链路下增加编译风险。底部 tabBar 已由悬浮胶囊改为贴底不透明导航栏。2026-05-19 发布页和地图抽屉已落地 design-ui-designer 组件化优化。2026-05-21 白屏排查未发现 WXML/WXSS/JS 语法错误,已移除 tabBar 中新增的空 cover-view 指示条以降低渲染兼容风险;同日继续排查确认 DevTools hot reload/getAppConfig 缓存会触发 subPackages 内部错误,清编译缓存并终止重编译后地图恢复,同时加入页面诊断和云读取 fallback;随后针对 `Error: timeout` 将地图首屏改为本地优先,定位、云函数列表和地图 region 读取都不再阻塞启动;详情页已补上本地 fallback,避免本地列表任务因云端不存在而显示“任务不存在”。信息列表已改为铺满抽屉、标题后置标签、底部轻量统计。A 组地图迭代新增 NearbyPreview 首屏附近优先预览和选中态联动,并收敛长标题风险;2026-06-15 用户手测暴露普通 view/button 覆盖原生 map 不可靠,因此折叠态任务入口、定位和找一找都保持为 cover-view/cover-image;经过多轮用户截图反馈,最终布局按偏好收敛为右上角白色“列表 N”单行居中按钮、右下角定位/找一找工具行、选中任务卡上移避让工具行,打开列表时仍缩小 native map 到 38vh 让普通任务卡抽屉位于 map 下方。2026-06-14 已新增地图列表静态韧性检查并接入 readiness/preflight,且手测准备 helper 会显式运行并提示该 preflight;这只降低结构、样式和执行漏跑风险,不代表 DevTools 或真机视觉验收通过。S 组补充了地图列表真实视觉 smoke 的模板 journey 和检查清单,T 组把该 journey 变成 manual evidence 必备项,U 组提供 blocked local draft helper,V 组把 blocked draft 与 sanitized summary 生成串联,W 组为 blocked JSON/summary 状态一致性增加自动 guard,X 组进一步守住 summary 与 JSON 的 branch/commit/blocker/followUp 同源完整性,Y 组把增强 guard 接入 wrapper 默认生成链路,Z 组又把生成后手工编辑必须复跑 guard 的命令直接打印到 wrapper 成功输出,AA 组新增评审前一键 preflight 扫描 ignored local blocked summaries 并逐对复跑 guard,AB 组把该 preflight 接入 harness/init.sh 和 DevTools readiness 默认检查,AC 组把 JSON/harness/readiness 门禁暴露为 npm 级 `check` 脚本,AD 组新增最小 GitHub Actions workflow 在 push/pull_request 上运行该检查,AE 组补充 required-check runbook 和稳定 job display name 以便远端 Actions/分支保护后续验证,AF 组补充 `inspect:devtools-port` 与 `check:devtools-smoke` 手动入口,明确当前 9420 service-port blocker 可复查但不进入默认 CI/check,AG 组补充 `inspect:devtools-recovery` dry-run 入口,明确恢复动作前后的 before/actions/after/next-step 证据且不执行 quit/open 副作用,AH 组补充 ignored local recovery dry-run report 生成与 guard,降低交接草稿被改写成恢复成功或 UI passed 的风险,AI 组再补充手动 preflight 扫描所有 ignored local recovery reports 并逐份复跑 guard,降低多份本地草稿交接前漏检风险;这些仍只是在证据结构上预留、守护、演练阻塞记录并提升评审可读性。viral 侧 AD/AE/AF 又把七条旅程证据包、JSON/Markdown summary 同源和附件 manifest 同源/脱敏 guard 串起来,能让真实手测后的截图/录屏/payload/readback 附件更安全地被评审引用;但这些 guard 仍不产生真实系统分享、朋友圈、payload、CloudBase readback、窄屏或真机 passed evidence。真实 safe area、地图抽屉、原生地图层、列表滚动、带图/无图和详情点击仍需 WeChat DevTools 或真机观察。仍需在 WeChat DevTools/真机中确认 safe area、键盘、地图抽屉、定位授权分支和多分类详情链路。" - }, - { - "id": "publish-001", - "priority": 2, - "area": "publish", - "title": "发布短时附近任务", - "user_visible_behavior": "用户可以填写分类、标题、描述、地点、坐标、图片和过期时间,并发布一条新任务。", - "status": "not_started", - "verification": [ - "在 WeChat DevTools 中打开发布 tab。", - "未登录状态尝试发布,确认出现登录或权限提示。", - "登录后填写每个分类的最小有效表单并提交。", - "确认成功提示、分享入口和地图/详情页可见新任务。", - "验证必填项、字数、图片数量和过期时间的错误提示。" - ], - "evidence": [ - "2026-05-15: Image publishing now compresses selected images, limits them to 4 files under 1.5MB each, uploads only to CloudBase Storage, and stores cloud:// file IDs.", - "2026-05-15: Image posts set requireCloud so cloud post save failures no longer fall back to local-only storage.", - "2026-05-15: If image upload or later post creation fails, newly uploaded cloud files are cleaned up and image items are reset for retry.", - "2026-05-15: node --check passed for pages/publish/publish.js, utils/store.js, and cloudfunctions/posts/index.js.", - "2026-05-15: git diff --check passed.", - "2026-05-15: node scripts/check-json.mjs passed, output: Checked 11 JSON files.", - "2026-05-15: node harness/check-harness.mjs passed, output: Harness OK: 6 features checked.", - "2026-05-15: bash harness/init.sh passed after npm-free fallback; package-lock has no external dependencies, then JSON and harness checks passed.", - "2026-06-12: B 组发布迭代产品假设:用户在提交时才触发定位和校验会造成失败感;把发布准备度和位置确认前置,应提升表单完成率。", - "2026-06-12: Added pages/publish/publish-state.js and scripts/check-publish-flow.mjs to model identity/content/category/location readiness before submission.", - "2026-06-12: Publish page now shows a readiness checklist, title/body counters, explicit current-location confirmation, direct expiry buttons, and a bottom action that names the next missing step.", - "2026-06-12: node --check passed for pages/publish/publish.js and pages/publish/publish-state.js; node --no-warnings scripts/check-publish-flow.mjs passed with Publish flow checks passed.", - "2026-06-12: Second B-group iteration refined publish readiness states for locating, failed location retry, only-location-missing submission, and lost/found intent completion.", - "2026-06-12: node --no-warnings scripts/check-publish-flow.mjs passed after adding coverage for locating, needs-location, failed-location retry, lost-found missing direction, and ready lost-found states.", - "2026-06-12: Third B-group iteration added primaryAction to buildPublishState so publish submit behavior is driven by an explicit state-machine action instead of re-inferring missing fields in the page.", - "2026-06-12: node --no-warnings scripts/check-publish-flow.mjs first failed because primaryAction was missing, then passed after covering login, fill, confirmLocation, waitLocation, and publish states.", - "2026-06-12: WeChat DevTools local compilers passed for B publish branch after third iteration: wcc compiled all WXML and wcsc -lc compiled all WXSS with exit code 0 and no output.", - "2026-06-12: Attempted WeChat DevTools CLI open and preview for B publish branch after enabling the service port prompt, but both failed with wait IDE port timeout against port 9420; no preview QR/info was produced.", - "2026-06-16: Viral publish iteration added utils/publish-spread.js and upgraded the detail page's from=publish success card into a structured spread plan with audience, signal goal, follow-up, image/comment hints, and closed-task no-spread handling.", - "2026-06-16: Added scripts/check-publish-spread.mjs and wired it into scripts/check-devtools-readiness.mjs so npm readiness checks cover category-specific spread copy, lost/found intent differences, resolved/expired no-spread handling, image/comment hints, and share path parameter preservation.", - "2026-06-23: Publish expiry presets changed from 6h/1d/3d to 1d/1w/1mo plus a custom deadline option.", - "2026-06-23: Custom publish expiry uses date and time pickers, records exact expiresAt when valid, and blocks custom times earlier than 30 minutes from now.", - "2026-06-23: Local store and posts cloud function now validate preset expiry hours [24,168,720] and accept exact custom expiresAt values without a 30-day maximum.", - "2026-06-23: Removed the custom expiry maximum after user feedback; the date picker no longer has an end date and publish-flow checks cover a 45-day custom expiry.", - "2026-06-23: Updated publish expiry presets to 1w/1mo/long-term/custom; long-term posts carry expiryType=long_term and render as 长期有效 instead of a large day count.", - "2026-06-23: Publish now defaults to the long-term expiry option while keeping the visible option order as 1周/1月/长期/自定义.", - "2026-06-23: Verification passed node --check for publish page/state, store, format helper, and posts cloud function; node --no-warnings scripts/check-publish-flow.mjs; node --no-warnings scripts/check-devtools-readiness.mjs; npm run check; bash harness/init.sh; git diff --check; and local WXML/WXSS compiler exits 0.", - "2026-07-10: WeChat DevTools direct UI smoke scrolled the publish form through category, location, expiry, and custom-expiry controls; the fixed bottom action stayed above the custom tabBar without obscuring the final fields. No post was submitted.", - "2026-07-10: Fixed the screenshot-reported duplicate tabBar after scrolling by replacing the fixed native cover-view tab tree with normal view/image nodes. Repeated Page_Down actions left exactly one 地图/发布/管理/我的 set in the active publish Webview and one visible dock." - ], - "notes": "图片发布已改为云端必需;仍需部署更新后的 posts 云函数,并在 WeChat DevTools/真机中验证上传、跨用户浏览和过大图片拦截。 2026-06-12 B 组发布迭代新增发布准备度、位置确认和有效期按钮;尚未在 WeChat DevTools/真机验证定位授权、键盘遮挡、图片上传和真实发布闭环。 第二轮 B 组已收敛定位失败/重试路径和 readiness 文案;定位授权弹窗、键盘、图片上传和真实发布仍需 DevTools/真机验证。 第三轮 B 组已将发布主动作显式化为 primaryAction;仍需 DevTools/真机验证真实定位授权、键盘安全区、图片上传和跳转。 2026-06-16 发布后扩散计划只在 from=publish 详情上下文显示,普通详情页不展示;分享路径会移除 from=publish 避免接收者看到发布者专属卡。2026-06-23 有效期预设已改为 1周/1月/长期/自定义,默认选择长期;自定义时间只校验至少晚于当前 30 分钟,不限制最大日期。长期预设保存为 long_term 并显示 长期有效;功能影响是默认发布任务不会很快自动过期,因此评论/确认/列表展示会长期保持可用,关闭、过时、举报和自定义截止时间仍按原逻辑生效。仍需在 DevTools/真机中实际操作日期/时间 picker、提交本地/云端发布并检查详情页过期文案。DevTools CLI open/preview 当前被 9420 服务端口超时阻塞,需在 DevTools UI 中确认服务端口并手动重开项目。" - }, - { - "id": "detail-trust-001", - "priority": 3, - "area": "detail", - "title": "任务详情、评论与结构化信任动作", - "user_visible_behavior": "用户进入详情页后可以查看评论,登录后补充线索或提问,并可以确认仍有效、标记过时、举报或关闭自己的任务,重复动作会被阻止。", - "status": "blocked", - "verification": [ - "从地图或列表进入一条 active 任务详情。", - "确认详情页不再展示地图,评论区在信任动作下方直接可见。", - "确认评论列表默认不展示发布评论面板,右下方显示悬浮评论按钮。", - "游客状态点击悬浮评论按钮,确认显示登录评论引导。", - "登录后点击悬浮评论按钮,确认弹出评论框。", - "在弹窗里输入空评论,确认不会提交并出现提示。", - "在弹窗里发布一条有效评论,确认弹窗关闭且评论出现在列表顶部并显示昵称与时间。", - "重新进入详情页,确认评论仍然保留。", - "进入 resolved 或 expired 任务详情,确认评论区只读且不能继续发布。", - "确认、过时、举报展示为一行三列,并且按钮或只读统计中直接展示对应数量。", - "点击确认,确认按钮数量增加且同一用户不能重复确认。", - "连续触发过时动作,确认按钮数量增加且 staleCount 达到阈值后状态变为 stale。", - "触发举报,确认按钮数量增加且达到阈值后普通列表不再显示 hidden 任务。", - "用发布者身份关闭任务,确认状态变为 resolved。" - ], - "evidence": [ - "2026-05-14: Code implementation added detail comments UI, local post_comments storage, and CloudBase listComments/createComment actions.", - "2026-05-14: Detail page layout removed the inline map and separate metrics card; confirm/stale/report now render as one three-column row with counts on the controls.", - "2026-05-14: Comment composer moved out of the default list view; a lower-right floating comment button now opens a bottom comment dialog.", - "2026-05-14: node --check passed for utils/store.js, pages/detail/detail.js, and cloudfunctions/posts/index.js.", - "2026-05-14: npm run check:json passed, output: Checked 11 JSON files.", - "2026-05-14: node harness/check-harness.mjs passed, output: Harness OK: 6 features checked.", - "2026-05-14: bash harness/init.sh passed after the comment changes; npm ci reported up to date, then check:json and check-harness both passed.", - "2026-05-14: Re-ran node --check for detail/store/cloud function, npm run check:json, node harness/check-harness.mjs, and bash harness/init.sh after compacting the detail layout; all passed.", - "2026-05-14: Re-ran the same syntax, JSON, harness, and init checks after moving comment publishing into the floating-button dialog; all passed.", - "2026-05-21: Added a publisher meta row to the detail hero: avatar/name on the left, place/distance/created time right-aligned on the same line.", - "2026-05-21: Re-ran node --check pages/detail/detail.js; node scripts/check-json.mjs; git diff --check; full WXML/WXSS DevTools compiler checks; node harness/check-harness.mjs; and WeChat DevTools detail rendering. All passed except the known non-blocking native WAServiceMainContext timeout remains visible in DevTools console.", - "2026-06-12: C 组详情迭代产品假设:详情页已有评论和信任动作,但用户仍需从原始计数自行判断可信度;信任判断摘要应让下一步行动更清楚。", - "2026-06-12: Added formatTrustInsight in utils/format.js and wired detail rendering to refresh insight after post load, comment load, and comment submit.", - "2026-06-12: Added a TrustInsight panel before confirm/stale/report actions, with segmented metrics for confirmations, stale reports, reports, and comments plus an inline comment entry in the comments header.", - "2026-06-12: node --check passed for pages/detail/detail.js and utils/format.js; a Node import check for formatTrustInsight passed with Trust insight checks passed.", - "2026-06-12: Second C-group iteration changed TrustInsight confirmation copy from an absolute validity claim to cautious confirmation-signal wording, and changed comments-only title to guide users to read comments first.", - "2026-06-12: Added harness/check-trust-insight.mjs; it first failed on the old confirmation wording, then passed after covering report-over-confirm, stale-over-confirm, resolved/expired, comments-only, and short metric notes.", - "2026-06-12: WeChat DevTools local compilers passed for C detail branch: wcc compiled all WXML and wcsc -lc compiled all WXSS with exit code 0 and no output.", - "2026-06-16: Detail page now derives a publishSpreadPlan only when entered with from=publish, keeping ordinary detail entries focused on task content, trust insight, comments, and trust actions.", - "2026-06-16: Added utils/share-message.js plus scripts/check-share-message.mjs so detail share copy is generated from category, status, comments, confirmations, stale counts, and report counts with a cautious path that preserves the post id and adds from=share.", - "2026-06-16: Detail page candidate combines the from=publish spread plan with the ordinary detail share guidance panel; publish shares keep receiver context on a normal detail page while ordinary shares use cautious status/category copy.", - "2026-06-16: Added utils/share-receiver.js plus scripts/check-share-receiver.mjs so from=share detail entries can explain why the task was sent, what to do first, and how to help when not on site, with cautious handling for hidden/resolved/expired/stale/high-report cases.", - "2026-06-16: Detail page now renders a lightweight receiver guidance panel only when from=share, while from=publish still shows the spread plan and ordinary detail entries keep the existing share prompt.", - "2026-06-16: Viral comment relay iteration added harness/viral-comment-relay-product-brief.md and harness/viral-comment-relay-design-checklist.md to define the comment-success relay hypothesis, scope, risk states, and narrow-screen QA expectations.", - "2026-06-16: Added utils/comment-relay.js plus scripts/check-comment-relay.mjs so comment-success relay copy is generated from post status, fresh comment summary, comment count, stale/report risk, and resolved/expired/hidden states.", - "2026-06-16: Detail page now resets commentRelayPrompt on page load/reload, sets it only after successful comment submission, renders a lightweight relay panel before comments, and uses a comment-relay share payload only for the safe open-type share CTA.", - "2026-06-16: Comment relay source iteration added harness/viral-comment-source-product-brief.md and harness/viral-comment-source-design-checklist.md, added `source=comment` to the comment relay share path, and taught the share receiver guide to say someone just added a clue while still keeping stale/high-report/resolved/expired/hidden states cautious.", - "2026-06-16: Wired scripts/check-comment-relay.mjs into scripts/check-devtools-readiness.mjs and scripts/check-viral-candidate.mjs so npm run check covers the comment relay gate alongside publish/share/receiver/candidate checks.", - "2026-06-16: Comment relay verification passed: node --check for utils/comment-relay.js, pages/detail/detail.js, scripts/check-comment-relay.mjs, scripts/check-devtools-readiness.mjs, and scripts/check-viral-candidate.mjs; node --no-warnings checks for comment relay, share message, publish spread, share receiver, viral candidate, and DevTools readiness; node scripts/check-json.mjs; node harness/check-harness.mjs; git diff --check; npm run check; and bash harness/init.sh.", - "2026-06-16: Loop candidate iteration added harness/viral-loop-candidate-product-brief.md and harness/viral-loop-candidate-design-checklist.md to define the rule that publish spread, share receiver guidance, comment relay, and ordinary share guidance should not compete as simultaneous primary CTAs.", - "2026-06-16: Detail page ordinary share panel now only renders when showPublishSuccess, shareReceiverGuide, and commentRelayPrompt are all absent, preserving a single primary propagation action for each entry context.", - "2026-06-16: scripts/check-viral-candidate.mjs and scripts/check-comment-relay.mjs now guard the ordinary share panel mutual-exclusion condition alongside the existing publish/share receiver/comment relay checks.", - "2026-06-16: Confirm relay iteration added harness/viral-confirm-relay-product-brief.md and harness/viral-confirm-relay-design-checklist.md to define the confirm-success relay hypothesis, stale/report no-spread rule, source=confirm receiver context, and narrow-screen QA expectations.", - "2026-06-16: Added utils/action-relay.js plus scripts/check-action-relay.mjs so trust-action success copy is generated from action type, post status, confirmation/stale/report counts, stale/report risk, and resolved/expired/hidden states.", - "2026-06-16: Detail page now resets actionRelayPrompt on page load/reload, sets it after trust action success, hides the ordinary share panel while actionRelayPrompt exists, and uses an action-relay share payload with source=confirm only for safe confirm CTA.", - "2026-06-16: Share receiver guidance now recognizes source=confirm and tells receivers to look at the confirmation signal and comments first while keeping stale/high-report/resolved/expired/hidden states cautious.", - "2026-06-16: Receiver conversion iteration added harness/viral-receiver-conversion-product-brief.md and harness/viral-receiver-conversion-design-checklist.md to define the from=share confirm/comment conversion moment, ordinary-entry no-prompt rule, risk-state no-spread rule, and narrow-screen QA expectations.", - "2026-06-16: Added utils/receiver-conversion.js plus scripts/check-receiver-conversion.mjs so from=share users who complete confirm or comment can receive a cautious second-hop relay prompt with from=share&source=receiver.", - "2026-06-16: Detail page now resets receiverConversionPrompt on load/reload, sets it after successful confirm/comment only for share-entry users, suppresses action/comment relay when receiver conversion exists, and hides the ordinary share panel while the receiver conversion prompt is visible.", - "2026-06-16: Share receiver guidance now recognizes source=receiver and tells the next receiver this came from a relay chain, while stale/high-report/resolved/expired/hidden states remain cautious.", - "2026-06-16: Receiver conversion verification passed: node --check for utils/receiver-conversion.js, utils/share-receiver.js, pages/detail/detail.js, scripts/check-receiver-conversion.mjs, scripts/check-action-relay.mjs, scripts/check-comment-relay.mjs, scripts/check-share-receiver.mjs, scripts/check-viral-candidate.mjs, and scripts/check-devtools-readiness.mjs; node --no-warnings checks for receiver conversion, action relay, comment relay, share receiver, viral candidate, share message, publish spread, and DevTools readiness; node scripts/check-json.mjs; node harness/check-harness.mjs; git diff --check; npm run check; and bash harness/init.sh.", - "2026-06-16: Receiver action iteration added harness/viral-receiver-action-product-brief.md and harness/viral-receiver-action-design-checklist.md to define the from=share first-action hypothesis, low-risk-only button rule, no-share-button event rule, and narrow-screen QA expectations.", - "2026-06-16: Added utils/share-receiver-actions.js plus scripts/check-share-receiver-action.mjs so ordinary detail entries get no action strip, from=share active tasks with no stale/report signal get confirm/comment actions, and hidden/resolved/expired/stale/any-stale/any-report tasks do not show encouraging action buttons.", - "2026-06-16: Detail page now renders the receiver action strip inside the shareReceiverGuide only before receiverConversionPrompt exists; confirm reuses bindtap=react with data-action=confirm, comment reuses openCommentDialog, and scripts/check-devtools-readiness.mjs plus scripts/check-viral-candidate.mjs run the new check.", - "2026-06-16: Receiver action verification passed: node --check for utils/share-receiver-actions.js, pages/detail/detail.js, scripts/check-share-receiver-action.mjs, scripts/check-share-receiver.mjs, scripts/check-receiver-conversion.mjs, scripts/check-action-relay.mjs, scripts/check-comment-relay.mjs, scripts/check-viral-candidate.mjs, and scripts/check-devtools-readiness.mjs; node --no-warnings checks for share receiver action, share receiver, receiver conversion, action relay, comment relay, viral candidate, and DevTools readiness; node scripts/check-json.mjs; node harness/check-harness.mjs; git diff --check; npm run check; and bash harness/init.sh.", - "2026-06-16: Viral journey evidence iteration added harness/viral-journey-evidence-product-brief.md, harness/viral-journey-evidence-design-checklist.md, and harness/viral-journey-manual-results.example.json to define the real-chain verification model and a not_run manual evidence template.", - "2026-06-16: Added scripts/check-viral-journey-evidence.mjs to model the first-hop from=share entry, receiver guide/action strip visibility, ordinary share panel mutual exclusion, receiverConversionPrompt priority after confirm/comment, from=share&source=receiver second-hop path, source=receiver copy, and ordinary/risk entry no-encouraging receiver surfaces.", - "2026-06-16: Wired scripts/check-viral-journey-evidence.mjs into scripts/check-devtools-readiness.mjs and scripts/check-viral-candidate.mjs so readiness and candidate preflights run the end-to-end viral journey evidence model; the script also verifies the manual evidence example remains not_run and cannot be mistaken for DevTools or real-device proof.", - "2026-06-16: O iteration added harness/viral-journey-manual-evidence-product-brief.md and harness/viral-journey-manual-evidence-checklist.md to define ignored local viral journey result boundaries, status rules, share payload expectations, and the no-file/no-UI-passed semantics.", - "2026-06-16: Added scripts/check-viral-journey-manual-evidence.mjs so real local viral journey result files are validated when present; the checker scans ignored local files by default, rejects unignored explicit paths and the example template, validates schema/current branch/current commit/environment/required unique journeys/status-specific evidence/blocker/followUp/share-payload rules, and enforces overallStatus aggregation.", - "2026-06-16: Added scripts/prepare-viral-journey-manual-evidence.mjs with --dry-run and ignored blocked draft generation; generated drafts contain no passed journeys and print the exact rerun commands for the manual evidence checker and DevTools readiness.", - "2026-06-16: Wired the viral manual evidence gate into scripts/check-devtools-readiness.mjs and made scripts/check-viral-journey-evidence.mjs require the new gate/docs; readiness now prints that no local viral result files were found and that this is not UI passed evidence.", - "2026-06-16: Viral manual evidence verification passed for node --check on the new checker/helper and updated readiness/evidence scripts; default checker output was No viral journey manual evidence files found; nothing checked; prepare --dry-run printed the ignored blocked draft target; explicit example and unignored local inputs were rejected; temporary bad passed-without-evidence and good passed-with-evidence samples behaved as expected; no local result files were left behind.", - "2026-06-16: P iteration added harness/viral-devtools-journey-run-product-brief.md and harness/viral-devtools-journey-run-checklist.md to define the real manual-run startup package, no-side-effect boundaries, five required viral journey readout, blocked/failed/passed semantics, and share payload recording rules.", - "2026-06-16: Added scripts/prepare-viral-journey-devtools-run.mjs and npm run prepare:viral-journey-run so a single command runs read-only DevTools port forensics, no-side-effect smoke access probe, recovery dry-run guidance, viral evidence draft dry-run, existing local evidence scan, and the five required journey package without writing files or opening/quitting DevTools.", - "2026-06-16: Wired the viral DevTools manual-run preparation into scripts/check-devtools-readiness.mjs; default readiness can report current 9420 port/smoke blockers as environment blockers while still exiting successfully unless strict mode is requested, and it repeatedly states this is not DevTools or real-device UI passed evidence.", - "2026-06-16: Q iteration added harness/viral-blocked-evidence-capture-product-brief.md and harness/viral-blocked-evidence-capture-checklist.md to define when a DevTools blocker should be captured into ignored local viral journey evidence, what fields must be recorded, and why capture success is still not UI passed.", - "2026-06-16: Added scripts/capture-viral-journey-blocked-evidence.mjs and npm run capture:viral-blocked-evidence so an explicit capture command writes the current port/smoke blocker to an ignored local viral journey result JSON, marks all five journeys blocked, runs the existing manual evidence checker, and refuses unignored or accidental overwrite paths.", - "2026-06-16: Wired the Q capture command into readiness as a required file/doc boundary only; default readiness intentionally does not run capture because capture writes ignored local evidence files.", - "2026-06-16: R iteration added harness/viral-targeted-relay-product-brief.md and harness/viral-targeted-relay-design-checklist.md to define a target-specific second-hop receiver relay after from=share users complete confirm/comment.", - "2026-06-16: utils/receiver-conversion.js now adds structured targetRows for low-risk receiver conversion prompts: 推荐转给, 为什么可信, 下一位先看, with category-specific copy for lost_found, help_needed, street_update, and check_in while risk/closed prompts keep targetRows empty.", - "2026-06-16: Targeted receiver relay verification passed for node --check on utils/receiver-conversion.js, scripts/check-receiver-conversion.mjs, scripts/check-viral-journey-evidence.mjs, and scripts/check-devtools-readiness.mjs; node --no-warnings checks for receiver conversion, viral journey evidence, and DevTools readiness passed, with readiness still reporting DevTools port/smoke blocked as not UI passed evidence.", - "2026-06-16: Final R verification passed: node --check pages/detail/detail.js; node scripts/check-json.mjs; node harness/check-harness.mjs; git diff --check; npm run check; and bash harness/init.sh.", - "2026-06-16: S iteration added receiverAction=confirm/comment to low-risk receiver conversion second-hop share paths while preserving from=share&source=receiver; risk/closed or weak stale/report prompts keep receiverAction out of sharePath and still expose no public relay CTA.", - "2026-06-16: Share receiver guidance now treats source=receiver&receiverAction=confirm as a cautious previous-confirmation signal and source=receiver&receiverAction=comment as a latest-comment clue, while missing/unknown receiverAction keeps the original source=receiver fallback copy and non-receiver sources ignore receiverAction.", - "2026-06-16: Detail page now preserves receiverAction from query in entryQuery and passes it into buildShareReceiverGuide without changing existing source=comment/source=confirm/source=receiver behavior.", - "2026-06-16: Receiver action-source verification passed: node --check for utils/receiver-conversion.js, utils/share-receiver.js, pages/detail/detail.js, scripts/check-receiver-conversion.mjs, scripts/check-share-receiver.mjs, and scripts/check-viral-journey-evidence.mjs; node --no-warnings checks for receiver conversion, share receiver, and viral journey evidence; targeted git diff --check for the changed runtime/check files.", - "2026-06-16: S manual evidence gate update now requires receiver-confirm-conversion sharePayload.path to include receiverAction=confirm and receiver-comment-conversion sharePayload.path to include receiverAction=comment; temporary ignored local passed evidence with both actions was accepted, and the same sample with missing receiverAction was rejected as expected.", - "2026-06-16: Final S closeout passed node scripts/check-json.mjs, node harness/check-harness.mjs, node --no-warnings scripts/check-devtools-readiness.mjs, full git diff --check, npm run check, and bash harness/init.sh; readiness still reports DevTools 9420 port/smoke blocked as not UI passed evidence.", - "2026-06-16: T iteration added harness/viral-share-reason-product-brief.md and harness/viral-share-reason-design-checklist.md to define a short, user-repeatable second-hop share reason after low-risk from=share receiver confirm/comment actions.", - "2026-06-16: utils/receiver-conversion.js now returns shareReason only when receiverConversionPrompt.shouldRelay is true: confirm says 我刚确认过,帮忙再核对一下; comment says 我刚补了线索,你先看最新评论. Risk/closed or weak stale/report prompts keep shareReason null.", - "2026-06-16: Detail page now renders receiverConversionPrompt.shareReason between targetRows and the share action, not as a fourth target row; pages/detail/detail.wxss adds compact wrapping styles that should not compress the main share button.", - "2026-06-16: T checks extend scripts/check-receiver-conversion.mjs and scripts/check-viral-journey-evidence.mjs to guard shareReason length, forbidden guarantee/pressure wording, confirm/comment distinction, risk-state absence, targetRows staying three rows, WXML ordering, and manual journey expectations.", - "2026-06-16: T verification passed node --check for utils/receiver-conversion.js, pages/detail/detail.js, scripts/check-receiver-conversion.mjs, scripts/check-viral-journey-evidence.mjs, and scripts/check-devtools-readiness.mjs; node --no-warnings checks for receiver conversion, viral journey evidence, share receiver, viral candidate, and DevTools readiness all passed. Readiness still reports DevTools 9420 port/smoke blocked and is not UI passed evidence.", - "2026-06-16: Final T closeout passed node scripts/check-json.mjs, node harness/check-harness.mjs, git diff --check, node --no-warnings scripts/check-viral-journey-manual-evidence.mjs, npm run check, and bash harness/init.sh; npm readiness output includes the new confirm/comment share reason manual-run expectations while still reporting DevTools 9420 port/smoke blocked as not UI passed evidence.", - "2026-06-17: U iteration added harness/viral-relay-channel-picker-product-brief.md and harness/viral-relay-channel-picker-design-checklist.md to define low-risk receiver-side relay channel suggestions that help users decide where to send a second-hop share without reading contacts or real WeChat groups.", - "2026-06-17: utils/receiver-conversion.js now returns relayChannels only when receiverConversionPrompt.shouldRelay is true; channels are 2-3 short label/hint scene suggestions differentiated by category, lost/found intent, and confirm/comment action. Risk/closed or weak stale/report prompts keep relayChannels empty.", - "2026-06-17: Detail page now renders relayChannels between targetRows and shareReason as lightweight wrapping chips, with no open-type share or contact/group picker behavior; the main receiver conversion share button remains the only high-weight CTA.", - "2026-06-17: U checks extend scripts/check-receiver-conversion.mjs and scripts/check-viral-journey-evidence.mjs to guard relay channel count, short copy, forbidden contact/group wording, confirm/comment differences, lost/found differences, category-specific channels, risk-state absence, no new targeting query params, WXML order, and manual journey expectations.", - "2026-06-17: U verification passed node --check for utils/receiver-conversion.js, pages/detail/detail.js, scripts/check-receiver-conversion.mjs, scripts/check-viral-journey-evidence.mjs, and scripts/check-devtools-readiness.mjs; node --no-warnings checks for receiver conversion, viral journey evidence, share receiver, viral candidate, and DevTools readiness all passed. Readiness still reports DevTools 9420 port/smoke blocked and is not UI passed evidence.", - "2026-06-17: Final U closeout passed node scripts/check-json.mjs, node harness/check-harness.mjs, git diff --check, node --no-warnings scripts/check-viral-journey-manual-evidence.mjs, npm run check, and bash harness/init.sh; npm readiness output includes the new relay channel suggestions manual-run expectations while still reporting DevTools 9420 port/smoke blocked as not UI passed evidence.", - "2026-06-17: V iteration added harness/viral-timeline-share-product-brief.md and harness/viral-timeline-share-design-checklist.md to define WeChat official timeline share boundaries, single-page mode risks, non-inducement rules, and how to compare the new system channel against U's receiver relay suggestions.", - "2026-06-17: Added utils/timeline-share.js and Page.onShareTimeline on the detail page so low-risk active task details can expose a WeChat timeline payload with query id=&from=share&source=timeline&shareChannel=timeline, while onShareTimeline never returns a custom path.", - "2026-06-17: Detail page now configures wx.showShareMenu with shareTimeline only after the loaded task is active and has no stale/report signal; unknown, stale/report, resolved, expired, hidden, or missing posts fall back to shareAppMessage only, and risk payloads use cautious titles without task images.", - "2026-06-17: Added scripts/check-timeline-share.mjs and wired it into scripts/check-devtools-readiness.mjs and scripts/check-viral-candidate.mjs to guard timeline query, no-path payloads, low-risk-only menu gating, weak stale/report exclusions, no inducement words, and readiness doc presence.", - "2026-06-17: W iteration added harness/viral-timeline-evidence-product-brief.md and harness/viral-timeline-evidence-checklist.md to define timeline evidence requirements, blocked/passed semantics, single-page mode observations, risk/closed no-timeline checks, and U receiver journey non-regression.", - "2026-06-17: Extended harness/viral-journey-manual-results.example.json and scripts/check-viral-journey-manual-evidence.mjs from five to seven required journeys by adding timeline-share-channel and timeline-risk-gating, while preserving receiverAction, shareReason, relayChannels, and risk-state receiver journey requirements.", - "2026-06-17: Updated scripts/prepare-viral-journey-devtools-run.mjs and scripts/capture-viral-journey-blocked-evidence.mjs so the run package and blocked capture both enumerate seven viral UI journeys; readiness now requires the W product/QA docs.", - "2026-06-17: W verification included a temporary ignored good sample with all seven journeys accepted, a bad timeline sample missing shareChannel=timeline rejected, and a blocked capture sample producing seven blocked journeys; all temporary local files were cleaned up.", - "2026-06-17: Precommit review tightened timeline-risk-gating so inability to inspect the system menu is no longer enough for passed; a temporary negative sample with only inability-to-inspect was rejected, while a positive no-shareTimeline observation sample passed, and older run-package plus blocked-capture docs were updated from five to seven journeys.", - "2026-06-17: X iteration added harness/viral-timeline-landing-product-brief.md and harness/viral-timeline-landing-checklist.md to define source=timeline landing-page context for users who open a nearby task from Moments.", - "2026-06-17: utils/share-receiver.js now gives low-risk active source=timeline visitors a dedicated receiver guide: kicker 朋友圈看到, title 附近任务,先核对一下, and copy that asks users to inspect status/comments before confirming or adding clues; weak stale/report and risk/closed states stay cautious.", - "2026-06-17: X checks extend scripts/check-share-receiver.mjs, scripts/check-viral-journey-evidence.mjs, and scripts/check-viral-candidate.mjs to guard timeline landing copy, forbidden proof/pressure/contact wording, risk priority, and receiver/comment/confirm source non-regression.", - "2026-06-17: The timeline manual-run package now asks testers to observe source=timeline receiver context inside timeline-share-channel, and scripts/check-devtools-readiness.mjs requires the X product/QA docs.", - "2026-06-17: Y iteration added harness/viral-attribution-events-product-brief.md and harness/viral-attribution-events-checklist.md to define minimal viral attribution events, privacy field boundaries, CloudBase best-effort behavior, and manual payload evidence requirements.", - "2026-06-17: Added utils/viral-attribution.js so from=share detail entries can record best-effort share_detail_landing, share_detail_loaded, share_detail_blocked, share_confirm_success, share_comment_success, share_relay_intent, and share_relay_success events with a strict whitelist and local fallback.", - "2026-06-17: Detail share paths created after a low-risk from=share receiver conversion now append share_id, parent_share_id, and share_depth while preserving from=share, source=receiver, and receiverAction=confirm/comment.", - "2026-06-17: Review follow-up made from=share timeline re-shares append the same relay attribution params to onShareTimeline query and record a timeline relay event; receiverConversion relay events now include conversion_action=confirm/comment derived from receiverAction.", - "2026-06-17: cloudfunctions/posts/index.js now exposes recordViralAttribution and writes sanitized events to viral_attribution_events with user_id_hash instead of raw openid; README, PROJECT_SUMMARY, and AGENTS.md document the new collection and privacy boundary.", - "2026-06-17: scripts/check-viral-attribution.mjs is wired into scripts/check-viral-candidate.mjs and scripts/check-devtools-readiness.mjs; viral journey manual evidence now requires relay attribution params in inspectable receiver confirm/comment share payloads without claiming DevTools or real-device UI passed.", - "2026-06-22: Multi-agent optimization round 2 added detail trust/resolve busy guards, button loading/disabled states, toastable error handling, and local duplicate trust-action rechecks around the async post lookup; scripts/check-detail-action-guards.mjs is wired into readiness and round 2 reviewers voted PASS.", - "2026-06-22: Multi-agent optimization round 3 moved local resolve permission enforcement into utils/store.js, allowing only owner/admin resolution and ignoring stale presentation isMine fields; scripts/check-store-permission-guards.mjs covers non-owner rejection, owner success, and admin success, and round 3 received at least two PASS votes.", - "2026-06-22: Multi-agent optimization round 5 moved CloudBase listComments ordering before MAX_COMMENTS_PER_POST limit with orderBy('createdAt', 'desc'), added scripts/check-cloud-comment-order.mjs, wired it into readiness, and received PASS votes from mini program, JS, and architecture reviewers.", - "2026-07-10: Removed the stale empty hero-grid child after the current dirty style refresh deleted its absolute-position CSS; this child had become a normal stack item and added an unintended 20rpx gap. DevTools then rendered the detail hero with normal top spacing, and readiness now rejects reintroducing the stale node." - ], - "notes": "评论与信任动作都涉及本地 storage 和 CloudBase 两条路径;当前代码实现已推进,仍需要 WeChat DevTools 手动验证后才能标记 passing。2026-05-21 详情头部已补充发布者头像/名称与右侧地址时间同排展示。当前会话转向发布图片云存储修复,避免同时存在多个 in_progress 功能。 2026-06-12 C 组详情迭代新增 TrustInsight 信任判断面板和评论头部入口;尚未在 WeChat DevTools/真机验证窄屏布局、信任动作刷新、游客登录评论引导和云端评论路径。 第二轮 C 组已收敛 TrustInsight 谨慎文案和冲突优先级自动检查;2026-06-16 组合候选同时保留发布成功扩散计划和普通详情分享提示,并新增接收侧转化提示,但仍未执行 DevTools/真机视觉、分享面板、接收路径、窄屏密度、信任动作刷新和云端评论验证。 2026-06-16 评论成功后接力提示已加入组合候选:只在评论提交成功后显示,active 低风险任务可接力转发,stale/高举报/resolved/expired/hidden 不鼓励公开扩散。传播闭环候选进一步让普通分享面板在发布成功、分享接收和评论接力状态下隐藏,降低同屏传播 CTA 竞争;确认成功后接力提示进一步覆盖 confirm 高意图时刻,且 stale/report/高风险状态不鼓励公开扩散。接收者转化迭代进一步覆盖 from=share 用户完成 confirm/comment 后的二跳接力,并用 source=receiver 让下一位接收者理解链路来源;普通详情入口、stale/report/高风险和关闭态不鼓励公开扩散。本轮接收者第一步行动入口进一步让 from=share active 且无过时/举报信号的低风险用户能直接确认或补线索,并在完成 confirm/comment 后继续优先出现 receiverConversionPrompt;普通入口、有过时/举报信号、hidden/resolved/expired 不显示鼓励性 action strip。N 组新增传播链路证据框架,把首跳 from=share、接收者行动、转化提示、二跳 source=receiver 和风险态互斥固化为自动场景模型,并提供 not_run 手测模板;这仍不代表 WeChat DevTools/真机通过。仍未执行 WeChat DevTools/真机验证五种入口视觉层级、action strip 点击、open-type share、风险态按钮、窄屏换行和真实云端评论路径。 O 组进一步把 N 组 not_run 模板升级为真实本地手测结果 gate:默认只扫描 ignored/local viral journey 结果文件,无文件时通过但明确不代表 UI passed;真实文件存在时必须匹配当前分支/commit、完整环境和唯一 required journeys,并按 passed/failed/blocked 规则校验 evidence、actual、blocker、followUp、share payload 或无法检查说明以及 overallStatus 聚合。prepare helper 只生成 ignored blocked draft 或 dry-run 输出,不声称 passed。P 组新增 no-side-effect 手测启动包,把 DevTools port/smoke blocker、evidence dry-run/check 和五条 journey 下一步收敛到单一命令;当前 9420 仍是环境 blocker,不是 UI failed 或 UI passed。Q 组新增显式 blocked evidence capture 命令,把当前 port/smoke blocker 写入 ignored local JSON 并自动跑 O checker;capture 文件仍是 blocked evidence,不是 UI passed,且默认 readiness 不执行写文件 capture。R 组把接收者完成确认/评论后的二跳提示从泛化“继续接力”升级为目标化三行信息,明确推荐转给谁、为什么可信和下一位先看什么;分类文案已对齐当前 lost_found/help_needed/street_update/check_in 枚举,风险和关闭态仍不显示鼓励性 target rows。S 组进一步在低风险二跳 path 上加入 receiverAction=confirm/comment,并让下一位接收者区分上一位刚确认或刚补线索;缺失/未知 action、非 receiver source 和风险/关闭态继续安全回退。仍未执行 WeChat DevTools/真机真实链路验证。T 组在低风险 from=share 接收者完成 confirm/comment 后新增一条可转述短理由,放在目标化三行信息与继续接力按钮之间,confirm/comment 文案不同且不新增分享 query;风险、关闭和弱 stale/report 仍不显示鼓励性理由。仍未执行 WeChat DevTools/真机真实链路验证。U 组进一步在低风险 from=share 接收者完成 confirm/comment 后增加 2-3 个“适合转给”的泛化场景建议,补足 T 组 shareReason 只解决“怎么说”的短板;它不读取联系人/微信群、不新增 targeting query,风险、关闭和弱 stale/report 仍不显示鼓励性场景建议。V 组新增微信原生分享到朋友圈渠道:只在低风险 active 详情页菜单中暴露 shareTimeline,timeline query 复用 from=share 并用 source=timeline/shareChannel=timeline 区分渠道;风险、关闭、弱 stale/report 和未知任务不开放朋友圈菜单且 payload 文案谨慎。W 组把真实朋友圈渠道纳入 manual evidence schema,新增 timeline-share-channel 与 timeline-risk-gating 两条 required journey,run package 和 blocked capture 都改为七条;这仍只是证据结构与阻塞记录能力,尚未产生真实 DevTools/真机 timeline passed evidence。X 组进一步让 source=timeline 落地页不再只是普通转发语境:低风险 active 朋友圈访客会看到“朋友圈看到”的附近任务核对说明,先看状态/评论,再确认或补线索;弱 stale/report、风险和关闭态仍优先谨慎。" - }, - { - "id": "admin-001", - "priority": 4, - "area": "admin", - "title": "轻量管理与风险内容处理", - "user_visible_behavior": "管理员可以查看 reported/stale/hidden 等风险任务,执行隐藏或关闭操作;普通用户不应看到管理能力。", - "status": "not_started", - "verification": [ - "以普通用户打开管理 tab,确认没有管理员能力或被引导。", - "以管理员身份打开管理 tab,确认列表、搜索和风险筛选可用。", - "对 reported 任务执行隐藏,确认普通地图列表不显示。", - "对任务执行关闭,确认详情状态同步为 resolved。", - "验证 admins 集合缺失时不会导致小程序崩溃。" - ], - "evidence": [ - "2026-06-13 admin iteration branch: Added product and design briefs for risk handling, focused on risk explanation, suggested next actions, and misoperation prevention.", - "2026-06-13 admin iteration branch: Extracted admin review presentation logic into pages/admin/admin-review.js with risk reason text, suggested action text, filter counts, stats, search matching, and busy action state.", - "2026-06-13 admin iteration branch: Updated admin task cards to show 处理原因 and 建议动作, disabled busy action buttons during hide/resolve, added task title and ID to confirmation modals, and showed success/failure toasts.", - "2026-06-13 admin iteration branch: node --check passed for pages/admin/admin.js and pages/admin/admin-review.js.", - "2026-06-13 admin iteration branch: node scripts/check-admin-review.mjs passed, output: Admin review helper checks passed. Node emitted the existing MODULE_TYPELESS_PACKAGE_JSON warning for ESM files.", - "2026-06-13 admin iteration branch: node scripts/check-json.mjs passed, output: Checked 11 JSON files.", - "2026-06-13 admin iteration branch: node harness/check-harness.mjs passed, output: Harness OK: 6 features checked.", - "2026-06-13 admin iteration branch: git diff --check passed with no output.", - "2026-06-13 admin iteration branch: WeChat DevTools local wcc compiled all WXML files with exit code 0.", - "2026-06-13 admin iteration branch: WeChat DevTools local wcsc -lc compiled all WXSS files with exit code 0.", - "2026-06-13 admin iteration branch: bash harness/init.sh passed; npm install reported up to date, then JSON and harness checks passed.", - "2026-06-15: After user-reported getMyRole failure, added formatAdminRoleError so wx-server-sdk missing dependency and undeployed cloud function failures show short actionable admin-check messages instead of raw cloud.callFunction stack traces.", - "2026-06-15: Added scripts/check-admin-auth-errors.mjs and wired it into scripts/check-devtools-readiness.mjs so npm run check guards admin auth error formatting.", - "2026-06-22: Multi-agent optimization round 1 hardened refreshAdminRole so missing cloud runtime, getMyRole errors, missing admins collection, malformed responses, and non-admin responses preserve guest state while clearing stale admin role; only ok === true and role === 'admin' upgrades to admin. scripts/check-admin-auth-errors.mjs now covers these cases and round 1 reviewers voted PASS.", - "2026-06-22: Performance round 5 replaced repeated admin review filter/stat full-list scans with one-pass buildAdminSummary, kept filter/stat semantics, strengthened scripts/check-admin-review.mjs after Raman caught weak guard coverage, wired the guard into readiness, and re-reviewers Aristotle, Curie, and Bohr voted PASS.", - "2026-07-10: WeChat DevTools direct UI smoke rendered the administrator summary, feedback section, filters, and task queue after asynchronous data load. No hide, resolve, or other destructive action was executed." - ], - "notes": "管理员身份依赖 getMyRole 云函数和本地 fallback。本实验分支已补充风险解释、建议动作、处置中防重复点击和确认弹窗信息;2026-06-15 已把云函数缺少 wx-server-sdk 或未部署的错误收敛为短提示和处理步骤。但真实管理员通过仍需要在 CloudBase 中重新上传部署 getMyRole 云函数并创建 admins 记录,且尚未在 WeChat DevTools 中以普通用户/管理员身份手动验证,因此不标记 passing。" - }, - { - "id": "profile-activity-001", - "priority": 5, - "area": "me", - "title": "我的、我的发布、动态与反馈入口", - "user_visible_behavior": "用户可以查看个人状态、自己的发布、活动动态,并提交反馈。", - "status": "not_started", - "verification": [ - "打开我的 tab,确认登录状态、管理员状态和入口展示正确。", - "进入我的发布,确认只展示当前用户相关任务。", - "进入动态页,确认活动列表与状态文案正确。", - "进入反馈页,提交各类型反馈并确认成功或失败提示。", - "重启小程序后确认本地状态没有异常丢失。" - ], - "evidence": [ - "2026-06-13 profile iteration branch: Added product and design briefs for turning the profile page into a status panel with next action and recent activity summary.", - "2026-06-13 profile iteration branch: Added pages/me/me-state.js to build stats, link summaries, nextAction, and recentActivities from posts, reactions, and user profile state.", - "2026-06-13 profile iteration branch: Updated pages/me to show a next-action card and up to two recent activities on the profile home page, while keeping existing profile, feedback, my-posts, and activities flows.", - "2026-06-13 profile iteration branch: node --check passed for pages/me/me.js and pages/me/me-state.js.", - "2026-06-13 profile iteration branch: node scripts/check-me-state.mjs passed, output: Me state checks passed. Node emitted the existing MODULE_TYPELESS_PACKAGE_JSON warning for ESM files.", - "2026-06-13 profile iteration branch: node scripts/check-json.mjs passed, output: Checked 11 JSON files.", - "2026-06-13 profile iteration branch: node harness/check-harness.mjs passed, output: Harness OK: 6 features checked.", - "2026-06-13 profile iteration branch: git diff --check passed with no output.", - "2026-06-13 profile iteration branch: WeChat DevTools local wcc compiled all WXML files with exit code 0.", - "2026-06-13 profile iteration branch: WeChat DevTools local wcsc -lc compiled all WXSS files with exit code 0.", - "2026-06-13 profile iteration branch: bash harness/init.sh passed; npm install reported up to date, then JSON and harness checks passed.", - "2026-07-09: Removed the stale profile-grid decorative node from pages/me/me.wxml after the current local main dirty style refresh deleted its absolute-position CSS; scripts/check-devtools-readiness.mjs now rejects reintroducing it as a normal profile grid child.", - "2026-07-10: WeChat DevTools direct UI smoke rendered the profile header, administrator state, stats, next action, and profile links without the former profile-grid displacement. Login/profile mutation and feedback submission were not exercised." - ], - "notes": "这组页面是辅助面,优先级低于核心地图/发布/详情闭环。本实验分支已补充个人中心下一步建议和最近动态摘要,但尚未在 WeChat DevTools 中验证登录、补资料、跳转、长文案和窄屏表现,因此不标记 passing。" - } - ] -} diff --git a/harness/hardening-design-checklist.md b/harness/hardening-design-checklist.md deleted file mode 100644 index 57caa47..0000000 --- a/harness/hardening-design-checklist.md +++ /dev/null @@ -1,114 +0,0 @@ -# G 组候选硬化设计手测清单 - -日期:2026-06-14 - -范围:仅覆盖 `codex/iter-candidate-hardening` 中发布页与详情页的视觉评测路径,不新增 UI,不判断代码实现优劣。 - -## 1. 发布页定位 / 底部按钮 - -### 表单状态 - -- [ ] 首屏进入发布页时,登录提示之后直接进入任务内容表单,不出现额外准备度卡片。 -- [ ] 标题、详情、分类、位置、地点、有效期逐步补齐时,底部标题、说明和按钮文案能准确提示下一步。 -- [ ] 游客态下登录提示和底部主动作之间没有互相矛盾的引导。 - -### 定位确认 - -- [ ] 初始“使用微信当前位置”状态能让用户理解发布依赖当前位置。 -- [ ] 定位中状态按钮显示 loading,按钮禁用后仍可读,不出现重复点击的视觉误导。 -- [ ] 定位成功后显示“位置已确认”,位置摘要能和地点补充输入形成清楚关系。 -- [ ] 定位失败后显示“位置未确认”和重试动作,失败态不应使用过重的错误视觉。 -- [ ] 位置卡片内标题、摘要、按钮在窄屏下不重叠,按钮宽度不挤压摘要到不可读。 -- [ ] 位置摘要为长地址或系统错误文案时,卡片高度可以增长且不遮挡后续字段。 - -### 底部按钮 - -- [ ] 底部固定操作条在页面滚动时始终贴底,背景与表单内容有清楚分隔。 -- [ ] 底部标题、说明、主按钮三者在默认屏宽下横向平衡,按钮不会被说明文案挤窄。 -- [ ] 主按钮文案随状态变化能明确下一步:登录、补字段、确认位置、等待定位、发布、提交中。 -- [ ] 禁用态、loading 态、可点击态视觉差异明确,且禁用态不被误认为可提交。 -- [ ] 页面底部最后一个表单项不会被固定操作条遮挡。 -- [ ] 带安全区设备上,底部操作条不贴住 Home 指示条,按钮可完整点击。 - -## 2. 详情页 TrustInsight / 评论入口 / 信任动作 - -### TrustInsight - -- [ ] TrustInsight 面板位于详情 hero 与评论区之间,能被理解为读完任务后的判断辅助。 -- [ ] “信任判断” eyebrow、标题、说明三层文字层级清晰,标题不应和详情页主标题竞争。 -- [ ] good / warn / danger / done 等 tone 的左侧色条差异明确,但整体仍保持街区任务的克制风格。 -- [ ] 四个信号格在默认屏宽下等宽排列,数字和标签垂直节奏一致。 -- [ ] 信号格不依赖二级说明文案;长数字不挤压相邻信号,不破坏边框圆角。 -- [ ] resolved、expired、hidden 之外的只读态显示 summary 时,不应让用户误以为还能点击信任动作。 - -### 评论入口 - -- [ ] 评论标题右侧“写评论 / 登录评论”入口可见,和评论数量形成清楚的一组信息。 -- [ ] 保留的悬浮评论按钮不遮挡评论列表正文、底部安全区或系统手势区域。 -- [ ] 标题右侧入口与悬浮入口文案一致,不制造两个不同动作的错觉。 -- [ ] 游客态显示“登录评论”时,视觉上仍像评论入口,不应误导为直接发送。 -- [ ] 任务关闭或过期时,评论暂停提示清楚,两个评论入口都不应出现。 -- [ ] 评论弹窗打开后,标题、取消、输入框、字数、发送按钮在键盘出现前后都可见。 - -### 信任动作 - -- [ ] “确认 / 过时 / 举报”三个按钮等宽排列,颜色语义清楚且不会过度警告。 -- [ ] 数字计数和动作文案在按钮内对齐稳定,计数增加后不导致按钮高度变化。 -- [ ] 用户已经执行过的动作显示“已确认 / 已过时 / 已举报”,禁用态仍可读。 -- [ ] 点击信任动作后,TrustInsight 数字和按钮状态刷新时不造成明显布局跳动。 -- [ ] 当 `post.canReact` 为 false 时,只显示确认、过时、举报汇总,不出现可点击按钮样式。 -- [ ] 信任动作与评论入口之间的视觉关系明确:一个是结构化反馈,一个是补充线索。 - -## 3. 窄屏、长文案、键盘、安全区、图片状态 - -### 窄屏 - -- [ ] 在 320px 级别窄屏下,发布表单分区、位置卡片和底部操作条不溢出。 -- [ ] 分类、有效期、失物方向按钮在窄屏下不出现文字截断到不可理解。 -- [ ] 发布页底部操作条在窄屏下按钮仍有足够点击面积,说明文案不压住按钮。 -- [ ] 详情页 hero 中标签、过期时间、标题、正文不互相遮挡。 -- [ ] 详情页发布者、地点、距离、发布时间一行在窄屏下保持可读,长地点应省略。 -- [ ] TrustInsight 四格信号在窄屏下仍不横向溢出;若真实设备过挤,应记录为后续硬化风险。 - -### 长文案 - -- [ ] 发布标题 32 字、详情 180 字填满时,计数字段位置稳定。 -- [ ] 地点补充 40 字填满时,不影响位置确认卡片和有效期区域。 -- [ ] 详情页标题、正文、评论正文为长中文连续文本时能自然换行,不与图片或元信息重叠。 -- [ ] 评论作者名、徽章、时间在长昵称或长时间文案下仍能保持行内秩序。 -- [ ] TrustInsight 标题和正文为长中文时,面板高度自然增长,不遮挡信任动作。 - -### 键盘 - -- [ ] 发布页输入标题、详情、地点时,键盘弹起后当前输入框和底部按钮不会产生误遮挡。 -- [ ] 评论弹窗 textarea focus 后,键盘不遮住发送按钮和字数统计。 -- [ ] 键盘收起后,页面滚动位置和底部固定操作条恢复正常。 -- [ ] 反复打开 / 关闭评论弹窗,不出现蒙层残留或页面无法滚动。 - -### 安全区 - -- [ ] iPhone 带 Home 指示条设备上,发布底部按钮留出 `safe-area-inset-bottom`。 -- [ ] 详情页悬浮评论按钮不贴近屏幕底边,安全区内仍可点击。 -- [ ] 评论弹窗底部在安全区设备上不被系统区域裁切。 -- [ ] 页面内容滚到底时,最后一条评论或最后一个发布字段不会被底部固定元素遮挡。 - -### 图片状态 - -- [ ] 发布页无图片时,“上传图片”按钮尺寸稳定,提示文案不被挤压。 -- [ ] 发布页 1、2、3 张图片时,缩略图网格对齐稳定,删除按钮不遮住主体信息过多。 -- [ ] 图片上传失败或被移除后,网格能回到可添加状态,不留下空洞。 -- [ ] 图片达到上限后,上传入口消失但网格仍保持规整。 -- [ ] 详情页无图片时,hero 内容间距自然,不留下空白图片区。 -- [ ] 详情页单图使用大图样式,多图使用三列缩略图,圆角和间距保持一致。 -- [ ] 图片加载慢或失败时,背景占位不影响文字阅读和页面滚动。 - -## 4. 不新增 UI 的硬化原则 - -- [ ] 只调整现有发布表单状态、定位卡片、底部按钮、TrustInsight、评论入口、信任动作的稳定性与可读性。 -- [ ] 不新增新的浮层、提示条、卡片、入口、步骤页或解释文案来解决视觉问题。 -- [ ] 优先通过间距、换行、省略、禁用态、loading 态和安全区适配完成硬化。 -- [ ] 不改变现有信息架构:发布页仍是表单加底部主动作,详情页仍是 hero、信任判断、评论。 -- [ ] 不改变现有用户任务路径:发布前补齐信息,详情页阅读后进行信任动作或评论。 -- [ ] 不引入新的颜色语义,沿用当前绿色、橙色、警告、危险、灰色状态体系。 -- [ ] 不为了单个极端文案引入一次性组件;先确认是否可用现有容器自然增长或省略处理。 -- [ ] 任何需要新增 UI 的发现只记录为后续产品问题,不在本轮候选硬化中实现。 diff --git a/harness/hardening-product-brief.md b/harness/hardening-product-brief.md deleted file mode 100644 index d619c4d..0000000 --- a/harness/hardening-product-brief.md +++ /dev/null @@ -1,68 +0,0 @@ -# G 组候选硬化产品 Brief - -日期:2026-06-14 - -依据:`harness/candidate-publish-trust-report.md`。本 worktree 未找到 `harness/iteration-evaluation-report.md`,因此本 brief 不引用 eval 报告结论。 - -## 1. F/G 候选核心产品风险排序 - -1. 发布闭环真实可用性风险最高:自动验证覆盖了发布页状态机、JSON、WXML/WXSS 编译,但尚未验证真机或 WeChat DevTools 中的定位授权、定位失败重试、提交中状态、发布成功后的详情跳转。 -2. 位置确认与主动作状态一致性风险次高:发布页位置确认和 `primaryAction` 状态机最容易卡住的地方是“字段已填但位置未确认/定位仍在等待/授权失败”时按钮文案与可点击状态是否一致。 -3. 详情信任解释的行动理解风险:TrustInsight 面板解释确认、过时、举报和评论数,但尚未验证用户在不同状态下能否正确理解“我该确认、标过时、举报还是只读”。 -4. 详情页窄屏与入口竞争风险:评论区标题右侧写评论入口与原有悬浮评论按钮共存,窄屏下可能挤压 TrustInsight、信任动作或评论入口。 -5. resolved/expired/hidden 等边界状态风险:报告已说明未验证 resolved/expired 只读状态,若信任动作仍显得可操作,会损害用户对任务状态的判断。 - -## 2. 发布闭环最该被验证的用户路径 - -优先验证一条从冷启动到完成发布的主路径: - -1. 首次进入发布页,用户未授权定位或定位尚未完成。 -2. 填写标题、详情、分类、有效期,观察定位块、底部主按钮和字数计数实时更新。 -3. 触发定位授权、定位失败重试或手动确认当前位置,确认主按钮从补字段/确认位置/等待定位切换到可发布。 -4. 点击发布,验证提交中态不会重复提交。 -5. 发布成功后进入详情页,详情页展示刚发布的标题、详情、分类、地点、有效期和初始信任数据。 -6. 返回地图或列表后,新任务可见且状态仍为 active。 - -这条路径是硬化优先级最高的原因:它同时覆盖发布状态、位置确认、状态机和发布后详情跳转,是用户产生内容的最短闭环。 - -## 3. 详情信任最该被验证的用户路径 - -优先验证一条从详情阅读到信任动作反馈的主路径: - -1. 从地图或发布成功页进入 active 任务详情。 -2. 阅读 TrustInsight,确认面板能解释当前确认数、过时数、举报数和评论数。 -3. 点击“确认仍有效”,验证确认数增加、最后确认时间刷新,且同一用户不能重复确认。 -4. 点击或尝试“标记过时”和“举报”,验证阈值前后的状态变化文案清楚,且不会误导用户认为举报等于任务已处理。 -5. 对 resolved/expired 任务进入详情,验证信任动作呈现只读或不可操作状态。 -6. 在窄屏下查看评论入口与悬浮评论按钮,确认不会遮挡 TrustInsight 或主要操作。 - -这条路径是硬化优先级最高的原因:详情页是发布后判断任务可信度的核心场景,信任解释必须和动作反馈保持一致。 - -## 4. 本轮硬化范围 - -本轮只做自动检查: - -- 语法检查:`node --check pages/publish/publish.js`、`node --check pages/publish/publish-state.js`、`node --check pages/detail/detail.js`、`node --check utils/format.js`。 -- 发布流程脚本:`node --no-warnings scripts/check-publish-flow.mjs`。 -- TrustInsight 脚本:`node harness/check-trust-insight.mjs`。 -- JSON 检查:`node scripts/check-json.mjs`。 -- harness 检查:`node harness/check-harness.mjs`。 -- 空白和补丁卫生:`git diff --check`。 -- 如本机 WeChat DevTools CLI 可用,再运行 WXML/WXSS 编译检查;若端口或 CLI 阻塞,只记录为未验证。 - -本轮不做功能: - -- 不新增发布能力、详情能力、评论能力或后台能力。 -- 不调整产品文案、视觉样式、页面结构或状态机逻辑。 -- 不修复定位授权、图片上传、云端评论、真机兼容等未验证问题。 -- 不合并 F/G 之外的新候选能力。 -- 不回退或整理他人改动。 - -## 5. 验收清单 - -- `harness/hardening-product-brief.md` 已写入并只包含本轮产品硬化范围。 -- 工作区除本 brief 外无其他文件改动。 -- 已记录 `iteration-evaluation-report.md` 缺失,结论只基于 candidate report。 -- 已明确发布闭环与详情信任两条最高优先级用户路径。 -- 已明确本轮只跑自动检查,不扩大到功能实现或产品改动。 -- 完成后运行 `git status --short`,确认实际改动范围。 diff --git a/harness/init.sh b/harness/init.sh deleted file mode 100755 index f93254f..0000000 --- a/harness/init.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -HARNESS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="$(cd "$HARNESS_DIR/.." && pwd)" - -cd "$ROOT_DIR" - -VERIFY_CMDS=( - "node scripts/check-json.mjs" - "node harness/check-harness.mjs" - "node scripts/check-map-list-blocked-summary-preflight.mjs" -) -START_NOTE="Open this repository in WeChat DevTools and use project.config.json with appid touristappid." - -echo "==> 当前目录: $PWD" -echo "==> 同步依赖" -if command -v npm >/dev/null 2>&1; then - npm ci --ignore-scripts -else - if ! node -e "const lock = require('./package-lock.json'); const deps = Object.keys((lock.packages && lock.packages[''] && lock.packages[''].dependencies) || {}).length + Object.keys((lock.packages && lock.packages[''] && lock.packages[''].devDependencies) || {}).length; if (deps > 0) process.exit(1);"; then - echo "npm not found and package-lock declares dependencies; install npm or run in WeChat DevTools environment" >&2 - exit 127 - fi - echo "npm not found; package-lock has no external dependencies, skipping install" -fi - -echo "==> 运行基础验证" -for cmd in "${VERIFY_CMDS[@]}"; do - echo "+ $cmd" - bash -lc "$cmd" -done - -echo "==> 启动方式" -echo "$START_NOTE" - -if [ "${RUN_START_COMMAND:-0}" = "1" ]; then - echo "当前项目没有可无头启动的小程序 CLI。请在 WeChat DevTools 中打开仓库并执行编译/预览。" -fi - -echo "Harness init complete." diff --git a/harness/manual-evidence-product-brief.md b/harness/manual-evidence-product-brief.md deleted file mode 100644 index a5d7ea9..0000000 --- a/harness/manual-evidence-product-brief.md +++ /dev/null @@ -1,176 +0,0 @@ -# I 组手测证据包产品简报 - -日期:2026-06-14 - -分支:`codex/iter-manual-evidence-gate` - -对象:完成 WeChat DevTools 或真机手测后,需要记录、复核、校验证据的 agent。 - -## 1. I 组目标 - -- 把手测结果从 Markdown 口头记录升级为可校验、可追溯的机器可读证据包。 -- 统一每条用户旅程的状态、环境、步骤、期望、实际和附件口径,减少“看起来测过但无法复核”的记录。 -- 明确哪些结果组合可以进入发布候选,哪些必须回修或补测。 -- 为后续自动证据完整性检查提供产品侧字段和判定规则。 - -## 2. I 组非目标 - -- I 组不代表已经完成真实 DevTools 或真机手测。 -- 不用证据包规范替代真实手测,也不把自动脚本结果写成用户旅程通过。 -- 不新增功能、不修改小程序代码、不改变 H 组 readiness 准入门槛。 -- 不要求本 brief 直接实现校验脚本;它定义后续校验器应检查什么。 - -## 3. 为什么需要机器可读手测证据包 - -当前手测记录主要依赖 Markdown 文本,适合人读,但不利于稳定判断“证据是否完整”。同一条“发布成功”可能缺少设备、基础库、定位权限、任务 id 或截图,下一位 agent 无法确认它是完整通过、局部通过、环境阻塞,还是只完成了自动检查。 - -机器可读证据包的价值是: - -- 能按旅程逐项校验必填证据,避免结论先行。 -- 能区分产品失败、环境阻塞和本轮未覆盖,避免误导发布判断。 -- 能让后续 agent 基于同一结构继续补测,而不是重读长篇叙述。 -- 能把截图、录屏、控制台日志、任务 id 和云端资源状态与具体旅程绑定。 - -## 4. 用户旅程结果状态 - -每个旅程结果必须使用以下状态之一: - -- `passed`:在 DevTools 或真机中按步骤完成验证,期望与实际一致,且证据字段完整。 -- `failed`:手测执行到目标场景,发现可复现或可判断的产品、交互、视觉、数据或云端行为问题。 -- `blocked`:由于环境、权限、工具、云端资源、DevTools CLI、基础库或设备问题,无法判断产品行为是否符合预期。 -- `not_covered`:本轮没有执行该旅程或没有构造到该分支,不能视为通过。 - -状态不得混用。若一个旅程主路径通过但失败分支未测,主路径可记录为 `passed`,失败分支必须单独记录为 `not_covered` 或 `blocked`,并影响整体发布准入结论。 - -## 5. `passed` 的必备证据 - -任何旅程要标为 `passed`,证据包至少包含: - -- 环境:测试人、测试时间、分支、提交 SHA、运行环境、DevTools 版本或真机机型、基础库版本、微信版本、网络状态、定位授权状态、登录状态、本地 Storage 状态;涉及云端时还要包含 CloudBase 环境、云函数、Storage 和数据库集合状态。 -- 步骤:入口页面、点击路径、关键输入、授权选择、网络或定位模拟方式,以及返回或跳转路径。 -- 期望:该旅程在当前候选中应出现的页面、状态、文案、数据变化、按钮状态或错误提示。 -- 实际:真实观察到的页面、状态、文案、数据变化、按钮状态或错误提示,必须能和期望逐项对齐。 -- 附件:至少一种可复核证据,例如截图、录屏、控制台日志、Network 或云开发请求截图、云函数日志、本地 Storage 截图、数据库记录截图。 -- 标识:能定位本次操作的数据 id,例如 post id、任务标题、marker id、评论 id、云函数 request id、上传文件 id 或日志 trace id;没有 id 时必须说明原因并给出替代定位信息。 -- 风险说明:如果相关失败分支没有覆盖,必须显式列为 `not_covered`,不能藏在 `passed` 备注里。 - -缺少任一关键字段时,该旅程不得标为 `passed`;应降级为 `blocked`、`not_covered`,或保留待补证。 - -## 6. 非通过状态的记录要求 - -### 6.1 `failed` - -`failed` 必须说明产品为什么不符合预期,不得只写“失败”。记录应包含: - -- 最小复现步骤和触发前置条件。 -- 期望结果与实际结果的差异。 -- 影响范围:阻断发布、影响主路径、影响分支路径、视觉缺陷、数据不一致或云端路径异常。 -- 证据:截图、录屏、日志、请求、任务 id 或数据记录。 -- 复测建议:修复后应重跑的旅程或分支。 - -如果只能看到工具超时但无法进入产品行为判断,不应写 `failed`,应写 `blocked`。 - -### 6.2 `blocked` - -`blocked` 用于说明本轮无法判断产品行为。记录应包含: - -- 阻塞类型:DevTools CLI 端口超时、WAServiceMainContext timeout、基础库异常、权限不可用、定位服务不可用、云函数未部署、Storage 未配置、网络不可达、设备不可用等。 -- 阻塞发生阶段:打开项目、编译、启动、授权、页面加载、交互、提交、上传、云端请求或日志查询。 -- 已尝试动作:重启 DevTools、清缓存、重新编译、切换网络、换设备、重试授权或检查云端资源。 -- 证据:命令输出摘要、错误截图、控制台日志、请求失败详情或资源缺失截图。 -- 下一步:恢复环境、部署资源、换设备复测或明确本候选仅覆盖本地 mock 路径。 - -`blocked` 不能被解释为产品通过,也不能用自动检查通过抵消。 - -### 6.3 `not_covered` - -`not_covered` 用于诚实说明没有测到。记录应包含: - -- 未覆盖范围:具体旅程、状态、设备、网络、权限、云端路径或失败分支。 -- 未覆盖原因:时间不足、数据难以构造、环境不具备、入口当前不存在、云端未部署或本轮范围不含该项。 -- 发布影响:是否阻断进入候选,是否允许有条件进入候选,或是否必须下一轮补测。 -- 补测条件:需要的数据、设备、权限、云端资源或操作路径。 - -`not_covered` 不等于“风险低”,也不等于“默认通过”。 - -## 7. 发布准入结论影响 - -建议证据包汇总出以下发布准入结论: - -- `candidate_ready`:所有必测主旅程均为 `passed`,关键失败分支也为 `passed`;仅允许存在对本次发布无影响且明确说明原因的低风险 `not_covered`。 -- `conditional_candidate`:所有主旅程均为 `passed`,但存在低风险分支 `not_covered` 或非阻断云端能力 `blocked`;必须写清候选限制和补测清单。 -- `needs_fix`:任一主旅程为 `failed`,或失败项影响发布、地图、详情、信任动作、数据一致性、云端提交、图片上传等用户关键路径。 -- `blocked_for_release`:任一必测主旅程为 `blocked`,导致无法判断候选是否可用;必须先恢复环境或资源后重测。 -- `insufficient_evidence`:结论声称通过,但证据字段缺失,或只有 Markdown 口头描述、自动脚本输出,没有可复核的手测附件和任务标识。 - -进入发布候选的最低要求是:必测主旅程没有 `failed` 或 `blocked`,且所有 `passed` 项证据完整。任何把 `blocked`、`not_covered` 或证据缺失项包装成通过的记录,都应判为 `insufficient_evidence`。 - -## 8. 与 H 组 readiness 的关系 - -H 组 readiness 是手测前置门槛:确认候选在进入 DevTools 或真机手测前,自动检查、必测旅程范围、环境风险和准入模板已经准备好。 - -I 组证据包是手测完成后的证据完整性检查:确认每条已执行旅程的状态、环境、步骤、期望、实际、附件和标识足够完整,能够支撑发布准入结论。 - -两者关系如下: - -- H 组回答“现在是否具备开始手测的条件”。 -- I 组回答“手测完成后,记录是否足以证明结论”。 -- H 组通过不代表用户旅程通过。 -- I 组规范通过也不代表已经真实手测;只有真实执行并留下完整证据后,旅程才能标为 `passed`。 - -## 9. 推荐机器可读证据包字段 - -后续校验器可按以下结构检查证据完整性: - -```json -{ - "schemaVersion": "manual-evidence/v1", - "candidate": { - "branch": "codex/iter-manual-evidence-gate", - "commit": "", - "testedAt": "", - "tester": "" - }, - "environment": { - "runtime": "DevTools | device | preview | debug", - "devtoolsVersion": "", - "device": "", - "baseLibraryVersion": "", - "wechatVersion": "", - "network": "", - "locationPermission": "", - "loginState": "", - "localStorageState": "", - "cloudbase": { - "envId": "", - "functions": "", - "storage": "", - "collections": "" - } - }, - "journeys": [ - { - "id": "", - "name": "", - "status": "passed | failed | blocked | not_covered", - "steps": [], - "expected": [], - "actual": [], - "evidence": { - "screenshots": [], - "recordings": [], - "logs": [], - "network": [], - "dataIds": [] - }, - "impact": "", - "nextAction": "" - } - ], - "releaseConclusion": "candidate_ready | conditional_candidate | needs_fix | blocked_for_release | insufficient_evidence" -} -``` - -## 10. I 组核心结论 - -I 组的核心结论是:发布准入不能只看“有人写了通过”,必须能被证据包复核。`passed` 需要完整环境、步骤、期望、实际和附件;`failed`、`blocked`、`not_covered` 必须诚实区分影响。I 组只定义手测结果的证据完整性规范,不声明当前候选已经完成真实 DevTools 或真机手测。 diff --git a/harness/manual-preflight-alignment-checklist.md b/harness/manual-preflight-alignment-checklist.md deleted file mode 100644 index 0a6b8c5..0000000 --- a/harness/manual-preflight-alignment-checklist.md +++ /dev/null @@ -1,94 +0,0 @@ -# 手测 Preflight 对齐 QA 清单 - -- 日期:2026-06-14 -- 分支:`codex/iter-manual-preflight-alignment` -- 角色:R 组 QA / 设计评测 agent - -范围:本清单验证 `scripts/prepare-manual-test-run.mjs` 已显式对齐 Q 组 readiness/preflight。它只证明手测准备入口会运行静态门禁和证据门禁,不代表 WeChat DevTools 或真机视觉验收已经通过。 - -## 1. 自动准备命令 - -- [ ] 运行手测准备 helper。 - - ```bash - node scripts/prepare-manual-test-run.mjs --out harness/manual-test-results.local-r-smoke.json --force - ``` - - 期望输出包含: - - - `Running readiness preflight before manual UI testing.` - - `This includes scripts/check-devtools-readiness.mjs and the map list static guard.` - - `Passing preflight does not prove DevTools or real-device visual acceptance.` - - `Publish flow checks passed.` - - `Trust insight checks passed.` - - `Candidate flow checks passed.` - - `Map list resilience checks passed.` - - `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.` - - `Manual evidence checks passed.` - - `Evidence hygiene checks passed.` - - `Manual test run prepared.` - -- [ ] 检查 Next steps。 - - 期望:第 1 步要求先阅读上方 readiness preflight 输出并包含 map list static guard 结果;随后才要求打开当前 worktree 的 WeChat DevTools UI。 - -- [ ] 确认 local JSON 仍为 ignored。 - - ```bash - git status --short --ignored - ``` - - 期望:`harness/manual-test-results.local-r-smoke.json` 只出现在 ignored 区域或不进入待提交文件;不得 staging 或 commit。 - -## 2. 后续门禁 - -- [ ] 校验本地结果文件。 - - ```bash - node scripts/check-manual-evidence.mjs harness/manual-test-results.local-r-smoke.json - ``` - - 期望:通过;由于 helper 只准备文件,journey 仍应保持未覆盖或占位状态,不自动产生真实 passed。 - -- [ ] 校验证据卫生。 - - ```bash - node scripts/check-evidence-hygiene.mjs - ``` - - 期望:通过;可提交文档不包含原始截图、录屏、本机路径、云端私密 ID、token 或 cookie。 - -- [ ] 如需摘要,先完成真实手测并填写 local JSON,再运行: - - ```bash - node scripts/create-manual-summary.mjs --input harness/manual-test-results.local-r-smoke.json --out harness/manual-test-summary.local-r-smoke.md - ``` - - 期望:摘要也是 ignored local 草稿,仍需人工脱敏复核后才能转入可提交报告。 - -## 3. 9420 Blocked 记录 - -- [ ] 如果 DevTools service port 9420 仍 blocked,真实 UI journey 写 `blocked` 或 `not_covered`。 -- [ ] 降级记录应写清:静态 preflight 已通过、DevTools/真机视觉未执行、端口状态摘要、下一步人工恢复动作。 -- [ ] 不要重复运行会改变本机 DevTools 状态的 quit/open/kill/cache/config 操作;优先引用 M/N/O 组已有诊断,或由有 UI 权限的执行者人工恢复。 - -## 4. 不能替代的真实验证 - -- [ ] 地图列表长标题、长正文、带图/无图、底部统计、safe area、地图原生层遮挡、列表滚动和详情入口点击仍需 DevTools 或真机验证。 -- [ ] 发布状态、定位拒绝/重试、图片上传失败回滚、发布后详情跳转、详情 TrustInsight 和评论入口仍需真实操作。 -- [ ] helper 成功只能写作 `manual run prepared` 或 `static gates passed`;不能写作 `UI passed`、`视觉已通过` 或 `真机通过`。 - -## 5. 清理 - -- [ ] 验证完成后清理 smoke local 文件。 - - ```bash - rm -f harness/manual-test-results.local-r-smoke.json harness/manual-test-summary.local-r-smoke.md - ``` - -- [ ] 提交前确认工作区只包含 R 组文档、脚本和 harness 记录。 - - ```bash - git status --short - git diff --check - ``` diff --git a/harness/manual-preflight-alignment-product-brief.md b/harness/manual-preflight-alignment-product-brief.md deleted file mode 100644 index d347cc1..0000000 --- a/harness/manual-preflight-alignment-product-brief.md +++ /dev/null @@ -1,56 +0,0 @@ -# 手测 Preflight 对齐产品 Brief - -- 日期:2026-06-14 -- 分支:`codex/iter-manual-preflight-alignment` -- 角色:R 组产品 agent -- 对应 feature:`map-feed-001` - -## 问题 - -Q 组已经把地图列表 static guard 接入 `scripts/check-devtools-readiness.mjs`。但是实际执行手测的人通常不会逐条回忆所有历史分支,而是从 `scripts/prepare-manual-test-run.mjs` 生成本地结果文件并开始 DevTools 或真机操作。如果这个入口没有显式说明它正在运行 Q 组 readiness,就容易出现两个误解:执行者不知道地图列表 guard 已经被跑过,或把 helper 成功误读成真实视觉验收通过。 - -## 用户价值 - -真实用户价值来自后续 DevTools 或真机手测,而不是 helper 本身。R 组的价值是让手测准备入口更可靠:执行者只要从 helper 开始,就能先看到 readiness、地图列表 static guard、manual evidence 和 evidence hygiene 的结果,再进入 UI 手测。这降低了漏跑 preflight、跳过证据门禁和误写 passed 的概率。 - -## 范围内 - -- `scripts/prepare-manual-test-run.mjs` 在运行 gate 前明确输出本轮 preflight 的边界。 -- Next steps 先提醒确认上方 preflight 输出,尤其是 map list static guard,再打开 WeChat DevTools。 -- 补充 R 组产品和 QA 文档,说明 helper 成功只代表准备完成,不代表 DevTools 或真机视觉通过。 -- 继续使用 ignored 的 `harness/manual-test-results.local*.json` 和 `harness/manual-evidence-artifacts/`,不把原始证据提交进仓库。 - -## 非目标 - -- 不修改地图页业务逻辑、WXML 或 WXSS。 -- 不新增用户可见功能,也不改变已有手测旅程定义。 -- 不恢复 DevTools service port 9420,不执行 quit/open/kill/cache/config 操作。 -- 不把 local JSON 生成、readiness 通过或 static guard 通过写成真实 UI passed。 -- 不替代后续 manual evidence、evidence hygiene 和 sanitized summary 收尾。 - -## 与 P/Q/K/L 的关系 - -- P 组提供地图列表 WXML/WXSS static guard。 -- Q 组把 P 的 guard 接入 readiness/preflight。 -- K 组提供手测准备 helper,生成 ignored local 结果文件并串联门禁。 -- L 组把真实 local 结果生成 ignored 的脱敏摘要草稿。 -- R 组把 K 的入口与 Q 的 readiness 关系显式化,让执行者在同一个 helper 输出里看到地图列表 static guard 已经被纳入准备流程。 - -## 成功标准 - -- 运行 `node scripts/prepare-manual-test-run.mjs --out harness/manual-test-results.local-r-smoke.json --force` 时,输出先说明正在运行 manual run preflight gates。 -- 同一输出包含 `Map list resilience checks passed.` 和 `DevTools readiness checks passed. Static gates passed; DevTools and real-device visual acceptance are still required.`。 -- Next steps 第一步要求确认上方 preflight 输出通过,再进入 WeChat DevTools UI。 -- 生成的 local JSON 保持 ignored,summary 状态仍为 `not_covered`,不能自动产生 passed journey。 -- `node scripts/check-manual-evidence.mjs `、`node scripts/check-evidence-hygiene.mjs`、`bash harness/init.sh` 和 `git diff --check` 均通过。 - -## 9420 Blocked 边界 - -R 组不解决 9420 blocked。如果 helper 成功但 DevTools UI 或真机链路仍无法执行,真实 journey 仍应写 `blocked` 或 `not_covered`,不能写 `passed`。端口状态仍应引用 M/N/O 组诊断或后续人工 UI 操作结果。 - -## 评测关注点 - -- 手测入口是否比 Q 更接近实际执行路径。 -- 是否降低执行者漏看地图列表 static guard 的概率。 -- 是否继续严格区分 static gates、manual preparation 和真实 UI acceptance。 -- 是否保持改动范围小,没有触碰页面实现或本机 DevTools 状态。 diff --git a/harness/manual-runbook-checklist.md b/harness/manual-runbook-checklist.md deleted file mode 100644 index a108e7a..0000000 --- a/harness/manual-runbook-checklist.md +++ /dev/null @@ -1,181 +0,0 @@ -# 手测执行 Runbook 清单 - -本清单给实际手测执行者使用,覆盖准备、执行、收尾和常见修复。它是操作清单,不代表已经完成 WeChat DevTools 或真机手测;只有执行者按本清单完成并写入本地结果文件后,才能在报告中引用对应结论。 - -## 0. 执行原则 - -- [ ] 只把脱敏摘要写入可提交文件;原始截图、录屏、日志、云端标识和本地路径只保存在 ignored 目录或提交外部位置。 -- [ ] `passed` 只能用于真实跑过且证据足够的 journey;未执行写 `not_covered`,环境阻断写 `blocked`,发现问题写 `failed`。 -- [ ] 不把真实账号、真实地点、精确经纬度、完整云端 file id、request id、环境 id、token、cookie、绝对本地路径写进可提交文件。 -- [ ] 不修改 `harness/manual-test-results.example.json` 作为真实结果;真实结果写入 local 文件。 - -## 1. 准备阶段 - -- [ ] 确认当前仓库和分支。 - - ```bash - pwd - git branch --show-current - git rev-parse --short HEAD - git status --short - ``` - - 期望:分支为 `codex/iter-manual-runbook` 或本轮指定的被测分支;记录短 commit;工作区没有会混入手测报告的意外改动。 - -- [ ] 跑 readiness 门禁,确认进入手测前的候选状态。 - - ```bash - node --no-warnings scripts/check-devtools-readiness.mjs - ``` - - 期望:readiness 检查通过。若失败,先记录 blocker,不继续把 UI journey 标为通过。 - -- [ ] 跑 manual evidence 示例检查,确认结果模板和证据规则可用。 - - ```bash - node scripts/check-manual-evidence.mjs - ``` - - 期望:示例 JSON 通过校验;注意这只证明示例结构有效,不是手测通过证据。 - -- [ ] 跑 evidence hygiene 门禁,确认本地证据边界仍正确。 - - ```bash - node scripts/check-evidence-hygiene.mjs - ``` - - 期望:hygiene 检查通过;若失败,先修复可提交证据边界。 - -- [ ] 生成本地手测结果文件。 - - ```bash - cp harness/manual-test-results.example.json harness/manual-test-results.local.json - ``` - - 然后把 `branch`、`commit`、`testedAt`、`tester`、`environment` 和每个 journey 的状态改成真实执行记录。local 文件应保持 ignored,不提交。 - -- [ ] 准备本地原始附件目录。 - - ```bash - mkdir -p harness/manual-evidence-artifacts - git status --short --ignored - ``` - - 期望:`harness/manual-test-results.local.json` 和 `harness/manual-evidence-artifacts/` 显示为 ignored 或未提交状态;原始附件只放这里或提交外部安全位置。 - -- [ ] 打开 WeChat DevTools。 - - 打开当前 worktree 的小程序项目。 - - 记录 DevTools 版本、基础库版本、模拟器或真机型号、网络档位、定位权限初始状态、CloudBase 和本地 storage 状态。 - - 如需真机,确认预览或真机调试路径可用;若 DevTools 服务端口或预览失败,写入 `blocked`,不要写 `passed`。 - -## 2. 执行阶段 - -- [ ] 按核心旅程逐项手测,并同步填写 `harness/manual-test-results.local.json`。 - - `map-to-detail`:地图页渲染、marker 或列表进入正确详情、返回后仍可操作。 - - `publish-state-location-retry`:发布状态、定位拒绝或失败、重试、允许后的主动作变化。 - - `publish-success-to-detail`:填齐表单、发布只创建一次、成功跳转新任务详情、返回地图可见。 - - `detail-trust-insight-comments`:详情 TrustInsight、确认或 stale/report、重复操作限制、评论入口和重进详情。 - - `image-cloud-paths`:图片上传、失败提示、保存引用形态、清缓存或跨用户可见性。 - -- [ ] 每个 journey 都写清楚真实观察。 - - `steps` 保留或补充实际步骤。 - - `expected` 保留验收标准。 - - `actual` 写观察到的行为,不写“正常”这类无法复核的结论。 - - `evidence` 写脱敏后的证据索引,例如“截图 S1,本地附件目录保存,显示发布按钮未被遮挡”。 - - `risks` 写仍未覆盖或环境差异。 - - `followUp` 写失败、阻断或未覆盖项的下一步。 - -- [ ] 保存原始附件到 ignored 目录。 - - 截图、录屏、控制台日志、Network 详情、云函数日志、数据库截图等原件放入 `harness/manual-evidence-artifacts/` 或提交外部安全位置。 - - 文件名用编号和旅程名,例如 `S1-map-to-detail.png`、`V1-location-retry.mov`。 - - 可提交报告里只写附件编号和脱敏摘要,不写真实绝对路径。 - -- [ ] 控制敏感信息。 - - 真实用户只写“用户 A”“游客态”“管理员账号”等角色化称呼。 - - 真实地点只写“测试 POI”“默认中心附近”“约 200m 距离”等模糊描述。 - - 云端 file id、request id、环境 id 只写“已生成云端文件引用,原值本地留存”这类摘要。 - - 本地路径只写仓库相对目录或“本地附件目录”。 - - 日志只摘录错误类型、状态码、页面路由和脱敏 message。 - -## 3. 收尾阶段 - -- [ ] 校验本地手测结果文件。 - - ```bash - node scripts/check-manual-evidence.mjs harness/manual-test-results.local.json - ``` - - 期望:通过。若失败,根据报错补齐环境、步骤、actual、evidence、risks 或 followUp;不要为了过校验把未测 journey 写成 `passed`。 - -- [ ] 校验证据卫生。 - - ```bash - node scripts/check-evidence-hygiene.mjs - ``` - - 期望:通过。若失败,先处理敏感内容或 ignored 边界,再继续。 - -- [ ] 查看工作区和 ignored 文件。 - - ```bash - git status --short --ignored - ``` - - 期望:可提交文件只包含预期报告或清单;local 结果、原始附件、私有配置和日志不应进入待提交列表。 - -- [ ] 执行 secret scan。 - - ```bash - rg --no-ignore -n -i "(api[_-]?key|secret|token|password|passwd|pwd|private[_-]?key|session|cookie|authorization|bearer|access[_-]?token|refresh[_-]?token|client[_-]?secret|appsecret|wx[0-9a-f]{16,}|sk-[A-Za-z0-9_-]{20,}|AKIA[0-9A-Z]{16})" . - ``` - - 期望:没有新增真实敏感值。若命中的是文档中的规则说明,仍要人工确认不是实际凭据。 - -- [ ] 判断是否能把脱敏摘要带入报告。 - - 可以带入:分支、commit、执行时间、DevTools 版本、基础库版本、设备类别、网络档位、定位权限状态、journey 状态、脱敏 actual、脱敏 evidence 编号、风险和 follow-up。 - - 不可带入:原始截图或录屏、完整日志、真实用户信息、真实地点、精确经纬度、完整云端资源标识、真实本机路径、token、cookie、私有 AppID 或密钥。 - - 如果不能判断是否敏感,按敏感处理,只写“原件本地留存,提交摘要待 reviewer 判断”。 - -## 4. 常见失败和修复 - -### 4.1 local 文件误被跟踪 - -- 现象:`git status --short` 中出现 `harness/manual-test-results.local.json` 或原始附件。 -- 修复: - - 先停止暂存或提交。 - - 确认文件名是否匹配 ignored 规则:`harness/manual-test-results.local*.json` 或 `harness/manual-evidence-artifacts/`。 - - 如果已经暂存,取消暂存对应文件,再重新运行 `git status --short --ignored`。 - - 不修改 `.gitignore`,除非本轮任务明确要求;本 runbook 执行不需要改 ignore 规则。 - -### 4.2 example 被误改 - -- 现象:`git diff -- harness/manual-test-results.example.json` 出现真实手测内容,或 example journey 被改成 `passed`。 -- 修复: - - 把真实内容移到 `harness/manual-test-results.local.json`。 - - example 文件只保留示例占位和非通过状态。 - - 重新运行 `node scripts/check-manual-evidence.mjs` 和 `node scripts/check-evidence-hygiene.mjs`。 - -### 4.3 journey 写 passed 但证据不足 - -- 现象:`node scripts/check-manual-evidence.mjs ` 报 passed journey 缺 evidence、actual、steps 或具体环境信息。 -- 修复: - - 若真实执行过,补充具体 DevTools 或设备信息、实际结果和脱敏证据索引。 - - 若没有真实执行,改为 `not_covered`。 - - 若被环境阻断,改为 `blocked`,并写清 blocker、risks 和 followUp。 - - 若发现行为错误,改为 `failed`,写复现步骤、实际结果和下一步。 - -### 4.4 hygiene 发现云端 file id 或本地路径 - -- 现象:`node scripts/check-evidence-hygiene.mjs` 或 secret scan 命中具体云端资源标识、本地绝对路径、账号或日志原文。 -- 修复: - - 把原始值移到 ignored 附件或提交外部安全位置。 - - 可提交内容改成脱敏摘要,例如“云端图片引用已生成,原值本地留存”“截图 S2 本地留存”。 - - 对需要稳定追踪的对象使用别名,例如“测试任务 A”“图片文件 1”“用户 A”。 - - 重新跑 manual evidence、hygiene、`git status --short --ignored` 和 secret scan,确认没有二次泄漏。 - -## 5. 最终交接口径 - -- [ ] 明确说明哪些 journey 已真实执行,哪些未覆盖或被阻断。 -- [ ] 报告只引用 `harness/manual-test-results.local.json` 中的脱敏摘要,不引用原始附件内容。 -- [ ] 若要把摘要写入可提交报告,先完成收尾阶段全部命令,并由 reviewer 按 `harness/evidence-redaction-checklist.md` 再审一遍。 -- [ ] 不声称“已完成真实手测”或“可发布”,除非本轮执行记录、证据和 reviewer 结论都支持这个说法。 diff --git a/harness/manual-runbook-product-brief.md b/harness/manual-runbook-product-brief.md deleted file mode 100644 index dbd329f..0000000 --- a/harness/manual-runbook-product-brief.md +++ /dev/null @@ -1,140 +0,0 @@ -# K 组手测运行手册与入口 helper 产品简报 - -日期:2026-06-14 - -分支:`codex/iter-manual-runbook` - -对象:准备开始、执行后收尾、或交接 WeChat DevTools/真机手测的 agent。 - -## 1. K 组目标 - -- 把“开始一次真实手测前要准备什么、先跑哪些门禁、手测后怎么收尾”沉淀成稳定运行手册。 -- 定义一个轻量 helper 的产品需求,帮助执行者准备本地结果文件、确认当前 worktree、列出前置命令和收尾命令。 -- 降低后续 agent 漏跑 H/I/J 相关门禁、误写仓库证据、或把未完成手测说成已通过的风险。 -- 让每次手测都有清晰入口:先准备本地结果文件,再执行真实 DevTools/真机旅程,最后用门禁复核并记录结论。 - -## 2. K 组非目标 - -- K 组不代表真实 WeChat DevTools 或真机手测已经完成。 -- 不新增或修改小程序功能代码。 -- 不修改 `.gitignore`,不改变 H/I/J 已有门禁规则。 -- 不提交本地结果文件、截图、录屏、日志或其它手测附件。 -- 不替代执行者对真实页面、授权、定位、地图、发布、详情、信任动作和云端路径的人工判断。 - -## 3. 为什么需要运行手册和 helper - -H/I/J 已经分别定义了手测前 readiness、手测结果完整性、提交前证据卫生,但实际执行者仍可能在入口处漏步骤。例如没有先从 example 复制本地结果文件、在错误 worktree 打开 DevTools、只跑了 JSON 检查就开始测试,或手测后忘记用证据完整性和卫生门禁复核。 - -K 组补齐的是执行入口:把准备动作和收尾动作显式列出来,让后续 agent 在开始真实手测前就知道当前候选、当前 worktree、本地结果文件和必跑命令。它不判断旅程是否通过,只降低流程遗漏。 - -## 4. helper 应做什么 - -建议 helper 作为一个只做本地准备和提示的命令或脚本,职责保持克制: - -- 复制 `harness/manual-test-results.example.json` 到 ignored 的本地结果文件。 -- 如果目标本地结果文件已存在,提醒执行者选择复用、备份或换时间戳文件名,不应静默覆盖。 -- 明确打印当前应该在 WeChat DevTools 打开的 worktree 路径和当前分支。 -- 列出开始手测前必须跑的前置命令。 -- 列出真实手测后必须跑的收尾命令。 -- 提醒执行者:只有真实 DevTools/真机执行并补齐证据后,旅程状态才可写为 `passed`。 -- 提醒执行者:本地结果文件和原始附件默认不提交,提交前只保留脱敏摘要。 - -helper 的输出应更像 checklist,而不是测试报告。它可以帮助执行者准备环境,但不应替执行者下结论。 - -## 5. helper 不应做什么 - -- 不自动把任何旅程标记为 `passed`。 -- 不因为自动检查通过而声称真实手测通过。 -- 不提交本地结果文件。 -- 不修改 `.gitignore`。 -- 不收集真实截图、录屏、控制台日志、Network/HAR、云端日志或数据库截图。 -- 不上传附件到仓库、云存储、Issue、PR 或外部服务。 -- 不读取、脱敏或重写真实敏感附件;证据卫生应由执行者按 J 组规则处理。 -- 不把 blocked、not covered 或证据缺失包装成 release-ready。 - -## 6. 本地结果文件命名建议 - -默认推荐: - -```text -harness/manual-test-results.local.json -``` - -多轮并行或需要保留历史时,推荐带时间戳: - -```text -harness/manual-test-results.20260614-1530.local.json -``` - -命名原则: - -- 文件名必须明确包含 `local`,提示它是本机真实手测结果,不应提交。 -- 时间戳使用本地执行时间,方便多轮手测区分。 -- 不在文件名里写真实用户、设备、地理位置、CloudBase env id、request id 或其它敏感信息。 -- 若后续落地 helper,应优先生成 `manual-test-results.local.json`;发现已存在时建议切换到带时间戳文件。 - -## 7. 推荐前置命令 - -开始真实 DevTools/真机手测前,helper 应展示以下命令,并提示所有失败都需要先处理或记录为阻塞: - -```bash -pwd -git branch --show-current -git status --short -bash harness/init.sh -node --check pages/publish/publish.js -node --check pages/publish/publish-state.js -node --check pages/detail/detail.js -node --check utils/format.js -node --no-warnings scripts/check-publish-flow.mjs -node harness/check-trust-insight.mjs -node scripts/check-json.mjs -node harness/check-harness.mjs -git diff --check -``` - -如本机 WeChat DevTools `wcc`/`wcsc` 或 CLI 可用,执行者可以补充编译或预览检查;不可用时应记录为“编译未验证”或“CLI 阻塞”,不得写成通过。 - -## 8. 推荐手测后命令 - -完成真实 DevTools/真机手测并填写本地结果文件后,helper 应展示以下收尾命令: - -```bash -node scripts/check-manual-evidence.mjs harness/manual-test-results.local.json -node scripts/check-json.mjs -node harness/check-harness.mjs -git diff --check -git status --short --ignored -``` - -如果使用带时间戳结果文件,应把第一条命令里的路径替换为实际本地文件名。若 evidence hygiene gate 已落地为独立命令,也应在这里列出,并要求提交前确认本地结果文件和原始附件仍处于 ignored 或未跟踪且不提交状态。 - -## 9. 与 H/I/J 的关系 - -- H 组是开始前 readiness:回答“候选是否具备进入 DevTools/真机手测的前置条件”。 -- I 组是结果完整性:回答“手测后每条旅程的状态、环境、步骤、期望、实际和附件是否足以支撑结论”。 -- J 组是提交卫生:回答“哪些证据可以摘要化进入仓库,哪些本地结果和原始附件不能提交”。 -- K 组是执行入口和运行手册:回答“执行者从哪里开始、准备哪个本地结果文件、先跑什么、测后再跑什么”。 - -四组应串联使用:K 引导入口,H 判断是否可以开始,真实 DevTools/真机手测产生本地结果,I 校验结果完整性,J 约束提交卫生。K 不能替代 H/I/J,也不能把任何未手测状态升级为通过。 - -## 10. 成功标准 - -- 后续 agent 能按手册在正确 worktree 打开 WeChat DevTools 或真机项目。 -- 本地结果文件从 example 复制而来,结构一致,且文件名明确为 local。 -- 开始手测前的自动检查和 readiness 命令被逐项执行或明确记录为阻塞。 -- 手测后执行者能用本地结果文件跑结果完整性检查,并区分 passed、failed、blocked、not_covered。 -- 提交前不会把本地结果文件、真实截图、录屏、完整日志或敏感云端附件加入仓库。 -- 文档读者不会误以为 K 组已经完成了真实手测。 - -## 11. 风险 - -- 如果 helper 输出过于像测试报告,后续 agent 可能误把“已准备”理解成“已通过”。 -- 如果本地结果文件没有被忽略,真实手测证据可能被误提交;K 组只能提出需求,不能在本任务中修改 `.gitignore`。 -- 如果执行者在错误 worktree 打开 DevTools,手测结果会和当前分支不匹配。 -- 如果只跑自动命令不做真实 DevTools/真机旅程,发布、定位、地图、授权、键盘、安全区、云端和附件路径仍然未验证。 -- 如果原始截图或日志被当作完整性证据直接提交,会违反 J 组证据卫生边界。 - -## 12. K 组核心结论 - -K 组定义的是手测执行入口和运行手册,不是手测结果。helper 应帮助执行者复制本地结果模板、确认 worktree、列出开始前和收尾后的门禁命令,并反复提醒本地结果和原始附件不应提交。只有真实 DevTools/真机执行完成,并通过 I 组完整性和 J 组卫生复核后,相关旅程才可能被记录为 `passed`。 diff --git a/harness/manual-test-results.example.json b/harness/manual-test-results.example.json deleted file mode 100644 index 3dbfaad..0000000 --- a/harness/manual-test-results.example.json +++ /dev/null @@ -1,190 +0,0 @@ -{ - "schemaVersion": "manual-test-results.example.v1", - "exampleNotice": "This file is a fill-in example only. It is not evidence that manual WeChat DevTools or device testing has been completed.", - "branch": "codex/iter-manual-evidence-gate", - "commit": "", - "testedAt": "", - "tester": "", - "statusAllowedValues": [ - "not_covered", - "blocked", - "failed", - "passed" - ], - "environment": { - "wechatDevToolsVersion": "", - "baseLibraryVersion": "", - "device": "", - "isRealDevice": false, - "network": "", - "locationPermission": "", - "cloudBase": { - "enabled": false, - "environmentId": "", - "postsFunctionDeployed": false, - "storageReady": false, - "notes": "Example values only; replace after checking the actual test environment." - }, - "storage": { - "localStorageSeeded": false, - "localStorageClearedBeforeRun": false, - "notes": "Example values only; record whether wx local storage was cleared, seeded, or reused." - } - }, - "summary": { - "overallStatus": "not_covered", - "recommendation": "Do not treat this example as release evidence. Copy this structure into a real result file and replace every placeholder after manual execution.", - "notes": [ - "No journey in this example is marked passed because no real manual test was run for this file.", - "Use blocked only when a concrete environment or tooling issue prevents completing the journey." - ] - }, - "journeys": [ - { - "id": "map-to-detail", - "title": "地图列表或标记进入任务详情", - "status": "not_covered", - "steps": [ - "Open the project in WeChat DevTools on the tested branch and commit.", - "Compile the mini program and open the map tab.", - "Open the nearby task list or tap a visible map marker.", - "Tap a task detail entry and wait for the detail page to render." - ], - "expected": [ - "The map page renders without a blocking blank screen.", - "A selected task opens the matching detail page.", - "The detail page shows title, body, publisher, place, time, trust actions, and comments area instead of the missing-task state." - ], - "actual": "Not covered in this example. Replace with observed behavior from the manual run.", - "evidence": [], - "risks": [ - "Local-first map data and CloudBase detail lookup can diverge if fallback behavior regresses.", - "Known DevTools native map timeouts may be noisy; record whether they block the journey or are only console warnings." - ], - "followUp": "Run this journey in DevTools and at least one narrow device profile, then record the exact post id used." - }, - { - "id": "map-list-visual-smoke", - "title": "地图信息列表真实视觉 smoke", - "status": "not_covered", - "steps": [ - "Open the project in WeChat DevTools or on a real device using the tested branch and commit.", - "Compile the mini program and open the map tab with seeded or real posts that include long titles, long body text, image and no-image variants.", - "Open the map information list, scroll through several task cards, and observe the top, middle, and bottom of the drawer.", - "Tap a visible map marker, then tap a list card detail entry and wait for the matching detail page to render.", - "Repeat the visual check on at least one narrow simulator or real-device profile with safe area enabled." - ], - "expected": [ - "Long titles, long body text, category/status tags, counts, time, distance, and the detail affordance remain readable without overlap or unexpected clipping.", - "Image and no-image cards keep stable spacing, and thumbnails do not push footer metadata out of view.", - "The list drawer respects safe area and tabBar boundaries, remains scrollable, and is not obscured by the native map layer.", - "Marker selection, list selection, and detail navigation point to the same task content.", - "Any native map timeout, cover-view layering issue, or safe-area problem is recorded as observed behavior rather than inferred from static checks." - ], - "actual": "Not covered in this example. Replace with observed visual behavior for long-title, long-body, image, no-image, safe-area, native-map-layer, scroll, marker/list, and detail-link checks.", - "evidence": [], - "risks": [ - "Static WXML/WXSS guards cannot prove native map layering, cover-view rendering, image loading, or safe-area behavior.", - "Long content and image variants may only reveal clipping or overlap on specific simulator widths or real devices.", - "Marker, list, and detail data can diverge if local-first posts and CloudBase lookup fallback regress." - ], - "followUp": "Run this smoke journey manually after DevTools or real-device access is available; record tested device width, post ids, whether cards had images, and any screenshot or recording artifact only if it actually exists." - }, - { - "id": "publish-state-location-retry", - "title": "发布状态与定位失败重试", - "status": "blocked", - "steps": [ - "Open the publish tab in WeChat DevTools.", - "Fill the minimum required title, body, category, and expiry fields.", - "Trigger current-location confirmation from the location block or bottom action.", - "Deny, timeout, or otherwise fail location once, then retry and allow location." - ], - "expected": [ - "The bottom action identifies location as the remaining blocker.", - "A failed location attempt shows retry-oriented copy and does not submit the post.", - "After retry succeeds, the primary action changes to publish." - ], - "actual": "Example blocked result: DevTools CLI service port was unavailable or returned wait IDE port timeout, so the tester could not automate/open the intended test target. Replace with the real blocker, or change status after manual UI execution.", - "evidence": [ - "Example only: DevTools CLI open/preview blocked by service-port timeout; replace with actual command output or leave empty if no evidence artifact exists." - ], - "risks": [ - "Location authorization behavior differs between simulator and real devices.", - "Keyboard and safe-area behavior still need a real-device check even if DevTools passes." - ], - "followUp": "Enable DevTools service port or run the journey manually in the DevTools UI; then repeat on a real device for allow, deny, and retry paths." - }, - { - "id": "publish-success-to-detail", - "title": "发布成功后跳转任务详情", - "status": "not_covered", - "steps": [ - "Prepare CloudBase or local fallback according to the test target.", - "Fill a valid publish form with current location confirmed.", - "Submit the task.", - "Observe success feedback and the destination page." - ], - "expected": [ - "The post is created exactly once.", - "The app navigates to the created task detail page.", - "The detail page shows the new task content and non-empty publisher/location metadata." - ], - "actual": "Not covered in this example. Replace with observed behavior, created post id, and destination route.", - "evidence": [], - "risks": [ - "CloudBase post creation and local fallback may produce different persistence behavior.", - "Duplicate taps during submission can create duplicate posts if submit locking regresses." - ], - "followUp": "Record the created post id, route, and whether the run used CloudBase or local storage." - }, - { - "id": "detail-trust-insight-comments", - "title": "详情 TrustInsight 与评论入口", - "status": "not_covered", - "steps": [ - "Open an active task detail page.", - "Inspect the TrustInsight panel and segmented metrics.", - "Tap confirm, stale, or report once with an eligible user.", - "Open the comment entry, submit a valid comment, and reload the detail page." - ], - "expected": [ - "TrustInsight copy is cautious and reflects confirmation, stale, report, and comment signals.", - "Counts update after a trust action without allowing duplicate same-user action.", - "A valid comment appears in the list and remains after re-entering the detail page.", - "Resolved or expired tasks remain read-only where applicable." - ], - "actual": "Not covered in this example. Replace with state before/after each action and the comment text used for testing.", - "evidence": [], - "risks": [ - "Comment persistence can differ between CloudBase comments and local post_comments storage.", - "Narrow screens may wrap TrustInsight metrics unexpectedly." - ], - "followUp": "Run active, stale, resolved, and expired detail variants; include a narrow-screen screenshot only if it actually exists." - }, - { - "id": "image-cloud-paths", - "title": "图片上传、云端路径与跨用户可见性", - "status": "not_covered", - "steps": [ - "Deploy the posts cloud function and confirm CloudBase Storage permissions before the run.", - "Publish a task with one or more valid images.", - "Verify the saved image references use cloud:// file IDs rather than local temp paths.", - "Open the task as another user or after clearing local storage." - ], - "expected": [ - "Valid images upload before post creation finishes.", - "Post creation fails visibly if required cloud upload or cloud save fails.", - "Uploaded images are visible from the detail page outside the original local session.", - "Oversized or too many images are rejected with user-facing feedback." - ], - "actual": "Not covered in this example. Replace with observed upload result, file id pattern, and cross-user visibility result.", - "evidence": [], - "risks": [ - "CloudBase Storage permissions and undeployed cloud functions can block this journey before UI assertions are meaningful.", - "Do not record private file IDs from production-like environments unless the team agrees they are safe to share." - ], - "followUp": "Before marking passed, include deployment status, storage permission status, and whether cross-user visibility was checked on real device or DevTools." - } - ] -} diff --git a/harness/map-list-blocked-evidence-checklist.md b/harness/map-list-blocked-evidence-checklist.md deleted file mode 100644 index 8a45dab..0000000 --- a/harness/map-list-blocked-evidence-checklist.md +++ /dev/null @@ -1,396 +0,0 @@ -# 地图列表 blocked evidence 演练清单 - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-blocked-evidence` -- 角色:U 组 QA agent - -范围:本清单只定义 `map-list-visual-smoke` 在 DevTools service port `9420` blocked、真机不可用或 UI 未执行时,如何把 ignored local JSON 写成 `blocked` 或 `not_covered` 并通过 schema/hygiene。它不是产品功能验收,也不代表 WeChat DevTools、真机或地图列表 UI 已通过。 - -关键边界: - -- [ ] `blocked` 不是产品失败;它表示环境、工具、权限、设备或测试数据阻止了真实观察。 -- [ ] `passed` 只能来自真实 WeChat DevTools UI 或真机观察;static guard、readiness preflight、helper prepared、CLI blocked 日志都不能替代视觉观察。 -- [ ] 真实或演练结果只写入 ignored 的 `harness/manual-test-results.local*.json`;local JSON 默认不提交。 -- [ ] U 组只提交 blocked evidence helper、产品 brief、QA 清单和 harness 记录;不修改业务 UI 或 example JSON。 -- [ ] 如果只是 U 组 blocked evidence 演练,不要写“DevTools 已通过”“真机已通过”“视觉 smoke 已通过”。 - -## 1. 准备 - -- [ ] 确认工作目录、分支和当前提交。 - - ```bash - pwd - git branch --show-current - git rev-parse --short HEAD - git status --short - ``` - - 期望:工作目录对应 `/tmp/street-tasks-iter-worktrees/map-list-blocked-evidence`,分支为 `codex/iter-map-list-blocked-evidence`;除本清单外没有 U 组需要处理的改动。 - -- [ ] 跑基础 harness。 - - ```bash - bash harness/init.sh - ``` - - 期望:输出包含 `Checked 11 JSON files.`、`Harness OK: 6 features checked.` 和 `Harness init complete.`。 - -- [ ] 只读确认必备 journey 存在。 - - ```bash - node scripts/check-manual-evidence.mjs - ``` - - 期望:输出 `Manual evidence checks passed.`;这只证明 example schema 合法,不证明 UI 已执行。 - -- [ ] 确认证据卫生基线。 - - ```bash - node scripts/check-evidence-hygiene.mjs - ``` - - 期望:输出 `Evidence hygiene checks passed.`。 - -## 2. 生成 local JSON - -- [ ] 生成 ignored 的 blocked 本地手测结果文件。 - - ```bash - node scripts/prepare-map-list-blocked-evidence.mjs --out harness/manual-test-results.local-u-blocked.json --reason "DevTools service port 9420 blocked" --force - ``` - - 期望输出包含: - - - `Manual evidence checks passed.` - - `Evidence hygiene checks passed.` - - `Map-list blocked evidence draft created.` - - `Blocked evidence is not UI passed or failed evidence; it only records the blocker.` - -- [ ] 确认 helper 默认没有伪造通过。 - - ```bash - node --input-type=module <<'NODE' - import { readFileSync } from 'node:fs'; - - const file = 'harness/manual-test-results.local-u-blocked.json'; - const results = JSON.parse(readFileSync(file, 'utf8')); - const target = results.journeys.find((journey) => journey.id === 'map-list-visual-smoke'); - const passedCount = results.journeys.filter((journey) => journey.status === 'passed').length; - - if (!target) throw new Error('missing map-list-visual-smoke'); - if (target.status !== 'blocked') throw new Error(`expected blocked, got ${target.status}`); - if (target.evidence.length !== 0) throw new Error('default evidence must be empty'); - if (passedCount !== 0) throw new Error(`expected passed=0, got ${passedCount}`); - - console.log(`helper default OK: overall=${results.summary.overallStatus}, passed=${passedCount}, mapList=${target.status}`); - NODE - ``` - - 期望:`map-list-visual-smoke` 默认为 `blocked`、`evidence` 为空、全部 journey 的 `passed` 数量为 0。 - -## 3. 改成 blocked 的字段要求 - -适用场景:DevTools service port `9420` 超时、项目无法在 DevTools 打开、真机不可用、测试账号/数据阻塞,或 UI 因环境原因没有执行。 - -- [ ] 修改 local JSON,只改 `harness/manual-test-results.local-u-blocked.json`。 - - ```bash - node --input-type=module <<'NODE' - import { readFileSync, writeFileSync } from 'node:fs'; - - const file = 'harness/manual-test-results.local-u-blocked.json'; - const results = JSON.parse(readFileSync(file, 'utf8')); - const journey = results.journeys.find((item) => item.id === 'map-list-visual-smoke'); - - if (!journey) throw new Error('missing map-list-visual-smoke'); - - results.summary.overallStatus = 'blocked'; - results.summary.recommendation = 'DevTools or device access is blocked; do not treat map-list-visual-smoke as UI evidence.'; - results.summary.notes = [ - 'U group rehearsed blocked evidence only.', - 'No real DevTools UI or real-device visual observation was completed for map-list-visual-smoke.' - ]; - - journey.status = 'blocked'; - journey.actual = 'Blocked: WeChat DevTools service port 9420 was unavailable or UI visual smoke was not executed, so no map-list visual behavior was observed.'; - journey.evidence = [ - 'Sanitized local note: readiness/helper output may be retained locally; no screenshot, recording, or real UI artifact exists for this rehearsal.' - ]; - journey.risks = [ - 'Static WXML/WXSS guards cannot prove native map layering, safe-area behavior, image loading, list scrolling, or detail navigation.', - 'Because UI was not executed, long-title, long-body, image/no-image, marker/list/detail, and safe-area observations remain unverified.' - ]; - journey.followUp = 'Restore DevTools service port access or use a real device, then rerun map-list-visual-smoke and replace this blocked result with real observations.'; - - writeFileSync(file, `${JSON.stringify(results, null, 2)}\n`); - console.log('map-list-visual-smoke set to blocked in ignored local JSON'); - NODE - ``` - -- [ ] 字段复核。 - - - `status`:必须是 `blocked`。 - - `actual`:必须明确说明 UI 没有执行或 DevTools/真机被阻塞。 - - `evidence`:可以为空;如果填写,只能写脱敏本地摘要,不能写不存在的截图、录屏或真机观察。 - - `risks`:至少写出“静态检查不能证明真实视觉”的风险,或保留其他具体风险。 - - `followUp`:至少写下一步恢复端口、换真机、补数据或重新执行真实 smoke。 - - `summary.overallStatus`:建议写 `blocked`,避免评审误读成已覆盖。 - -- [ ] 运行正向校验。 - - ```bash - node scripts/check-manual-evidence.mjs harness/manual-test-results.local-u-blocked.json - node scripts/check-evidence-hygiene.mjs - ``` - - 期望:两条都通过;通过含义是 blocked local JSON 结构和可提交文件卫生合格,不是 UI 通过。 - -## 4. 保持 not_covered 的字段要求 - -适用场景:U 组只演练流程、没有尝试打开 DevTools/真机,或还没安排真实视觉 smoke。 - -- [ ] 如果没有具体环境 blocker,`map-list-visual-smoke` 可以保持 `not_covered`。 - - ```bash - node scripts/prepare-manual-test-run.mjs --out harness/manual-test-results.local-u-not-covered.json --force - node scripts/check-manual-evidence.mjs harness/manual-test-results.local-u-not-covered.json - ``` - -- [ ] 字段复核。 - - - `status`:保持 `not_covered`。 - - `actual`:保持或改成“U 组未执行真实视觉 smoke”;不要写观察结论。 - - `evidence`:保持空数组,除非存在真实、脱敏、可复查的本地记录。 - - `risks` / `followUp`:可以保留 example 中的真实视觉风险和后续执行建议。 - - `summary.overallStatus`:保持 `not_covered`。 - -- [ ] 口径复核:`not_covered` 表示没测,不表示失败;同样不能写 `passed`。 - -## 5. 正向校验 - -- [ ] blocked local JSON schema 通过。 - - ```bash - node scripts/check-manual-evidence.mjs harness/manual-test-results.local-u-blocked.json - ``` - - 期望:输出 `Manual evidence checks passed.`。 - -- [ ] not_covered local JSON schema 通过。 - - ```bash - node scripts/check-manual-evidence.mjs harness/manual-test-results.local-u-not-covered.json - ``` - - 期望:输出 `Manual evidence checks passed.`。 - -- [ ] evidence hygiene 通过。 - - ```bash - node scripts/check-evidence-hygiene.mjs - ``` - - 期望:输出 `Evidence hygiene checks passed.`;example JSON 中不能有任何 `passed` journey。 - -- [ ] 手动复核 summary 口径。 - - ```bash - node --input-type=module <<'NODE' - import { readFileSync } from 'node:fs'; - - for (const file of [ - 'harness/manual-test-results.local-u-blocked.json', - 'harness/manual-test-results.local-u-not-covered.json' - ]) { - const results = JSON.parse(readFileSync(file, 'utf8')); - const target = results.journeys.find((journey) => journey.id === 'map-list-visual-smoke'); - const passedCount = results.journeys.filter((journey) => journey.status === 'passed').length; - console.log(`${file}: overall=${results.summary.overallStatus}, mapList=${target.status}, passed=${passedCount}, evidence=${target.evidence.length}`); - } - NODE - ``` - - 期望:blocked 文件显示 `mapList=blocked`;not_covered 文件显示 `mapList=not_covered`;两者都不应出现 `passed`。 - -## 6. 坏样例:blocked 但 risks/followUp 为空 - -目标:确认 schema 能阻止“只有 blocked 状态、没有风险和后续动作”的空洞记录。 - -- [ ] 生成坏样例。 - - ```bash - cp harness/manual-test-results.example.json harness/manual-test-results.local-u-bad-blocked.json - node --input-type=module <<'NODE' - import { readFileSync, writeFileSync } from 'node:fs'; - - const file = 'harness/manual-test-results.local-u-bad-blocked.json'; - const results = JSON.parse(readFileSync(file, 'utf8')); - const journey = results.journeys.find((item) => item.id === 'map-list-visual-smoke'); - - journey.status = 'blocked'; - journey.actual = 'Blocked rehearsal without enough follow-up detail.'; - journey.risks = []; - journey.followUp = ''; - - writeFileSync(file, `${JSON.stringify(results, null, 2)}\n`); - NODE - node scripts/check-manual-evidence.mjs harness/manual-test-results.local-u-bad-blocked.json - ``` - -- [ ] 预期:命令失败,错误包含等价语义。 - - ```text - journey map-list-visual-smoke is blocked but both risks and followUp are empty. - ``` - -## 7. 坏样例:误写 passed 且无 evidence - -目标:确认未执行 UI 时不能靠改状态伪造通过。 - -- [ ] 生成坏样例。 - - ```bash - cp harness/manual-test-results.example.json harness/manual-test-results.local-u-bad-passed.json - node --input-type=module <<'NODE' - import { readFileSync, writeFileSync } from 'node:fs'; - - const file = 'harness/manual-test-results.local-u-bad-passed.json'; - const results = JSON.parse(readFileSync(file, 'utf8')); - const journey = results.journeys.find((item) => item.id === 'map-list-visual-smoke'); - - journey.status = 'passed'; - journey.actual = 'Incorrect: UI was not executed, but the journey was marked passed.'; - journey.evidence = []; - - writeFileSync(file, `${JSON.stringify(results, null, 2)}\n`); - NODE - node scripts/check-manual-evidence.mjs harness/manual-test-results.local-u-bad-passed.json - ``` - -- [ ] 预期:命令失败,至少包含以下错误之一。 - - ```text - journey map-list-visual-smoke is passed but evidence is empty. - journey map-list-visual-smoke is passed but environment lacks concrete DevTools or device information. - ``` - -- [ ] 手动复核:即使某个 local JSON 为了演练写了 evidence 文本,只要 evidence 不是来自真实 DevTools/真机观察,就不能把 `map-list-visual-smoke` 写成 `passed`。 - -## 8. 证据卫生 - -- [ ] 本地真实或演练结果必须匹配 ignored 路径。 - - ```bash - rg -n "harness/manual-test-results\\.local\\*\\.json|harness/manual-test-summary\\.local\\*\\.md|harness/manual-evidence-artifacts/" .gitignore - ``` - - 期望:三类 ignored 规则都存在。 - -- [ ] 原始附件只放 ignored 位置。 - - - `harness/manual-evidence-artifacts/` - - 外部安全位置 - -- [ ] 可提交文档只允许写脱敏摘要或附件编号;不要提交截图、录屏、二维码、完整 Console/Network、云函数日志、数据库截图、`cloud://` 完整 fileID、CloudBase env id、openId、手机号、cookie、token、本机绝对路径或精确经纬度。 - -- [ ] 运行证据卫生 gate。 - - ```bash - node scripts/check-evidence-hygiene.mjs - ``` - - 期望:输出 `Evidence hygiene checks passed.`。 - -## 9. local 文件 ignored - -- [ ] 检查 local JSON 没进入待提交范围。 - - ```bash - git status --short --ignored - ``` - - 期望: - - - `harness/manual-test-results.local-u-*.json` 只出现在 ignored 区域,或已经被清理。 - - `harness/manual-evidence-artifacts/` 只出现在 ignored 区域,或为空/不存在。 - - 可提交改动只包含 U 组 helper、产品/QA 文档和 harness 记录。 - -- [ ] 不使用 `git add -f` 添加 local JSON、local summary 或附件目录。 - -## 10. 错误日期扫描 - -目标:防止 U 组清单或 local 摘要误写后一日日期。 - -- [ ] 扫描 U 组清单和 local JSON。 - - ```bash - node --input-type=module <<'NODE' - import { existsSync, readFileSync } from 'node:fs'; - - const badDate = ['2026', '06', String(14 + 1).padStart(2, '0')].join('-'); - const files = [ - 'harness/map-list-blocked-evidence-checklist.md', - 'harness/manual-test-results.local-u-blocked.json', - 'harness/manual-test-results.local-u-not-covered.json', - 'harness/manual-test-results.local-u-bad-blocked.json', - 'harness/manual-test-results.local-u-bad-passed.json' - ]; - - let failed = false; - for (const file of files) { - if (!existsSync(file)) continue; - const text = readFileSync(file, 'utf8'); - if (text.includes(badDate)) { - console.error(`${file}: contains wrong run date`); - failed = true; - } - } - - if (failed) process.exit(1); - console.log('Wrong date scan passed.'); - NODE - ``` - - 期望:输出 `Wrong date scan passed.`。 - -## 11. 清理 - -- [ ] 清理 U 组演练 local JSON。 - - ```bash - rm -f harness/manual-test-results.local-u-*.json - ``` - -- [ ] 如果生成过 local summary,也一并清理。 - - ```bash - rm -f harness/manual-test-summary.local-u-*.md - ``` - -- [ ] 最终运行基础 harness。 - - ```bash - bash harness/init.sh - ``` - -- [ ] 最终检查 diff 空白。 - - ```bash - git diff --check - ``` - -- [ ] 最终检查工作树。 - - ```bash - git status --short --ignored - ``` - - 期望:待提交范围限于 U 组 helper、产品/QA 文档和 harness 记录;所有 local JSON、local summary 和附件目录均已清理或仍 ignored。 - -## 12. 最终报告口径 - -- [ ] 报告修改文件:`scripts/prepare-map-list-blocked-evidence.mjs`、`harness/map-list-blocked-evidence-product-brief.md`、`harness/map-list-blocked-evidence-checklist.md` 和 harness 记录。 -- [ ] 报告验证:`bash harness/init.sh` 和 `git diff --check` 的结果。 -- [ ] 明确说明:U 组只完成 blocked evidence helper 和演练文档;没有执行真实 WeChat DevTools/真机视觉 smoke。 -- [ ] 明确说明:`blocked` 是环境或执行阻塞,不是产品失败;`not_covered` 是未覆盖,不是通过;`passed` 必须来自真实 UI 观察。 -- [ ] 明确说明:local JSON 默认 ignored 且不提交。 diff --git a/harness/map-list-blocked-evidence-product-brief.md b/harness/map-list-blocked-evidence-product-brief.md deleted file mode 100644 index 9e725d8..0000000 --- a/harness/map-list-blocked-evidence-product-brief.md +++ /dev/null @@ -1,84 +0,0 @@ -# 地图列表视觉 Smoke Blocked Evidence 演练产品 Brief - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-blocked-evidence` -- 角色:U 组产品 agent -- 对应 feature:`map-feed-001` - -## 用户问题 - -S 组已经新增 `map-list-visual-smoke` 证据槽位,T 组已经要求手测 JSON 中必须保留且只保留一条该 journey。但当前仍缺一次明确演练:当 WeChat DevTools 9420 服务端口 blocked、DevTools UI 或真机无法执行地图列表视觉 smoke 时,执行者应该如何把这个状态写成合规的 ignored local JSON 草稿。 - -如果没有这层演练,后续 agent 可能出现两类误判:一是因为无法执行 UI 就不填 `map-list-visual-smoke`;二是把静态检查、helper 成功或 example 模板通过误写成视觉 `passed`。两者都会让评审无法判断真实视觉 smoke 到底是未执行、环境阻塞,还是已经观察通过。 - -## 产品假设 - -把 `map-list-visual-smoke` 的 blocked 写法提前产品化,可以让无法执行真实 UI 的情况也留下可校验、可评审、不会误报 passed 的证据草稿。blocked evidence 合规通过只说明“环境阻塞被正确记录”,不代表产品失败,也不代表地图列表 UI passed。 - -## 范围 - -- 定义 `map-list-visual-smoke` 在当前 9420 blocked 或 DevTools 未执行时的 local JSON 填写口径。 -- 明确 `blocked`、`passed`、`failed`、`not_covered` 的边界,重点区分 `blocked` 与 `passed`。 -- 说明 blocked 草稿至少要写清环境、阻塞阶段、实际结果、影响范围、证据引用、风险和下一步。 -- 约束证据来源只能是实际观察到的端口、CLI、DevTools UI 或真机状态摘要;不能用自动静态 guard 代替视觉证据。 -- 配套 helper 和 QA 清单只服务于生成、复核 ignored local blocked 草稿;不修改业务 UI 或把 blocked 升级成 passed。 - -## 非目标 - -- 不执行 WeChat DevTools UI、CLI open/preview、真机调试或任何端口恢复动作。 -- 不声明 WeChat DevTools、真机、地图列表视觉 smoke 或完整用户旅程已经通过。 -- 不提交 `harness/manual-test-results.local*.json`;helper 生成的 blocked 草稿只允许留在 ignored local 文件中。 -- 不改 `harness/manual-test-results.example.json`、`scripts/check-manual-evidence.mjs`、readiness preflight、WXML、WXSS 或业务 JS。 -- 不把 9420 blocked 写成产品缺陷;只有进入真实用户旅程并观察到用户可见问题时,才应写 `failed`。 - -## Blocked/Passed 边界 - -- `blocked`:环境或工具阻止执行真实地图列表视觉 smoke,例如 9420 未监听、CLI timeout、DevTools 无法打开项目、真机不可用、测试数据无法准备、原生 map 层阻断操作。此时可以写 blocked evidence,说明入口不可用和发布判断仍不可得。 -- `passed`:只能来自 DevTools UI 或真机中的真实观察,且必须包含具体环境、步骤、实际结果和可复核证据。静态脚本、模板校验、helper 生成 local JSON、readiness 通过都不能产生 `passed`。 -- `failed`:DevTools UI 或真机可运行,且已经观察到重叠、遮挡、无法滚动、点击无效、详情跳转错误等用户可见问题。 -- `not_covered`:本轮没有执行也没有具体 blocker;如果已经有 9420/DevTools 阻塞证据,应优先写 `blocked`,不要把环境阻塞淡化成未覆盖。 - -核心口径:blocked evidence 通过只说明记录方式合规;它既不是产品失败证据,也不是 UI 通过证据。 - -## 与 S/T/K/L/M/N/O/P/Q/R 的关系 - -| 组别 | 已提供能力 | U 组如何承接 | -| --- | --- | --- | -| S | 新增 `map-list-visual-smoke` 真实视觉 smoke 证据槽位 | 规定无法执行该 smoke 时如何写 blocked,而不是留空或写 passed | -| T | 将 `map-list-visual-smoke` 升级为必备 journey gate | 确保必备 journey 在 blocked 场景下也有合规内容,不绕过 gate | -| K | 提供手测准备 helper 和 ignored local JSON 入口 | U 组新增 blocked helper,直接生成合规的 blocked local 草稿 | -| L | 定义 local 结果的脱敏摘要草稿 | U 组 blocked 草稿后续可被摘要化,但摘要不能升级为视觉通过 | -| M | 记录 DevTools smoke access blocked 的端口/CLI 口径 | U 组可引用 M 类诊断作为 blocked evidence 来源 | -| N | 定义受控恢复和恢复失败的记录边界 | 若恢复失败,U 组要求把恢复后仍 blocked 的事实写入 `actual`、`risks` 和 `followUp` | -| O | 拆分 CLI 可用、进程声明、端口监听和 smoke ready | U 组沿用 O 的分层,避免把 CLI 存在或进程声明误写成 smoke ready | -| P | 地图列表 WXML/WXSS static guard | U 组强调 static guard 只能降低结构风险,不能替代 blocked 或 passed UI evidence | -| Q | 将地图列表 static guard 接入 readiness preflight | U 组要求 readiness 通过时仍保留 `map-list-visual-smoke=blocked` 的真实环境结论 | -| R | 让手测 helper 显式展示 readiness 和 static guard | U 组补齐 helper 之后、真实 UI 之前的 blocked local JSON 写法 | - -## 证据字段要求 - -在 ignored 的 `harness/manual-test-results.local*.json` 中,blocked 草稿应至少满足以下要求: - -- 顶层字段:保留 `branch`、`commit`、`testedAt`、`tester`、`environment`、`summary`、`journeys`。 -- `environment`:记录 DevTools 版本或状态、基础库版本或未知原因、设备/模拟器状态、网络、定位权限、CloudBase/后端状态;若某项因 blocked 无法确认,写明 `unknown because ...`,不要留占位符。 -- `summary.overallStatus`:如果地图列表视觉 smoke 是本轮发布判断 blocker,应写 `blocked`;推荐语说明真实 UI 未执行。 -- `journeys[].id`:必须保留且只保留一条 `map-list-visual-smoke`。 -- `map-list-visual-smoke.status`:当前 9420/DevTools 未执行场景应写 `blocked`,不能写 `passed`。 -- `steps` 和 `expected`:保留真实视觉 smoke 原始步骤和期望,便于端口恢复后继续执行。 -- `actual`:写具体阻塞事实、阶段和影响,例如服务端口未监听、CLI timeout、DevTools UI 未能打开目标 worktree,因此长标题、长正文、图片、安全区、原生 map 层、滚动和详情链路均未观察。 -- `evidence`:只放实际存在且已脱敏的命令摘要、截图/录屏引用或人工 UI 观察摘要;没有可提交 artifact 时可以为空,但不能填 example 文案或虚构截图。 -- `risks`:列出未能判断的用户风险,例如 safe area、原生 map 层、图片加载、抽屉滚动、marker/list/detail 数据一致性。 -- `followUp`:写清下一步恢复条件,例如人工开启 DevTools 服务端口、换端口/换机、重新运行 readiness/helper,再执行真实地图列表视觉 smoke。 - -建议 blocked `actual` 使用这种结构: - -```text -DevTools service port 9420 was not reachable, so the tester could not open or automate the target worktree for map-list-visual-smoke. No DevTools UI or real-device visual observation was executed. Static readiness and map-list guard results remain preflight only; long-title, long-body, image/no-image, safe-area, native-map-layer, scroll, marker/list, and detail-link checks are still unverified. -``` - -## 下一步 - -- QA/执行者在准备真实手测时,可以用 `scripts/prepare-map-list-blocked-evidence.mjs` 生成 ignored blocked local JSON;若真实执行完成,再把 `map-list-visual-smoke` 改成 `passed/failed` 并补齐真实观察证据。 -- 若仍是 9420 blocked,引用 M/N/O 类端口诊断摘要,跑 `node scripts/check-manual-evidence.mjs ` 确认结构合规,再用 L 组方式生成脱敏摘要草稿。 -- 若端口或真机入口恢复,重新执行 S 组定义的地图列表视觉 smoke;只有真实观察完成并补齐 evidence 后,才允许写 `passed`。 -- 评审 blocked 草稿时,应只接受“阻塞记录合规”这个结论,不应据此判断地图列表产品体验通过或失败。 diff --git a/harness/map-list-blocked-summary-checklist.md b/harness/map-list-blocked-summary-checklist.md deleted file mode 100644 index bfee2bf..0000000 --- a/harness/map-list-blocked-summary-checklist.md +++ /dev/null @@ -1,136 +0,0 @@ -# 地图列表 blocked 摘要 Wrapper 设计 / QA 检查清单 - -范围:用于后续 wrapper 把 U 组生成的 `harness/manual-test-results.local*.json` blocked 草稿,与 L 组 `scripts/create-manual-summary.mjs` 生成的脱敏 `harness/manual-test-summary.local*.md` 串起来。本文只定义设计与 QA 复核口径,不代表 WeChat DevTools 或真机地图列表视觉 smoke 已经执行。 - -工作目录:`/tmp/street-tasks-iter-worktrees/map-list-blocked-summary` - -重要边界: - -- [ ] 只处理 ignored 的本地结果和本地摘要:`harness/manual-test-results.local*.json`、`harness/manual-test-summary.local*.md`。 -- [ ] 不修改 `harness/manual-test-results.example.json`,不提交 local JSON/local MD,不提交原始截图、录屏、日志或云端证据。 -- [ ] wrapper 成功只说明 blocked 草稿可被脱敏摘要化;不能把地图列表视觉 smoke 解释为 `passed`。 -- [ ] 如果 DevTools UI 或真机没有真实执行,最终口径必须保持 `blocked` 或未验证,不得用自动脚本通过替代用户可见验收。 - -## 1. 推荐命令链路 - -- [ ] 先生成 ignored blocked local JSON。 - - ```bash - node scripts/prepare-map-list-blocked-evidence.mjs \ - --reason "DevTools service port blocked; map-list visual smoke was not executed." \ - --out harness/manual-test-results.local-map-list-blocked.json \ - --force - ``` - - 期望:脚本运行 `check-manual-evidence` 和 `check-evidence-hygiene`;输出说明 blocked evidence 不是 UI passed 或 failed evidence。 - -- [ ] 再从同一个 local JSON 生成 ignored local summary。 - - ```bash - node scripts/create-manual-summary.mjs \ - --input harness/manual-test-results.local-map-list-blocked.json \ - --out harness/manual-test-summary.local-map-list-blocked.md - ``` - - 期望:摘要生成成功,输出路径匹配 `harness/manual-test-summary.local*.md`,文件仍为 ignored。 - -- [ ] wrapper 应把两步串成一个安全路径,但仍保留这两个脚本的原有门禁:local JSON 路径门禁、local summary 路径门禁、manual evidence 校验、evidence hygiene 校验、summary 写入前敏感内容扫描。 - -## 2. 状态不变量 - -生成摘要前后都必须复核以下不变量。 - -- [ ] `summary.overallStatus` 必须保持 `blocked`。 -- [ ] `journeys[].id === "map-list-visual-smoke"` 的 journey 必须存在且只存在一条。 -- [ ] `map-list-visual-smoke.status` 必须保持 `blocked`。 -- [ ] 全部 journey 中 `status === "passed"` 的数量必须为 `0`。 -- [ ] `map-list-visual-smoke.evidence` 的证据数量必须为 `0`。 -- [ ] 摘要表格中 `overallStatus` 显示为 `blocked`,`map-list-visual-smoke` 行的 `status` 显示为 `blocked`,该行 `evidenceCount` 显示为 `0`。 - -建议用一次性 Node 断言复核 JSON: - -```bash -node -e "const fs=require('fs');const r=JSON.parse(fs.readFileSync('harness/manual-test-results.local-map-list-blocked.json','utf8'));const targets=(r.journeys||[]).filter(j=>j&&j.id==='map-list-visual-smoke');const passed=(r.journeys||[]).filter(j=>j&&j.status==='passed').length;const ev=Array.isArray(targets[0]?.evidence)?targets[0].evidence.length:(targets[0]?.evidence?1:0);if(r.summary?.overallStatus!=='blocked'||targets.length!==1||targets[0].status!=='blocked'||passed!==0||ev!==0){process.exit(1)}" -``` - -建议用文本检查复核 summary: - -```bash -rg -n "overallStatus|map-list-visual-smoke|evidenceCount|\\| 0 \\|" harness/manual-test-summary.local-map-list-blocked.md -``` - -## 3. 摘要脱敏要求 - -summary 只能承载可读、脱敏、最小必要的信息。 - -- [ ] 可以写:分支名、短 commit、测试时间摘要、执行者占位、环境类别、journey 状态、actual 的 blocked 结论、风险摘要、follow-up、证据数量。 -- [ ] 只能写证据数量,例如 `evidenceCount=0`;不要把 raw evidence 字符串、截图文件名或截图原路径复制进 summary。 -- [ ] 不得出现本机绝对路径,包括 `/Users/...`、`/private/tmp/...`、DevTools 缓存路径、截图原始保存路径。 -- [ ] 不得出现具体 `cloud://` 资源、CloudBase env id、Storage fileID、数据库真实记录 id、request id 或 trace id。 -- [ ] 不得出现 token、cookie、Authorization/Bearer、access token、refresh token、password、private key、AppSecret。 -- [ ] 不得出现手机号、真实微信号、openid、unionid、真实账号、真实昵称、头像 URL、聊天记录或真实地址。 -- [ ] 如果需要引用附件,只能写角色化编号或数量,例如“本地无可提交证据,evidenceCount=0”,不能写截图原路径。 - -`scripts/create-manual-summary.mjs` 已在写入前扫描部分敏感模式;wrapper QA 仍要补充人工复核,避免未覆盖的真实账号、截图路径或云端标识进入摘要。 - -## 4. 负向样例 - -以下负向样例应在临时 local 文件上执行,执行后清理,不提交。 - -- [ ] 非 ignored JSON 输出路径应失败。 - - ```bash - node scripts/prepare-map-list-blocked-evidence.mjs \ - --reason "negative path check" \ - --out harness/manual-test-results.blocked.json \ - --force - ``` - - 期望:失败,错误说明输出必须匹配 `harness/manual-test-results.local*.json`。 - -- [ ] 非 ignored summary 输出路径应失败。 - - ```bash - node scripts/create-manual-summary.mjs \ - --input harness/manual-test-results.local-map-list-blocked.json \ - --out harness/manual-test-summary.md - ``` - - 期望:失败,错误说明输出必须匹配 `harness/manual-test-summary.local*.md`。 - -- [ ] 敏感内容应被 create summary 拦截。 - - 做法:复制一份 ignored local JSON,在 `actual`、`followUp` 或 `environment.notes` 中临时写入 `/Users/example/secret.png`、`cloud://example-env/path`、`Bearer example-token-123456` 或测试手机号样式内容,再运行 summary 生成。 - - 期望:`scripts/create-manual-summary.mjs` 在写入前失败,报告 prohibited sensitive content;不得产生 local summary。 - -- [ ] 不能把 blocked 改成 passed。 - - 做法:复制一份 ignored local JSON,把 `summary.overallStatus` 或 `map-list-visual-smoke.status` 临时改成 `passed`,再运行 wrapper 或第 2 节状态不变量断言。 - - 期望:wrapper 或断言失败;不得生成可被误读为通过的 summary。即使静态 readiness、manual evidence gate 或 summary 生成脚本通过,也不能把未执行的 DevTools/真机视觉 smoke 写成 `passed`。 - -## 5. 清理与 Git 卫生 - -- [ ] 负向样例和正向演练结束后清理 local JSON/local MD。 - - ```bash - rm -f harness/manual-test-results.local-map-list-blocked.json - rm -f harness/manual-test-summary.local-map-list-blocked.md - ``` - -- [ ] 如果使用了其他临时 local 文件,也一并清理,例如 `harness/manual-test-results.local-*.json`、`harness/manual-test-summary.local-*.md`。 -- [ ] 确认本轮可提交改动只包含预期 checklist 或 wrapper 代码;local evidence 不得出现在待提交列表中。 - - ```bash - git status --short --ignored - ``` - - 期望:`harness/manual-test-results.local*.json`、`harness/manual-test-summary.local*.md` 不出现在 staged 或 unstaged 待提交项;如果仍存在,只能显示为 ignored,且提交前应清理。 - -## 6. 最终报告口径 - -- [ ] 报告必须区分:blocked local JSON 已生成、脱敏 local summary 已生成、状态不变量已验证、证据卫生已验证、DevTools/真机视觉 smoke 未执行。 -- [ ] 明确写出:summary 只是便于人阅读的脱敏材料,不是 DevTools/真机 UI passed 证据。 -- [ ] 如果仍是环境 blocked,报告应说明地图列表长标题、长正文、图片/无图、安全区、原生 map 层、滚动、marker/list/detail 链路仍未被真实观察。 -- [ ] 不使用“通过验收”“视觉通过”“发布可准入”等表述,除非已有 DevTools UI 或真机真实执行证据,且 local JSON 中对应 journey 合规记录为 `passed`。 diff --git a/harness/map-list-blocked-summary-product-brief.md b/harness/map-list-blocked-summary-product-brief.md deleted file mode 100644 index 0125475..0000000 --- a/harness/map-list-blocked-summary-product-brief.md +++ /dev/null @@ -1,78 +0,0 @@ -# 地图列表视觉 Smoke Blocked Summary 产品 Brief - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-blocked-summary` -- 角色:V 组产品 agent -- 对应 feature:`map-feed-001` - -## 用户问题 - -U 组已经能把 `map-list-visual-smoke` 在 DevTools 或真机不可用时写成 ignored local JSON,并保持 `map-list-visual-smoke=blocked`、`passed=0`、`evidence=[]`。L 组也已经能从 ignored local JSON 生成 ignored local Markdown 摘要。 - -剩下的问题是:评测/评审读 JSON 时仍需要自己判断 blocked 的含义,容易把“有摘要”“helper 跑通”“JSON 合规”误读成地图列表视觉 smoke 通过。V 组要补齐产品口径,让 blocked JSON 到脱敏摘要这一步只承担“让结论更易读”的价值,不改变真实 UI 验收状态。 - -## 产品假设 - -如果把 blocked local JSON 摘要化,并在摘要边界中明确“真实视觉 smoke 未执行”,评审可以更快理解当前阻塞原因、未验证风险和下一步,而不会把摘要生成误判为 UI passed。 - -这个假设只针对评审理解效率和证据卫生,不针对地图列表体验本身。地图列表视觉 smoke 是否通过,仍只能由 WeChat DevTools UI 或真机中的真实观察决定。 - -## 范围 - -- 定义从 U 组 blocked local JSON 到 L 组脱敏 Markdown 摘要的用户价值。 -- 明确摘要应保留 `overallStatus=blocked`、`map-list-visual-smoke=blocked`、`passed=0` 和空真实 UI evidence 的语义。 -- 明确摘要用于评审阅读、交接和发布风险判断,不能用于替代真实视觉 smoke。 -- 明确 blocked 摘要应突出阻塞原因、未观察范围、风险和恢复后的下一步。 -- 帮评审区分“blocked summary 生成合规”和“地图列表视觉 smoke 通过”这两个完全不同的结论。 - -## 非目标 - -- V 组不执行 WeChat DevTools、DevTools CLI open/preview、真机调试或任何真实 UI smoke。 -- V 组不修改业务 UI、WXML、WXSS、JS、手测脚本或 evidence 生成脚本。 -- V 组不声明地图列表视觉 smoke、DevTools smoke、真机 smoke 或完整用户旅程通过。 -- V 组不把 U 组 blocked JSON、L 组 Markdown 摘要或任何静态检查结果升级为 `passed`。 -- V 组不提交 ignored local JSON 或 ignored local Markdown 摘要;本 brief 只定义产品口径。 - -## 与 L/S/T/U 的关系 - -| 组别 | 已提供能力 | V 组补齐的产品口径 | -| --- | --- | --- | -| S | 新增 `map-list-visual-smoke` 证据槽位,要求记录长标题、长正文、带图/无图、安全区、原生地图层、滚动和详情链路 | V 组强调这些观察仍未执行,摘要只能呈现 blocked 状态和未验证范围 | -| T | 要求手测 JSON 中恰好保留一条 `map-list-visual-smoke` journey | V 组要求摘要继续展示该 journey 的 blocked 结论,不能因为摘要化而丢失必备槽位 | -| U | 生成 ignored blocked local JSON,并明确 blocked 不是 UI passed 或 failed evidence | V 组承接 U 的 JSON 语义,把它翻译成评审更容易阅读的脱敏摘要口径 | -| L | 从 ignored local JSON 生成 ignored local Markdown 摘要,并避免泄露 raw evidence | V 组定义 L 摘要在 blocked 场景下的用户价值和发布判断边界 | - -## Blocked Summary 边界 - -blocked summary 可以被认为“通过”的条件: - -- 输入来自合规的 ignored local JSON,且 `map-list-visual-smoke` 状态仍为 `blocked`。 -- 摘要展示 blocked 原因、实际未执行事实、未验证风险和 follow-up。 -- 摘要不包含敏感路径、凭证、手机号、CloudBase 私有资源或未经脱敏的原始证据。 -- 摘要中的地图列表视觉 smoke 仍保持 `passed=0` 语义;如果 evidence 为空,只能说明没有真实 UI 证据。 -- 摘要让评审能看懂当前结论:环境阻塞已被记录,真实地图列表视觉 smoke 仍未验证。 - -blocked summary 应判为失败或不可接受的条件: - -- 把 `blocked` 改写成 `passed`、`ready`、`已验证通过` 或类似视觉通过表述。 -- 隐藏 DevTools/真机未执行事实,或只写“摘要生成成功”而不写 UI 未观察。 -- 用 readiness、static guard、helper 成功、JSON 校验通过或 Markdown 生成成功替代真实视觉证据。 -- 丢失 `map-list-visual-smoke` journey、丢失 blocked reason、丢失 risks/follow-up,导致评审无法判断发布风险。 -- 泄露本机路径、凭证、私有 cloud 路径、用户手机号或其他不应进入评审摘要的信息。 - -核心边界:blocked summary 的合格只表示“阻塞结论被清楚、脱敏地转述”;它不是地图列表视觉 smoke 的 passed,也不是业务 UI 的 failed。 - -## 成功标准 - -- 评审只读摘要即可知道当前地图列表视觉 smoke 是 blocked,而不是 passed。 -- 评审能看到为什么 blocked、哪些用户可见风险仍未观察、恢复 DevTools/真机后要做什么。 -- 摘要链路保留 U 组的 `passed=0` 和 `evidence=[]` 语义,不制造新的通过证据。 -- 摘要链路符合 L 组脱敏目标,不暴露敏感路径、凭证或私有资源。 -- 任何发布或合入判断都继续要求真实 WeChat DevTools UI 或真机视觉 smoke 证据。 - -## 下一步 - -- 当 DevTools 或真机仍不可用时,先用 U 组 helper 生成 ignored blocked local JSON,再用 L 组摘要脚本生成 ignored local Markdown,供评审理解 blocker。 -- 评审 blocked summary 时,只接受“blocked 记录可读且合规”这个结论,不接受“地图列表视觉 smoke 已通过”。 -- DevTools 服务端口或真机入口恢复后,重新执行 S 组定义的 `map-list-visual-smoke`;只有真实观察完成并补齐证据后,才允许把 journey 改为 `passed` 或 `failed`。 -- 若真实 smoke 仍被阻塞,更新 blocked reason 和 follow-up,再重新生成脱敏摘要,保持 blocked 结论可读、可追踪。 diff --git a/harness/map-list-evidence-gate-checklist.md b/harness/map-list-evidence-gate-checklist.md deleted file mode 100644 index 634307e..0000000 --- a/harness/map-list-evidence-gate-checklist.md +++ /dev/null @@ -1,370 +0,0 @@ -# 地图列表视觉证据 Journey Gate QA 清单 - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-evidence-gate` -- 角色:T 组 QA agent - -范围:本清单只定义 `map-list-visual-smoke` 必备 journey 的证据模板完整性 gate。它用于确认校验脚本能发现 journey 被误删、关键字段失效、状态误写或默认结果被误标为通过;它不代表 WeChat DevTools、真机、地图列表 UI 或视觉 smoke 已通过。 - -重要边界: - -- [ ] gate 通过只能写作“证据模板完整性通过”或“manual evidence schema gate 通过”,不得写作 `UI passed`、`视觉已通过`、`DevTools 已通过` 或 `真机通过`。 -- [ ] `harness/manual-test-results.example.json` 中的 `map-list-visual-smoke` 默认必须是 `not_covered`,`evidence` 必须为空数组,`actual` 必须仍表达未执行。 -- [ ] `scripts/prepare-manual-test-run.mjs` 生成的 local JSON 中,`summary.overallStatus` 仍应为 `not_covered`,全部 journeys 的 `passed` 数量仍应为 `0`。 -- [ ] 只有真实打开 WeChat DevTools UI 或真机并完成观察后,才允许在 ignored local JSON 中把具体 journey 改成 `passed`;本 gate 不能替代该观察。 -- [ ] 下面坏样例命令只写 ignored 的 `harness/manual-test-results.local*.json` 临时文件;执行后要清理,不要修改脚本、JSON 模板、产品文档或业务代码。 - -## 1. 准备 - -- [ ] 确认工作目录、分支和干净状态。 - - ```bash - pwd - git branch --show-current - git status --short - ``` - - 期望:工作目录对应 `/tmp/street-tasks-iter-worktrees/map-list-evidence-gate`,分支为 `codex/iter-map-list-evidence-gate`;除本清单外没有需要 T 组处理的改动。 - -- [ ] 跑基础 harness。 - - ```bash - bash harness/init.sh - ``` - - 期望:输出包含 `Checked 11 JSON files.`、`Harness OK: 6 features checked.` 和 `Harness init complete.`。 - -- [ ] 确认必备 journey 当前存在且默认未覆盖。 - - ```bash - node --input-type=module -e "import fs from 'node:fs'; const r=JSON.parse(fs.readFileSync('harness/manual-test-results.example.json','utf8')); const matches=r.journeys.filter((j)=>j.id==='map-list-visual-smoke'); if (matches.length!==1) throw new Error('expected exactly one map-list-visual-smoke journey'); const j=matches[0]; if (j.status!=='not_covered') throw new Error('map-list-visual-smoke must default to not_covered'); if (!Array.isArray(j.evidence) || j.evidence.length!==0) throw new Error('map-list-visual-smoke default evidence must be empty'); console.log('map-list-visual-smoke default template OK');" - ``` - - 期望:命令通过并输出 `map-list-visual-smoke default template OK`。 - -## 2. 正向命令 - -- [ ] example 证据模板结构校验通过。 - - ```bash - node scripts/check-manual-evidence.mjs - ``` - - 期望:输出 `Manual evidence checks passed.`。 - -- [ ] 证据卫生校验通过。 - - ```bash - node scripts/check-evidence-hygiene.mjs - ``` - - 期望:输出 `Evidence hygiene checks passed.`;同时 example 中没有任何 journey 被标为 `passed`。 - -- [ ] 手测准备 helper 能生成 ignored local JSON。 - - ```bash - node scripts/prepare-manual-test-run.mjs --out harness/manual-test-results.local-gate-smoke.json --force - ``` - - 期望输出至少包含: - - - `Running readiness preflight before manual UI testing.` - - `This includes scripts/check-devtools-readiness.mjs and the map list static guard.` - - `Passing preflight does not prove DevTools or real-device visual acceptance.` - - `Manual evidence checks passed.` - - `Evidence hygiene checks passed.` - - `Manual test run prepared.` - -- [ ] local JSON 默认仍没有任何 passed journey。 - - ```bash - node --input-type=module -e "import fs from 'node:fs'; const p='harness/manual-test-results.local-gate-smoke.json'; const r=JSON.parse(fs.readFileSync(p,'utf8')); const j=r.journeys.find((item)=>item.id==='map-list-visual-smoke'); const passed=r.journeys.filter((item)=>item.status==='passed').length; if (!j) throw new Error('local JSON missing map-list-visual-smoke'); if (j.status!=='not_covered') throw new Error('local map-list-visual-smoke must default to not_covered'); if (passed!==0) throw new Error(`local JSON must have passed=0, got ${passed}`); if (r.summary?.overallStatus!=='not_covered') throw new Error('local summary.overallStatus must remain not_covered'); console.log(`overall=${r.summary.overallStatus} passed=${passed} mapList=${j.status}`);" - ``` - - 期望:输出 `overall=not_covered passed=0 mapList=not_covered`。 - -- [ ] local JSON 保持 ignored,不进入提交范围。 - - ```bash - git status --short --ignored - ``` - - 期望:`harness/manual-test-results.local-gate-smoke.json` 只出现在 ignored 区域,不能出现在 staged 或普通未跟踪待提交列表。 - -## 3. 坏样例:缺 Journey - -目标:确认 gate 能发现 `map-list-visual-smoke` 被误删。若当前脚本未报错,应把该项记录为 gate 缺口,不得视为通过。 - -- [ ] 生成缺 journey 的坏样例 local JSON。 - - ```bash - cp harness/manual-test-results.example.json harness/manual-test-results.local-gate-missing-journey.json - node --input-type=module -e "import fs from 'node:fs'; const p='harness/manual-test-results.local-gate-missing-journey.json'; const r=JSON.parse(fs.readFileSync(p,'utf8')); r.journeys=r.journeys.filter((j)=>j.id!=='map-list-visual-smoke'); fs.writeFileSync(p, `${JSON.stringify(r,null,2)}\n`);" - node scripts/check-manual-evidence.mjs harness/manual-test-results.local-gate-missing-journey.json - ``` - -- [ ] 预期错误提示。 - - 期望:命令失败,错误应包含等价语义: - - ```text - Missing required journey id: map-list-visual-smoke - ``` - - 若只输出 `Manual evidence checks passed.`,说明现有校验没有覆盖必备 journey 存在性,本轮 gate 不可签通过。 - -## 4. 坏样例:重复 Journey - -目标:确认 gate 能发现 `map-list-visual-smoke` 被重复复制。重复条目会让评审难以判断哪条才是真实视觉 smoke 结果。 - -- [ ] 生成重复 journey 的坏样例 local JSON。 - - ```bash - cp harness/manual-test-results.example.json harness/manual-test-results.local-gate-duplicate-journey.json - node --input-type=module -e "import fs from 'node:fs'; const p='harness/manual-test-results.local-gate-duplicate-journey.json'; const r=JSON.parse(fs.readFileSync(p,'utf8')); const j=r.journeys.find((item)=>item.id==='map-list-visual-smoke'); r.journeys.splice(2,0,{...j}); fs.writeFileSync(p, `${JSON.stringify(r,null,2)}\n`);" - node scripts/check-manual-evidence.mjs harness/manual-test-results.local-gate-duplicate-journey.json - ``` - -- [ ] 预期错误提示。 - - 期望:命令失败,错误应包含等价语义: - - ```text - Expected exactly one required journey id: map-list-visual-smoke; found 2. - ``` - -## 5. 坏样例:关键字段失效 - -目标:确认 `map-list-visual-smoke` 的必备字段损坏时会失败。 - -- [ ] 删除 `steps` 字段。 - - ```bash - cp harness/manual-test-results.example.json harness/manual-test-results.local-gate-missing-steps.json - node --input-type=module -e "import fs from 'node:fs'; const p='harness/manual-test-results.local-gate-missing-steps.json'; const r=JSON.parse(fs.readFileSync(p,'utf8')); const j=r.journeys.find((item)=>item.id==='map-list-visual-smoke'); delete j.steps; fs.writeFileSync(p, `${JSON.stringify(r,null,2)}\n`);" - node scripts/check-manual-evidence.mjs harness/manual-test-results.local-gate-missing-steps.json - ``` - - 期望错误包含: - - ```text - journey map-list-visual-smoke is missing required field: steps - ``` - -- [ ] 删除 `expected` 字段。 - - ```bash - cp harness/manual-test-results.example.json harness/manual-test-results.local-gate-missing-expected.json - node --input-type=module -e "import fs from 'node:fs'; const p='harness/manual-test-results.local-gate-missing-expected.json'; const r=JSON.parse(fs.readFileSync(p,'utf8')); const j=r.journeys.find((item)=>item.id==='map-list-visual-smoke'); delete j.expected; fs.writeFileSync(p, `${JSON.stringify(r,null,2)}\n`);" - node scripts/check-manual-evidence.mjs harness/manual-test-results.local-gate-missing-expected.json - ``` - - 期望错误包含: - - ```text - journey map-list-visual-smoke is missing required field: expected - ``` - -- [ ] 删除 `evidence` 字段。 - - ```bash - cp harness/manual-test-results.example.json harness/manual-test-results.local-gate-missing-evidence.json - node --input-type=module -e "import fs from 'node:fs'; const p='harness/manual-test-results.local-gate-missing-evidence.json'; const r=JSON.parse(fs.readFileSync(p,'utf8')); const j=r.journeys.find((item)=>item.id==='map-list-visual-smoke'); delete j.evidence; fs.writeFileSync(p, `${JSON.stringify(r,null,2)}\n`);" - node scripts/check-manual-evidence.mjs harness/manual-test-results.local-gate-missing-evidence.json - ``` - - 期望错误包含: - - ```text - journey map-list-visual-smoke is missing required field: evidence - ``` - -## 6. 坏样例:错误状态 - -目标:确认 status 只能是 `passed`、`failed`、`blocked`、`not_covered`。 - -- [ ] 把 `map-list-visual-smoke` 改成非法状态。 - - ```bash - cp harness/manual-test-results.example.json harness/manual-test-results.local-gate-bad-status.json - node --input-type=module -e "import fs from 'node:fs'; const p='harness/manual-test-results.local-gate-bad-status.json'; const r=JSON.parse(fs.readFileSync(p,'utf8')); const j=r.journeys.find((item)=>item.id==='map-list-visual-smoke'); j.status='ui_passed'; fs.writeFileSync(p, `${JSON.stringify(r,null,2)}\n`);" - node scripts/check-manual-evidence.mjs harness/manual-test-results.local-gate-bad-status.json - ``` - - 期望错误包含: - - ```text - journey map-list-visual-smoke has invalid status "ui_passed" - ``` - -- [ ] 手动复核:不要新增 `visual_passed`、`devtools_passed`、`real_device_passed` 等自造状态;真实 UI 结果仍只能映射到允许状态之一。 - -## 7. Blocked / Passed 边界 - -目标:确认 blocked 能表达环境阻塞,passed 不能被空证据或未执行文案伪造。 - -- [ ] `blocked` 边界:blocked 但没有 `risks` 和 `followUp` 应失败。 - - ```bash - cp harness/manual-test-results.example.json harness/manual-test-results.local-gate-bad-blocked.json - node --input-type=module -e "import fs from 'node:fs'; const p='harness/manual-test-results.local-gate-bad-blocked.json'; const r=JSON.parse(fs.readFileSync(p,'utf8')); const j=r.journeys.find((item)=>item.id==='map-list-visual-smoke'); j.status='blocked'; j.risks=[]; j.followUp=''; fs.writeFileSync(p, `${JSON.stringify(r,null,2)}\n`);" - node scripts/check-manual-evidence.mjs harness/manual-test-results.local-gate-bad-blocked.json - ``` - - 期望错误包含: - - ```text - journey map-list-visual-smoke is blocked but both risks and followUp are empty. - ``` - -- [ ] `blocked` 正向边界:如果 DevTools service port、项目导入、真机不可用或测试数据准备失败,应写 `blocked`,并保留 `risks` 或 `followUp`,不得改成 `passed`。 - -- [ ] `passed` 边界:passed 但 evidence 为空应失败。 - - ```bash - cp harness/manual-test-results.example.json harness/manual-test-results.local-gate-bad-passed.json - node --input-type=module -e "import fs from 'node:fs'; const p='harness/manual-test-results.local-gate-bad-passed.json'; const r=JSON.parse(fs.readFileSync(p,'utf8')); const j=r.journeys.find((item)=>item.id==='map-list-visual-smoke'); j.status='passed'; j.evidence=[]; fs.writeFileSync(p, `${JSON.stringify(r,null,2)}\n`);" - node scripts/check-manual-evidence.mjs harness/manual-test-results.local-gate-bad-passed.json - ``` - - 期望错误至少包含: - - ```text - journey map-list-visual-smoke is passed but evidence is empty. - ``` - -- [ ] 手动复核:即使 `steps`、`expected`、`actual` 非空,只要 `actual` 仍写着未覆盖、未执行、blocked、静态检查通过或 helper prepared,就不能把 `map-list-visual-smoke` 写成 `passed`。 - -- [ ] 手动复核:example 模板中任何 journey 被改成 `passed` 都应被证据卫生 gate 拒绝;若只能在一次性分支或临时副本里验证,验证后立即丢弃该副本,不要在当前工作树改模板。 - -## 8. Local Helper 回归 - -目标:确认 helper 只准备手测文件,不伪造 UI 通过。 - -- [ ] 重新生成 local JSON。 - - ```bash - node scripts/prepare-manual-test-run.mjs --out harness/manual-test-results.local-gate-helper.json --force - ``` - -- [ ] 检查 helper 输出边界。 - - 期望:输出包含 `Passing preflight does not prove DevTools or real-device visual acceptance.`;不得出现 `UI passed`、`DevTools visual passed`、`real-device passed` 等措辞。 - -- [ ] 检查 local JSON 默认值。 - - ```bash - node --input-type=module -e "import fs from 'node:fs'; const r=JSON.parse(fs.readFileSync('harness/manual-test-results.local-gate-helper.json','utf8')); const passed=r.journeys.filter((j)=>j.status==='passed'); const target=r.journeys.find((j)=>j.id==='map-list-visual-smoke'); if (passed.length!==0) throw new Error(`helper must keep passed=0, got ${passed.length}`); if (!target) throw new Error('helper output missing map-list-visual-smoke'); if (target.status!=='not_covered') throw new Error(`helper map-list-visual-smoke must be not_covered, got ${target.status}`); if (target.evidence.length!==0) throw new Error('helper map-list-visual-smoke evidence must be empty'); console.log('helper local JSON keeps passed=0 and map-list-visual-smoke not_covered');" - ``` - - 期望:输出 `helper local JSON keeps passed=0 and map-list-visual-smoke not_covered`。 - -- [ ] 检查 local 文件 ignored。 - - ```bash - git status --short --ignored - ``` - - 期望:`harness/manual-test-results.local-gate-helper.json` 不会进入可提交变更。 - -## 9. 证据卫生 - -- [ ] 运行证据卫生 gate。 - - ```bash - node scripts/check-evidence-hygiene.mjs - ``` - - 期望:输出 `Evidence hygiene checks passed.`。 - -- [ ] 确认 `.gitignore` 仍包含本地手测产物。 - - ```bash - rg -n "harness/manual-test-results\\.local\\*\\.json|harness/manual-test-summary\\.local\\*\\.md|harness/manual-evidence-artifacts/" .gitignore - ``` - - 期望:三类 ignored 规则都存在。 - -- [ ] 检查待提交内容没有本地证据、截图、录屏、完整日志或敏感路径。 - - ```bash - git status --short --ignored - git diff -- harness - ``` - - 期望:可提交范围只有本轮 QA 清单;local JSON、local summary 和 `harness/manual-evidence-artifacts/` 保持 ignored。 - -- [ ] 如果未来填写真实 evidence,只能写脱敏摘要或附件编号;不要提交真实截图、录屏、二维码、完整 Console/Network、`cloud://` 完整 fileID、CloudBase env id、openId、手机号、cookie、token、本机绝对路径或精确经纬度。 - -## 10. 错误日期扫描 - -目标:防止本轮文档误写成后一日日期。 - -- [ ] 扫描本轮相关文件,不应出现后一日误写。 - - ```bash - node --input-type=module <<'NODE' - import { existsSync, readFileSync } from 'node:fs'; - - const badDate = ['2026', '06', String(14 + 1).padStart(2, '0')].join('-'); - const files = [ - 'harness/map-list-evidence-gate-checklist.md', - 'harness/manual-test-results.example.json', - 'harness/feature_list.json', - 'harness/claude-progress.md' - ]; - - let failed = false; - for (const file of files) { - if (!existsSync(file)) continue; - const text = readFileSync(file, 'utf8'); - if (text.includes(badDate)) { - console.error(`${file}: contains wrong run date`); - failed = true; - } - } - - if (failed) process.exit(1); - console.log('Wrong date scan passed.'); - NODE - ``` - - 期望:输出 `Wrong date scan passed.`。 - -## 11. 收尾验证 - -- [ ] 清理坏样例和 helper local 文件。 - - ```bash - rm -f harness/manual-test-results.local-gate*.json - ``` - -- [ ] 跑基础 harness。 - - ```bash - bash harness/init.sh - ``` - - 期望:完整通过;这仍只证明 baseline 与 harness gate 通过,不证明 UI 通过。 - -- [ ] 检查 diff 空白。 - - ```bash - git diff --check - ``` - - 期望:无输出,退出码为 0。 - -- [ ] 检查最终工作树。 - - ```bash - git status --short --ignored - ``` - - 期望:可提交改动只包含 T 组产品 brief、QA 清单、manual evidence gate 脚本和 harness 记录;本地 smoke JSON 或附件目录仍为 ignored 或已清理。 - -- [ ] 最终报告只说明: - - - 修改文件:T 组产品 brief、QA 清单、manual evidence gate 脚本和 harness 记录 - - 已运行:manual evidence 正向/坏样例、helper local JSON、证据卫生、readiness、harness init 和 diff 检查 - - 结论:T 组完成的是证据模板完整性 gate;没有声明 WeChat DevTools、真机或地图列表 UI 已通过。 diff --git a/harness/map-list-evidence-gate-product-brief.md b/harness/map-list-evidence-gate-product-brief.md deleted file mode 100644 index 0d3211e..0000000 --- a/harness/map-list-evidence-gate-product-brief.md +++ /dev/null @@ -1,63 +0,0 @@ -# 地图列表视觉 Smoke 必备 Journey 门禁产品 Brief - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-evidence-gate` -- 角色:T 组产品 agent -- 对应 feature:`map-feed-001` - -## 用户问题 - -S 组已经在 `harness/manual-test-results.example.json` 里新增 `map-list-visual-smoke` journey,用来承接地图列表真实视觉 smoke 的证据。T 组要处理的风险是:如果未来有人删除、改名或重复复制这个 journey,通用字段校验可能看起来仍然完整,导致地图列表视觉 smoke 从手测入口里静默消失或变得含混。 - -这会让执行者误以为手测模板完整,评审也难以及时发现地图列表长标题、长正文、图片、安全区、原生地图层、滚动和详情链路没有固定记录入口。 - -## 产品假设 - -把 `map-list-visual-smoke` 从“文档约定”升级为“必备 journey 门禁”,可以降低后续候选遗漏地图列表视觉证据槽位的风险。门禁通过只证明模板仍包含这条必测 journey,不证明 DevTools 或真机视觉已经通过。 - -## 范围 - -- 定义 `map-list-visual-smoke` 是手测结果模板里的必备 journey。 -- 要求门禁至少校验 `journeys[].id` 中存在且只存在一条 `map-list-visual-smoke`。 -- 要求该 journey 保留地图列表视觉 smoke 的核心记录口径:长标题、长正文、带图/无图、安全区、原生地图层、抽屉滚动、marker/list/detail 链路。 -- 要求门禁失败时给出明确提示,说明缺少地图列表视觉 smoke 记录入口。 -- 要求输出文案继续区分“模板完整性通过”和“真实视觉验收通过”。 - -## 非目标 - -- 不声明 WeChat DevTools 或真机已经通过。 -- 不把 `not_covered`、`blocked`、自动脚本通过或 example JSON 通过写成视觉 `passed`。 -- 不替代 S 组 checklist 中要求的真实观察、截图、录屏、日志或任务 id。 -- 不新增或修改地图页 UI、WXML、WXSS、业务逻辑、数据模型或云端能力。 -- 不恢复 DevTools 9420 服务端口,也不执行任何本机 DevTools 操作。 - -## 成功/失败/Blocked 判定 - -- 成功:手测证据门禁能发现 `map-list-visual-smoke` 是否存在;删除、改名或重复该 journey 时应失败。正常模板通过时,只能说明“必备 journey 存在且字段结构可被校验”。 -- 失败:模板缺少 `map-list-visual-smoke`,或该 journey 被改成无法承接地图列表视觉 smoke 的泛化条目;如果门禁仍通过,应视为产品门禁失败。 -- Blocked:若校验器无法稳定读取手测结果、模板路径不稳定,或并行分支改变了手测结果结构,应记录 blocked 并回到产品/QA 对齐。 - -任何情况下,门禁通过都不等于 DevTools/真机视觉 passed。它只证明模板包含必备 journey,真实视觉结论仍必须来自 DevTools UI 或真机观察,并按 evidence 规范记录。 - -## 与 S/R/P/Q 的关系 - -- P 组:定义地图列表 WXML/WXSS static guard,降低结构和样式回归风险。 -- Q 组:把 P 的 static guard 接入 readiness preflight,降低候选漏跑自动检查的风险。 -- R 组:让手测准备 helper 显式提示 readiness 和地图列表 static guard,降低执行入口误解风险。 -- S 组:在手测模板中新增 `map-list-visual-smoke`,为真实视觉 smoke 留出证据槽位。 -- T 组:定义必备 journey 门禁价值,防止 S 组新增的证据槽位未来被删除、改名或绕过。 - -P/Q/R/T 都不能替代 S 组要求的真实视觉观察;S 组模板存在也不能替代真实执行。 - -## 证据 - -- 当前 `harness/manual-test-results.example.json` 已包含 `map-list-visual-smoke`,状态为 `not_covered`,没有伪造 passed evidence。 -- T 组计划把 `scripts/check-manual-evidence.mjs` 从通用字段校验扩展为必备 journey gate:缺少或重复 `map-list-visual-smoke` 都应失败。 -- T 组不改 example JSON 的真实结论,不声称真实手测完成,也不把模板完整性通过升级成视觉验收通过。 - -## 下一步 - -- 开发实现应让 `scripts/check-manual-evidence.mjs` 把 `map-list-visual-smoke` 加入 required journey 校验,并覆盖缺失与重复两类坏样例。 -- QA 验证应保留坏样例:删除或重复 `map-list-visual-smoke` 后门禁必须失败。 -- 后续真实手测仍需在 DevTools UI 或真机中执行,并把结果写入 ignored local JSON 或脱敏摘要。 -- 若真实环境仍 blocked,应把 `map-list-visual-smoke` 写为 `blocked` 或 `not_covered`,不要用门禁通过替代视觉 passed。 diff --git a/harness/map-list-preflight-checklist.md b/harness/map-list-preflight-checklist.md deleted file mode 100644 index dd5ff44..0000000 --- a/harness/map-list-preflight-checklist.md +++ /dev/null @@ -1,71 +0,0 @@ -# 地图列表 Preflight 集成 QA 清单 - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-preflight` -- 工作目录:`/tmp/street-tasks-iter-worktrees/map-list-preflight` -- 角色:Q 组 QA / 设计评测 agent - -范围:本清单验证地图列表静态韧性检查已经进入 readiness/preflight 链路。它只覆盖自动门禁和证据口径,不代表地图列表已经在 WeChat DevTools 或真机上通过视觉验收。 - -## 1. 自动检查 - -- [ ] 运行地图列表静态检查。 - - ```bash - node --no-warnings scripts/check-map-list-resilience.mjs - ``` - - 期望:输出 `Map list resilience checks passed.`。若失败,失败信息应指出缺失的 WXML 结构或 WXSS 规则,例如标题行、标签组、正文 clamp、缩略图尺寸、footer 右侧省略或详情按钮固定宽度。 - -- [ ] 运行 readiness 聚合检查。 - - ```bash - node --no-warnings scripts/check-devtools-readiness.mjs - ``` - - 期望:输出发布、信任洞察、候选流程和 `Map list resilience checks passed.`;最终输出 `DevTools readiness checks passed.`,同时文档或评测报告仍说明 DevTools 与真机视觉验收需要人工执行。 - -- [ ] 运行基础 harness。 - - ```bash - bash harness/init.sh - ``` - - 期望:JSON 与 harness 自检通过,仓库仍能从标准入口重启。 - -## 2. 失败时的阻断口径 - -- [ ] 如果 `scripts/check-map-list-resilience.mjs` 缺失,readiness 必须失败,不能把候选写成 ready for visual acceptance。 -- [ ] 如果地图列表静态检查失败,记录为 preflight blocker,并把具体缺失项写入 handoff 或评测报告。 - - 期望首行:`Map list resilience checks failed:` - - 期望缺口明细:`- WXML missing ...`、`- WXSS missing ...` 或 `- WXSS ... should ...` - - 期望边界提示:`This is a static guard only; DevTools or real-device visual acceptance is still required.` -- [ ] 如果 readiness 聚合检查因为地图列表失败而退出,终端应保留地图列表的具体失败项,不能只显示笼统的 readiness 失败。 -- [ ] 如果 readiness 其他门禁失败,保持原有失败优先级,不用地图列表检查覆盖发布、信任洞察或候选流程风险。 -- [ ] 失败报告中不要写“DevTools 已通过”“真机已通过”或“地图 UI 已验证”,除非后续有真实环境证据。 - -## 3. 地图列表风险覆盖 - -- [ ] 长标题:标题应允许换行或断词,不能把分类/状态标签和详情入口挤出卡片。 -- [ ] 标签:分类、lost/found、状态标签应允许换行,不能依赖单行硬撑。 -- [ ] 缩略图:带图卡片应保留固定图片列、正文列和固定详情入口列。 -- [ ] 正文:摘要应有行数和溢出保护,长正文不能让单卡无限拉高。 -- [ ] Footer:确认/过时统计可换行,右侧时间距离可省略,二者不能互相覆盖。 -- [ ] 详情入口:轻量按钮宽高应稳定,不能因 hover、loading 或文本变化撑大布局。 - -## 4. 仍需人工验证 - -- [ ] 自动检查只能证明关键 WXML/WXSS 结构和样式约束存在,不能证明真实渲染、触摸命中、图片加载、字体度量或地图原生层表现正确。 -- [ ] DevTools service port 9420 恢复后,用微信 DevTools 打开本分支项目并完成一次普通编译。 -- [ ] 在常见模拟器宽度和窄屏下打开地图列表抽屉,验证长标题、长正文、带图、无图、多状态和底部统计组合。 -- [ ] 在真机或真机预览中验证安全区、tabBar 上方空间、最后一张卡片滚动到底部、图片加载前后布局和详情跳转。 -- [ ] 记录 DevTools 版本、基础库、设备或模拟器宽度、分支、提交 SHA、步骤、实际结果和脱敏截图或录屏编号。 - -## 5. 9420 Blocked 时的降级证据 - -- [ ] 若端口仍 blocked,记录 `scripts/inspect-devtools-port-state.mjs` 或 `scripts/check-devtools-smoke-access.mjs` 的摘要,而不是反复尝试 quit/open。 -- [ ] 降级报告应同时写清:静态 readiness 结果、DevTools/真机视觉验收状态、下一步人工恢复动作。 -- [ ] 降级字段至少包括:`branch`、`commit`、`worktree`、`baseline`、`devtoolsPort=9420`、`portStatus`、`actualResult`、`manualJourneyImpact`、`nextAction`。 -- [ ] 可接受摘要包括:`9420 connection refused`、`declared ide-http-port but no listener`、`DevTools UI journey not executed`。 -- [ ] 可提交文档中不要包含本机用户目录、原始截图路径、cookie、token、云端私密 ID 或完整控制台日志。 -- [ ] 结论使用 `blocked`、`unverified`、`static gate passed` 等状态词,避免把静态门禁包装成真实用户体验通过。 diff --git a/harness/map-list-preflight-product-brief.md b/harness/map-list-preflight-product-brief.md deleted file mode 100644 index 8051529..0000000 --- a/harness/map-list-preflight-product-brief.md +++ /dev/null @@ -1,53 +0,0 @@ -# 地图列表 Preflight 集成产品 Brief - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-preflight` -- 角色:Q 组产品 agent -- 对应 feature:`map-feed-001` - -## 问题 - -P 组已经新增 `scripts/check-map-list-resilience.mjs`,可以静态检查地图列表抽屉和任务卡片的关键 WXML/WXSS 结构。这个检查单独存在时,后续候选版仍可能只运行旧的 readiness 命令,漏掉地图列表长标题、标签、缩略图、footer 和详情入口的防回归门禁。 - -当前 WeChat DevTools service port 9420 仍处于 blocked 状态,真实视觉 smoke 还不能稳定执行。因此 Q 组的重点是把这道静态检查纳入更高层的 `scripts/check-devtools-readiness.mjs`,作为候选进入人工 DevTools 或真机验证前的自动门禁,而不是把它包装成已经完成的 UI 验收。 - -## 用户价值 - -地图列表是用户进入附近任务的主入口之一。把列表卡片的静态韧性检查接入 readiness 链路,可以在每次准备大版本手测前自动发现明显结构回退,减少“手测前才发现列表卡片被挤坏”或“手测报告遗漏地图列表风险”的概率。 - -## 范围内 - -- `scripts/check-devtools-readiness.mjs` 默认运行 `scripts/check-map-list-resilience.mjs`。 -- readiness 所需文件清单包含地图列表韧性脚本和 P 组产品/QA 文档。 -- readiness 成功输出继续说明静态门禁通过不等于 DevTools 或真机视觉通过。 -- 失败时把地图列表静态检查视为 preflight blocker,不能继续把候选描述为 ready for visual acceptance。 -- Q 组文档补充执行者该如何解释这道门禁,以及在 9420 blocked 时如何记录降级证据。 - -## 非目标 - -- 不修改地图页业务逻辑、WXML 或 WXSS。 -- 不新增用户可见功能,也不改变地图列表视觉方案。 -- 不恢复 DevTools 9420 服务端口,不重启、退出或操作本机 DevTools。 -- 不宣称地图列表、发布、详情或完整用户旅程已经通过真实 UI smoke。 -- 不替代 P 组 checklist 中要求的 DevTools、真机、窄屏、安全区和真实交互检查。 - -## 成功标准 - -- 运行 `node --no-warnings scripts/check-devtools-readiness.mjs` 时,输出包含 `Map list resilience checks passed.`。 -- 如果 `scripts/check-map-list-resilience.mjs` 缺失或失败,readiness 命令失败,并把它作为进入人工手测前必须处理的 blocker。 -- readiness 的最终通过文案同时保留两层结论:自动静态门禁通过;DevTools 和真机视觉验收仍需人工执行。 -- `bash harness/init.sh`、JSON 检查和 harness 检查仍可从干净仓库状态重新跑通。 -- 证据记录使用“降低结构/样式回归风险”“preflight blocker”这类措辞,不写成“视觉已通过”。 - -## 与 9420 Blocker 的关系 - -Q 组不解决 9420 blocked。它只让端口仍 blocked 时的候选准备更诚实:能运行的静态门禁必须先通过,不能运行的真实 DevTools/真机验收继续标记为 blocked 或 unverified。 - -如果后续端口恢复,执行者仍应按 P 组地图列表清单和已有手测 runbook 执行真实视觉 smoke,并记录 DevTools 版本、机型、屏宽、步骤、实际结果和脱敏证据。 - -## 评测关注点 - -- 相比 P 组,Q 组是否降低了“新增检查但无人记得运行”的风险。 -- 是否避免把静态检查当作视觉验收通过。 -- readiness 输出是否足够清楚,能让执行者知道下一步是人工 DevTools 或真机验证。 -- 是否保持改动范围小,没有为了门禁集成触碰页面实现。 diff --git a/harness/map-list-resilience-checklist.md b/harness/map-list-resilience-checklist.md deleted file mode 100644 index f264165..0000000 --- a/harness/map-list-resilience-checklist.md +++ /dev/null @@ -1,193 +0,0 @@ -# 地图列表 UX Resilience 检查清单 - -日期:2026-06-14 - -范围:用于 `codex/iter-map-list-resilience` 分支检查地图列表卡片在长内容、图片、状态标签和底部信息挤压下的韧性。当前 WeChat DevTools service port 仍 blocked,本清单不代表视觉验收已经通过;只有后续在 DevTools 或真机逐项确认并记录证据后,才能把相关用户可见结论写为 passed。 - -工作目录:`/tmp/street-tasks-iter-worktrees/map-list-resilience` - -## 0. 进入前检查 - -- [ ] 确认工作目录和分支。 - - ```bash - pwd - git branch --show-current - git status --short - ``` - - 期望:工作目录为 `/tmp/street-tasks-iter-worktrees/map-list-resilience`,分支为 `codex/iter-map-list-resilience`;工作区只包含本轮预期的 harness 文档改动。 - -- [ ] 跑基础 harness。 - - ```bash - bash harness/init.sh - ``` - - 期望:`node scripts/check-json.mjs` 输出 JSON 检查通过,`node harness/check-harness.mjs` 输出 harness 自检通过。若失败,先记录基础 blocker,不继续把视觉检查写成通过。 - -- [ ] 读取当前地图列表实现。 - - ```bash - sed -n '1,220p' pages/map/map.wxml - sed -n '1,260p' pages/map/map.wxss - sed -n '1,180p' utils/mock-posts.js - ``` - - 期望:理解卡片字段来源、图片分支、状态标签、详情入口、底部统计和 mock 数据覆盖缺口。 - -## 1. 静态结构检查 - -这些检查可以由脚本或命令辅助完成,但只能证明结构存在,不能证明视觉不溢出。 - -- [ ] 列表抽屉存在独立头部、分类筛选、滚动列表和空状态。 - - 可脚本检查:`pages/map/map.wxml` 中有 `list-drawer`、`drawer-head`、`filter-bar`、`post-list`、`empty-panel`。 - - 必须人工确认:抽屉打开时不会压住底部 tabBar,头部、筛选条、列表滚动区域在窄屏上层级清楚。 - -- [ ] 每张列表卡片包含内容区、标题行、正文摘要、底部信息和详情入口。 - - 可脚本检查:`post-card` 内有 `post-main`、`post-title-row`、`post-summary`、`post-footer`、`mark-button`。 - - 必须人工确认:卡片可点击聚焦任务,右侧详情按钮可以独立进入详情,不与整卡点击冲突。 - -- [ ] 标签跟随标题行,而不是挤占底部统计。 - - 可脚本检查:`post-title-row` 内包含 `post-title` 和 `post-inline-tags`,状态标签使用 `item.status !== 'active'` 条件渲染。 - - 必须人工确认:长标题、多标签、状态标签同时出现时,标签换行后仍属于标题区域,不把正文或详情按钮顶到不可用。 - -- [ ] 图片和无图分支结构都存在。 - - 可脚本检查:有 `item.coverImage ? 'with-thumb' : ''` 和 `wx:if="{{item.coverImage}}" class="post-thumb"`。 - - 必须人工确认:有图卡片图片尺寸稳定,无图卡片文字列自动占满,不出现空洞或错位。 - -## 2. WXSS 布局约束检查 - -这些检查适合写成静态脚本或 review checklist;若缺失,应先作为布局风险记录。 - -- [ ] 抽屉高度和滚动区域有明确约束。 - - 可脚本检查:`.list-drawer` 使用 `height: calc(100vh - 116rpx - env(safe-area-inset-bottom))`,`.post-list` 使用 `flex: 1`、`height: 0`、`min-height: 0`。 - - 必须人工确认:列表内容很多时只滚动列表区域,抽屉头部和筛选条不会被推走或遮挡。 - -- [ ] 卡片主区使用可收缩网格。 - - 可脚本检查:`.post-main` 使用 `grid-template-columns: minmax(0, 1fr) 48rpx`,`.post-main.with-thumb` 使用 `104rpx minmax(0, 1fr) 48rpx`。 - - 必须人工确认:窄屏下文字列会收缩,详情入口仍保持可点,不被标题或标签覆盖。 - -- [ ] 文本容器允许收缩并处理超长字符串。 - - 可脚本检查:`.post-text` 有 `min-width: 0`,`.post-title` 有 `max-width: 100%` 和 `word-break: break-all`,`.post-summary` 有 `overflow: hidden`、`text-overflow: ellipsis`、`-webkit-line-clamp: 2`。 - - 必须人工确认:连续中文、英文长词、数字地址和标点混排不会横向撑破卡片。 - -- [ ] 底部左右信息有收缩策略。 - - 可脚本检查:`.post-footer` 为 flex + `justify-content: space-between`,`.post-counts` 可换行,`.post-footer-meta` 有 `flex: 1`、`min-width: 0`、`text-align: right`、`white-space: nowrap`、`text-overflow: ellipsis`。 - - 必须人工确认:左侧“确认/过时”与右侧“时间/距离”在窄屏不重叠;右侧过长时截断,左侧统计仍可读。 - -- [ ] 固定尺寸元素不会随内容跳动。 - - 可脚本检查:`.post-thumb` 为 `104rpx` 正方形,`.post-card .mark-button` 为 `48rpx`。 - - 必须人工确认:图片加载前后、状态切换前后、筛选条数量变化时,卡片高度变化可接受且不会产生遮挡。 - -## 3. 内容韧性场景 - -后续执行者应准备本地 mock 或临时云端测试数据。若修改 mock,只能在专门测试分支或本地临时改动中进行;本清单本身不要求提交测试数据。 - -- [ ] 长标题。 - - 建议数据:40 到 80 个中文字符;一段不含空格的英文或数字串;标题后同时带分类、lost/found intent 和非 active 状态。 - - 可脚本检查:标题结构和 WXSS 换行约束存在。 - - 必须人工确认:标题换行后不遮挡标签、正文、图片或详情按钮,卡片间距仍清楚。 - -- [ ] 长正文。 - - 建议数据:正文超过 120 个中文字符,包含地点、时间、联系方式占位和标点。 - - 可脚本检查:`.post-summary` 两行截断规则存在。 - - 必须人工确认:正文最多展示两行,省略后不影响底部统计;极短正文和空格较多正文也不造成高度异常。 - -- [ ] 长地点、距离和时间。 - - 建议数据:`placeName` 使用长商圈/楼层/门口描述,`distanceText` 覆盖近距离和较远距离,`createdText` 覆盖“刚刚”、小时级和日期级文案。 - - 可脚本检查:底部右侧使用 `.post-footer-meta`,并在模板里渲染 `{{item.createdText}} · {{item.distanceText}}`。 - - 必须人工确认:右侧 meta 截断时仍从右对齐,不挤压左侧统计;长地点若只在选中卡片或详情出现,也要确认不会把列表卡片判断混淆。 - -- [ ] 有图卡片。 - - 建议数据:至少 1 张横图、1 张竖图、1 张加载慢或失败的图片引用。 - - 可脚本检查:`post-thumb` 使用 `mode="aspectFill"` 和 `lazy-load="{{true}}"`。 - - 必须人工确认:图片裁切稳定,加载失败不导致文字列横跳;有图时标题、标签、正文和详情按钮仍在同一视觉节奏中。 - -- [ ] 无图卡片。 - - 建议数据:当前 `utils/mock-posts.js` 默认均为无图,可作为最小无图样本。 - - 可脚本检查:`.post-main` 无图网格为文本列 + 详情按钮。 - - 必须人工确认:无图卡片不会留下缩略图空位;长标题场景下详情按钮仍贴右可点。 - -- [ ] 过期和已解决状态。 - - 建议数据:`status: 'expired'`、`status: 'resolved'`,并保留标题较长、正文较长、图片有无两类组合。 - - 可脚本检查:状态标签条件包含 `expired` 的 `neutral` 和 `resolved` 的 `done` class。 - - 必须人工确认:状态标签颜色可区分但不过度抢占;状态标签换行后不把详情入口挤出卡片。 - -- [ ] 多标签挤压。 - - 建议数据:`lost_found` + `intent` + `stale/resolved/expired`,标题使用长文本,另加图片。 - - 可脚本检查:`.post-inline-tags` 支持 `flex-wrap: wrap`,`.post-title-row` 也支持换行。 - - 必须人工确认:标签不会覆盖标题或进入右侧 chevron 区,整卡可扫读顺序仍为标题、标签、摘要、底部。 - -## 4. 底部统计与详情入口 - -- [ ] 左侧统计只承载轻量数字。 - - 可脚本检查:模板中 `post-counts` 只渲染 `确认:{{item.confirmations}}` 和 `过时:{{item.staleCount}}`。 - - 必须人工确认:两项统计在窄屏可同排或自然换行;不会与右侧时间距离重叠。 - -- [ ] 右侧 meta 只承载时间和距离。 - - 可脚本检查:模板中 `post-footer-meta` 渲染 `createdText` 和 `distanceText`,WXSS 有右对齐和 ellipsis。 - - 必须人工确认:超长 meta 被截断时仍能看出最近更新时间或距离中的一个关键信息;不出现省略号覆盖左侧统计。 - -- [ ] 详情入口保持稳定。 - - 可脚本检查:详情按钮为 `button.mark-button`,带 `aria-label="查看详情"`、`data-id="{{item.id}}"`、`catchtap="openDetail"`。 - - 必须人工确认:点击详情按钮进入对应任务详情;点击卡片其他区域仍执行聚焦;两者事件不会串扰。 - -## 5. 宽度与环境验收 - -当前 DevTools service port blocked,不能把以下项目写成已通过。端口恢复或 UI 可操作后,按下列矩阵补真实证据。 - -- [ ] DevTools 窄屏模拟器,约 320 到 360 CSS px。 - - 重点:长标题 + 有图 + 多标签 + 右侧 meta 截断。 - - 通过标准:无横向滚动、无文本重叠、详情按钮可点击、底部统计可读。 - -- [ ] DevTools 常见宽度,约 375 到 414 CSS px。 - - 重点:默认 mock 无图列表、筛选条滚动、抽屉高度、底部 tabBar safe area。 - - 通过标准:列表滚动顺畅,抽屉不遮挡 tabBar,卡片间距稳定。 - -- [ ] 大屏或折叠屏宽度。 - - 重点:卡片宽度变大后,标题/标签/正文排布不显得散乱,右侧 meta 不离主体过远。 - - 通过标准:信息层级仍可扫读,右侧详情入口位置一致。 - -- [ ] iOS 真机。 - - 重点:safe area、地图原生层、图片加载、触摸命中、底部抽屉。 - - 通过标准:抽屉底部与系统安全区协调,卡片和按钮没有被系统手势区遮挡。 - -- [ ] Android 真机。 - - 重点:字体度量、地图原生层、长英文/数字串、低端机图片加载。 - - 通过标准:文字不被裁掉,图片加载失败有可接受占位,不因渲染差异破坏布局。 - -## 6. 失败记录字段 - -任何失败、blocked 或 not covered 都要留下可复查摘要。不要只写“样式异常”。 - -- `checkedAt`:日期和时间,日期使用 `2026-06-14` 所在轮次口径。 -- `branch`:`codex/iter-map-list-resilience`。 -- `commit`:`git rev-parse --short HEAD`。 -- `worktree`:`/tmp/street-tasks-iter-worktrees/map-list-resilience`。 -- `environment`:DevTools 模拟器 / iOS 真机 / Android 真机。 -- `viewport`:宽度、高度、像素比或设备型号摘要。 -- `dataCase`:长标题 / 长正文 / 长地点 / 有图 / 无图 / 过期 / 已解决 / 多标签。 -- `postAlias`:使用 `post_001`、`测试任务 A` 等脱敏别名;真实云端 id 可本地留存,不提交完整值。 -- `steps`:打开地图、打开列表、切分类、滚动、点击详情等最小复现步骤。 -- `expected`:例如“不重叠、可截断、详情入口可点”。 -- `actual`:实际错位、遮挡、截断过度、点击串扰或白屏摘要。 -- `impact`:阻断详情进入、仅视觉瑕疵、只影响某宽度、只影响某状态等。 -- `evidenceLocation`:本地附件编号或外部安全位置,不写真实绝对路径。 -- `nextAction`:需要改 WXSS、改结构、补数据、恢复 DevTools 端口或真机复测。 - -## 7. 脱敏证据规则 - -- [ ] 原始截图、录屏、二维码、控制台完整日志、云端记录和真实图片只放 ignored 本地附件目录或外部安全位置。 -- [ ] 可提交文档只写脱敏摘要,例如“窄屏 DevTools 截图 S-map-01 显示长标题未遮挡详情入口,本地附件留存”。 -- [ ] 不提交真实 AppID、openId、unionId、头像 URL、昵称、手机号、精确经纬度、CloudBase 环境 ID、requestId、完整 `cloud://`、token、cookie 或本机绝对路径。 -- [ ] 地点用“默认中心附近”“测试 POI”“约 200m 距离”等模糊说法;不要写真实家庭、公司、学校或门牌号。 -- [ ] 日志只摘录最小必要错误类型,例如 service port blocked、connection refused、首条红色错误摘要;不要粘贴完整堆栈或 Network 详情。 -- [ ] 提交前运行: - - ```bash - git status --short --ignored - rg --no-ignore -n -i "(api[_-]?key|secret|token|password|passwd|pwd|private[_-]?key|session|cookie|authorization|bearer|access[_-]?token|refresh[_-]?token|client[_-]?secret|appsecret|wx[0-9a-f]{16,}|sk-[A-Za-z0-9_-]{20,}|AKIA[0-9A-Z]{16})" . - ``` - - 期望:真实附件和 local 结果仍是 ignored;secret scan 没有新增真实敏感值。若命中的是清单里的规则说明,也要人工确认不是实际凭据。 diff --git a/harness/map-list-resilience-product-brief.md b/harness/map-list-resilience-product-brief.md deleted file mode 100644 index cd06982..0000000 --- a/harness/map-list-resilience-product-brief.md +++ /dev/null @@ -1,77 +0,0 @@ -# 地图列表 UX Resilience 产品 Brief - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-resilience` -- 角色:P 组产品 agent -- 对应 feature:`map-feed-001` - -## 问题 - -地图信息列表已经从紧凑卡片调整为铺满 tabBar 上方的抽屉,并把任务卡改成“标题后跟分类/状态标签、正文摘要、底部统计和时间距离”的结构。这个方向解决了信息密度不足的问题,但也引入了新的布局稳定性风险:长标题、长正文、带图卡片、底部统计和右侧时间距离可能互相挤压。 - -当前 WeChat DevTools service port 9420 仍处于 blocked 状态,真实 UI smoke 暂时无法稳定执行。因此本轮目标不是宣称视觉通过,而是在真实 UI 验证恢复前,先把地图列表布局的结构和样式防回归口径固定下来。 - -## 用户价值 - -用户打开列表抽屉时,需要快速判断每条附近信息的标题、类型、状态、正文线索、可信统计、发布时间和距离。静态 UX resilience 检查的价值是尽早发现会破坏这些核心阅读路径的改动,避免因为一次样式调整让卡片在边界内容下变得难以扫读、遮挡或错位。 - -## 范围内 - -- 地图列表抽屉的高度、滚动区域和 tabBar 上方空间关系。 -- 任务卡当前结构:可选缩略图、标题、分类/方向/状态标签、正文摘要、详情入口、底部确认/过时统计、发布时间和距离。 -- 针对长标题、长正文、带图卡片和底部统计挤压的静态检查口径。 -- 明确哪些样式规则应被视为防回归约束,例如 `minmax(0, 1fr)`、`min-width: 0`、`flex-wrap`、正文行数限制、固定缩略图尺寸、footer 右侧省略。 -- 明确 P 组产物只定义可检查口径,不替代设计验收、QA smoke 或真机验证。 - -## 非目标 - -- 不修改业务代码、WXML、WXSS 或运行时逻辑。 -- 不新增用户可见功能;静态检查脚本只能作为结构/样式防回归 guard,不能替代真实视觉验收。 -- 不证明地图列表在 WeChat DevTools、真机或所有机型上已经通过。 -- 不重新定义地图列表视觉风格、配色、文案或信息架构。 -- 不覆盖发布、详情、评论、定位授权、云端数据或跨用户图片链路。 - -## 关键 UX 风险 - -- 长标题风险:标题与分类/状态标签同排展示时,如果标题不换行或没有断词策略,标签和详情入口可能被挤出可视区域。 -- 长正文风险:正文摘要如果不限制行数,单张卡片会吞掉抽屉空间,降低列表可扫读性。 -- 带图卡片风险:缩略图加入后,文字列和详情入口列必须保持稳定;图片不能压缩标题,也不能导致按钮错位。 -- 底部统计风险:`确认:数量` 和 `过时:数量` 增长后,左侧统计和右侧时间距离可能争抢宽度。 -- 时间距离风险:右侧 `createdText · distanceText` 应能省略,不能覆盖左侧统计,也不能换行后造成 footer 高度异常。 -- 抽屉滚动风险:抽屉铺满后,header、筛选条和列表滚动区必须形成稳定纵向布局,不能让列表内容被 tabBar 遮挡。 -- 空状态风险:无数据时仍要保留抽屉结构,避免空态面板高度或边距破坏整体空间。 - -## 静态检查成功标准 - -- WXML 中列表抽屉仍包含 `drawer-head`、`filter-bar`、`post-list` 和 `post-card`,且任务卡保持标题、标签、正文、footer 统计和详情入口的层级关系。 -- `post-card` 内的 `post-main` 使用稳定列布局;无图卡片保留正文列和固定详情入口列,带图卡片保留固定缩略图列、正文列和固定详情入口列。 -- 标题区域支持换行和断词;分类、方向、状态标签允许换行,不依赖单行硬撑。 -- 正文摘要有明确行数上限和溢出处理,长正文不会无限拉高单卡。 -- 缩略图有固定宽高和圆角,带图卡片不会因为图片加载前后改变布局骨架。 -- footer 使用左右分区:左侧统计可换行,右侧时间距离可省略;二者都设置了防挤压所需的最小宽度或溢出规则。 -- 抽屉主体为纵向 flex 布局,`post-list` 占据剩余空间并可滚动,避免列表内容落到 tabBar 下方。 -- 检查失败时应指向具体结构或样式规则缺失,而不是只报告“文件存在”或“选择器存在”。 - -## 仍需真机/DevTools 验证项 - -- DevTools service port 9420 恢复后,在常见模拟器宽度下打开地图列表抽屉并截图确认。 -- 真机或 DevTools 中验证长标题、长正文、带图、无图、stale、resolved、expired、lost/found 等组合卡片。 -- 验证底部统计数字较大时,左侧统计和右侧时间距离仍可读且不重叠。 -- 验证抽屉底部与 tabBar、安全区的关系,确认最后一张卡片不被遮挡。 -- 验证筛选切换、空状态、点击详情入口、点击整卡聚焦任务的真实交互。 -- 验证地图原生层既有 `WAServiceMainContext timeout` 是否影响抽屉打开、滚动和详情跳转。 - -## 如何避免误判为视觉通过 - -- 静态检查只能说明结构和关键样式规则仍在,不能说明实际渲染、字体度量、原生 map 层叠、安全区或机型差异通过。 -- 通过 `node`、JSON、WXML/WXSS 编译或未来静态 resilience 检查,都不得写成“地图列表 UI 已通过真机验收”。 -- 任何 `passed` 结论必须包含真实运行环境、机型或 DevTools 版本、操作步骤、实际结果和可复核证据。 -- 如果 9420 仍 blocked,应记录为 blocked;不能用截图缺失的静态检查结果替代 smoke evidence。 -- 产品结论应使用“降低结构/样式回归风险”,不要使用“证明视觉正确”“确认用户旅程通过”等措辞。 - -## 下一步建议 - -- D 组或开发 agent 可在不依赖 DevTools 的前提下新增静态检查,覆盖本 brief 的 WXML/WXSS 结构和样式口径。 -- 静态检查应优先构造高风险内容样例的约束,而不是只检查选择器存在。 -- 9420 恢复后,按现有手测 runbook 执行地图列表最小 smoke,并补充脱敏截图或录屏证据。 -- 若静态检查发现当前样式缺口,再由设计/开发 agent 在独立任务中做最小代码修复并重新验证。 diff --git a/harness/map-list-summary-guard-checklist.md b/harness/map-list-summary-guard-checklist.md deleted file mode 100644 index 25bef67..0000000 --- a/harness/map-list-summary-guard-checklist.md +++ /dev/null @@ -1,186 +0,0 @@ -# 地图列表 blocked summary guard QA 清单 - -范围:用于 W 组即将新增的 summary guard,检查 ignored local summary 与 ignored local JSON 之间的关键状态不变量。guard 只防止摘要被人工改成 `passed`、丢掉 `map-list-visual-smoke` blocked 结论或改写 evidence 数量;它不代表 WeChat DevTools 或真机地图列表视觉 smoke 已经通过。 - -工作目录:`/tmp/street-tasks-iter-worktrees/map-list-summary-guard` - -重要边界: - -- [ ] 只读取 ignored 的本地结果和本地摘要:`harness/manual-test-results.local*.json`、`harness/manual-test-summary.local*.md`。 -- [ ] 不修改 `harness/manual-test-results.example.json`,不提交 local JSON/local MD,不提交截图、录屏、日志或云端证据。 -- [ ] guard 通过只说明“blocked JSON 与脱敏 summary 的摘要状态一致”;不能写成 DevTools UI passed、真机 passed 或地图列表视觉 passed。 -- [ ] 如果真实 UI 仍未执行,最终报告必须保留 `blocked` 或未验证口径。 - -## 1. 正向生成 blocked JSON 和 summary - -- [ ] 使用 V wrapper 生成同一轮 ignored blocked JSON 与 ignored sanitized summary。 - - ```bash - node scripts/prepare-map-list-blocked-summary.mjs \ - --reason "DevTools service port blocked; map-list visual smoke was not executed." \ - --results-out harness/manual-test-results.local-w-guard.json \ - --summary-out harness/manual-test-summary.local-w-guard.md \ - --force - ``` - - 期望: - - - 输出包含 `Created blocked result and sanitized summary.`。 - - 输出提示 summary 不是 UI passed evidence。 - - `harness/manual-test-results.local-w-guard.json` 和 `harness/manual-test-summary.local-w-guard.md` 都保持 ignored local 文件。 - -- [ ] 可选复核 JSON 和 summary 基线。 - - ```bash - node scripts/check-manual-evidence.mjs harness/manual-test-results.local-w-guard.json - node scripts/check-evidence-hygiene.mjs - rg -n "overallStatus|map-list-visual-smoke|evidenceCount" harness/manual-test-summary.local-w-guard.md - ``` - - 期望:manual evidence 与 evidence hygiene 通过;summary 能看到总体状态、目标 journey 和 evidenceCount 字段。 - -## 2. 运行 summary guard - -- [ ] 新增 guard 后,推荐命令使用 `--results --summary ` 显式绑定同一轮 local 产物。 - - ```bash - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.local-w-guard.json \ - --summary harness/manual-test-summary.local-w-guard.md - ``` - - 期望:命令成功,并输出 `Map-list blocked summary checks passed.`。 - -## 3. 正向不变量 - -guard 必须同时校验 JSON 和 summary,任一不满足都失败。 - -- [ ] JSON 顶层 `summary.overallStatus` 必须等于 `blocked`。 -- [ ] JSON 中 `journeys[].id === "map-list-visual-smoke"` 必须存在且只存在一条。 -- [ ] JSON 中目标 journey 的 `status` 必须等于 `blocked`。 -- [ ] JSON 中全部 journey 的 `status === "passed"` 数量必须为 `0`。 -- [ ] JSON 中目标 journey 的 evidence 数量必须为 `0`,空数组、空字符串或缺省值按实现约定都不得被误算为正证据。 -- [ ] summary 必须包含 `| overallStatus | blocked |`,允许 Markdown 空格差异,但语义必须是 `overallStatus` 对应 `blocked`。 -- [ ] summary 的 `map-list-visual-smoke` journey 行必须存在,且该行 `status` 必须为 `blocked`。 -- [ ] summary 的 `map-list-visual-smoke` journey 行 `evidenceCount` 必须为 `0`。 -- [ ] summary 中不得出现目标 journey 被摘要为 `passed` 的行或等价内容。 - -建议 guard 的错误信息指向具体不变量,例如 `expected JSON overallStatus=blocked`、`missing map-list-visual-smoke summary row`、`expected summary evidenceCount=0`,便于 QA 快速定位是 JSON 还是 summary 被篡改。 - -## 4. 负向样例 - -以下样例只在临时 ignored local 文件上执行。每个样例执行前先从正向产物复制一份,执行后清理,不提交。 - -- [ ] summary 目标行被改成 `passed` 应失败。 - - ```bash - cp harness/manual-test-summary.local-w-guard.md harness/manual-test-summary.local-w-guard-bad-passed.md - perl -0pi -e 's/(\\| map-list-visual-smoke \\|[^\\n]*?\\| )blocked( \\|)/${1}passed${2}/' \ - harness/manual-test-summary.local-w-guard-bad-passed.md - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.local-w-guard.json \ - --summary harness/manual-test-summary.local-w-guard-bad-passed.md - ``` - - 期望:失败;错误说明目标 journey 的 summary status 不能是 `passed`。 - -- [ ] summary 丢掉 `map-list-visual-smoke` 应失败。 - - ```bash - cp harness/manual-test-summary.local-w-guard.md harness/manual-test-summary.local-w-guard-missing.md - perl -ni -e 'print unless /map-list-visual-smoke/' harness/manual-test-summary.local-w-guard-missing.md - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.local-w-guard.json \ - --summary harness/manual-test-summary.local-w-guard-missing.md - ``` - - 期望:失败;错误说明 summary 缺少目标 journey 行。 - -- [ ] summary 的目标 journey `evidenceCount` 改成 `1` 应失败。 - - ```bash - cp harness/manual-test-summary.local-w-guard.md harness/manual-test-summary.local-w-guard-bad-evidence.md - perl -0pi -e 's/(\\| map-list-visual-smoke \\|[^\\n]*?\\| blocked \\|[^\\n]*?\\| )0( \\|)/${1}1${2}/' \ - harness/manual-test-summary.local-w-guard-bad-evidence.md - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.local-w-guard.json \ - --summary harness/manual-test-summary.local-w-guard-bad-evidence.md - ``` - - 期望:失败;错误说明目标 journey 的 summary evidenceCount 必须为 `0`。 - -- [ ] JSON 目标 journey 改成 `passed` 应失败。 - - ```bash - cp harness/manual-test-results.local-w-guard.json harness/manual-test-results.local-w-guard-bad-passed.json - node --input-type=module <<'NODE' - import { readFileSync, writeFileSync } from 'node:fs'; - - const file = 'harness/manual-test-results.local-w-guard-bad-passed.json'; - const results = JSON.parse(readFileSync(file, 'utf8')); - const target = results.journeys.find((journey) => journey.id === 'map-list-visual-smoke'); - - if (!target) throw new Error('missing map-list-visual-smoke'); - target.status = 'passed'; - - writeFileSync(file, `${JSON.stringify(results, null, 2)}\n`); - NODE - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.local-w-guard-bad-passed.json \ - --summary harness/manual-test-summary.local-w-guard.md - ``` - - 期望:失败;错误说明 JSON 目标 journey 必须保持 `blocked`,且全部 passed 数量必须为 `0`。 - -- [ ] 非 local JSON 路径应失败。 - - ```bash - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.example.json \ - --summary harness/manual-test-summary.local-w-guard.md - ``` - - 期望:失败;错误说明 results 必须匹配 `harness/manual-test-results.local*.json`。 - -- [ ] 非 local summary 路径应失败。 - - ```bash - cp harness/manual-test-summary.local-w-guard.md harness/manual-test-summary.md - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.local-w-guard.json \ - --summary harness/manual-test-summary.md - ``` - - 期望:失败;错误说明 summary 必须匹配 `harness/manual-test-summary.local*.md`。执行后删除这个非 local 临时文件,避免误入提交。 - -## 5. 清理与 Git 卫生 - -- [ ] 清理正向和负向 local JSON/local MD。 - - ```bash - rm -f harness/manual-test-results.local-w-guard*.json - rm -f harness/manual-test-summary.local-w-guard*.md - rm -f harness/manual-test-summary.md - ``` - -- [ ] 确认 local evidence 不进入提交。 - - ```bash - git status --short --ignored - ``` - - 期望:可提交改动只包含 W 组预期文件;`harness/manual-test-results.local*.json`、`harness/manual-test-summary.local*.md` 不出现在 staged 或 unstaged 待提交项。如果仍存在,只能显示为 ignored,且提交前应清理。 - -- [ ] guard 自身落地后,至少运行以下基础检查。 - - ```bash - node --check scripts/check-map-list-blocked-summary.mjs - git diff --check - ``` - -## 6. 最终报告口径 - -- [ ] 报告应明确写出:guard 只验证 blocked JSON 与脱敏 summary 的摘要一致性。 -- [ ] 报告不得把 guard 通过写成 DevTools、真机、地图列表 UI 或视觉 smoke `passed`。 -- [ ] 如果真实 UI 没有执行,继续说明长标题、长正文、图片/无图、安全区、原生 map 层、滚动、marker/list/detail 链路仍未被真实观察。 -- [ ] 若 guard 发现 summary 被改成 `passed`、丢掉 `map-list-visual-smoke` 或 evidenceCount 被改大,应报告为摘要一致性失败,不要改写 JSON 或 summary 来凑通过。 diff --git a/harness/map-list-summary-guard-product-brief.md b/harness/map-list-summary-guard-product-brief.md deleted file mode 100644 index 1b68f21..0000000 --- a/harness/map-list-summary-guard-product-brief.md +++ /dev/null @@ -1,81 +0,0 @@ -# 地图列表 Blocked Summary Guard 产品 Brief - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-summary-guard` -- 角色:W 组产品 agent -- 对应 feature:`map-feed-001` - -## 用户问题 - -V 组已经新增 `scripts/prepare-map-list-blocked-summary.mjs`,能把 U 组 ignored blocked local JSON 和 L 组 ignored sanitized summary 串起来,生成更适合评审阅读的 blocked 摘要。 - -评测指出 V 组提升了可读性,但仍存在一个后续风险:ignored local summary 是 Markdown,可能被人工编辑成 `passed`、删除 `blocked` 语义,或让 `evidenceCount` 看起来像真实 UI 证据。这样会把“环境阻塞被合规记录”误导成“地图列表视觉 smoke 已通过”,也会让 blocked JSON 与 summary 的状态不一致。 - -## 产品假设 - -如果为 blocked summary 增加自动守门,检查它与 blocked JSON 的关键状态不变量一致,评审就能信任摘要仍只是 blocked 结论的脱敏转述,而不是被手工改写过的通过证据。 - -W 组的价值不在于扩大手测覆盖,而在于守住证据链路语义:`map-list-visual-smoke=blocked`、`passed=0`、`evidenceCount=0` 必须从 ignored local JSON 一直保留到 ignored local summary。 - -## 范围 - -- 定义 blocked JSON 与 sanitized summary 之间的状态一致性 guard 口径。 -- 要求 guard 能发现 summary 表格中 `map-list-visual-smoke` 被人工改成 `passed` 或 evidenceCount 被改大。 -- 要求 guard 能发现 summary 丢失 `map-list-visual-smoke`、`blocked`、阻塞原因、风险或 follow-up。 -- 要求 guard 能确认 blocked summary 仍表达 `passed=0` 和 `evidenceCount=0`,没有制造真实 UI evidence。 -- 要求 guard 只针对 ignored local 文件:`harness/manual-test-results.local*.json` 与 `harness/manual-test-summary.local*.md`。 - -## 非目标 - -- W 组不执行 WeChat DevTools、DevTools CLI open/preview、真机调试或任何真实 UI smoke。 -- W 组不修改业务 UI、WXML、WXSS、JS、地图列表交互、数据模型或云端能力。 -- W 组不声明地图列表视觉 smoke、DevTools smoke、真机 smoke 或完整用户旅程通过。 -- W 组不把 readiness、static guard、helper 成功、JSON 校验或 summary guard 通过写成视觉 `passed`。 -- W 组不提交 ignored local JSON 或 ignored local summary;guard 只服务本地证据一致性复核。 - -## 与 S/T/U/V/L 的关系 - -| 组别 | 已提供能力 | W 组补齐的守门口径 | -| --- | --- | --- | -| S | 新增 `map-list-visual-smoke` 真实视觉 smoke 证据槽位 | W 组确保 summary 仍承认该视觉 smoke 未执行,不能把槽位摘要成 passed | -| T | 要求手测 JSON 中恰好保留一条 `map-list-visual-smoke` journey | W 组要求 summary 也必须保留这条 journey 的 blocked 结论,不能在摘要层丢失 | -| U | 生成 ignored blocked local JSON,并保持 `map-list-visual-smoke=blocked`、`passed=0`、`evidence=[]` | W 组把这些 JSON 不变量延伸到 summary 守门,防止摘要人工漂移 | -| V | 串联 blocked JSON helper 与 sanitized summary 生成,提升 blocked 结果可读性 | W 组在 V 组可读性之上增加自动 guard,检查 summary 未被改写成通过证据 | -| L | 从 ignored local JSON 生成 ignored local Markdown 摘要,并避免泄露 raw evidence | W 组沿用 L 的脱敏边界,但额外要求状态语义与 blocked JSON 一致 | - -## Summary Guard 通过边界 - -summary guard 可以通过的条件: - -- 输入 JSON 与 summary 都是 ignored local 路径,分别匹配 `harness/manual-test-results.local*.json` 和 `harness/manual-test-summary.local*.md`。 -- JSON 中 `map-list-visual-smoke.status` 为 `blocked`,且 summary 中同一 journey 的状态仍为 `blocked`。 -- JSON 的 passed journey 数量为 `0`,summary 不能出现任何 map list 视觉 smoke `passed` 结论。 -- JSON 中 `map-list-visual-smoke.evidence` 为空数组时,summary 中该 journey 的 `evidenceCount` 必须为 `0`。 -- summary 保留目标 journey 的 blocked 状态和 `evidenceCount=0`,能让评审知道它没有被摘要成真实 UI 通过证据。 - -## Summary Guard 失败边界 - -summary guard 应失败的条件: - -- summary 把 `map-list-visual-smoke` 表格状态写成 `passed`。 -- summary 丢失 `map-list-visual-smoke` 行,或不再能对应 JSON 中的 blocked journey。 -- summary 中 `evidenceCount` 大于 `0`,但 JSON 中 blocked journey 没有真实 evidence。 -- summary 的目标 journey 行不再是 blocked,或 evidenceCount 被改成非零。 -- JSON 与 summary 的分支、commit 或 journey 状态明显不一致,导致摘要不再是该 blocked JSON 的可信转述。 - -核心边界:summary guard 通过只表示“ignored local summary 与 blocked JSON 状态一致”;它不代表地图列表视觉 smoke passed,也不代表业务 UI failed。 - -## 成功标准 - -- 自动守门能阻止 blocked summary 被手动改成视觉通过语义。 -- 自动守门能守住 `map-list-visual-smoke=blocked`、`passed=0`、`evidenceCount=0`。 -- 评审看到 guard 通过时,只能得出“blocked 摘要仍可信且未制造 evidence”的结论。 -- 真实发布判断仍要求 WeChat DevTools UI 或真机执行 S 组定义的地图列表视觉 smoke,并补齐真实观察证据。 -- guard 失败信息应指向具体不变量,例如状态不一致、passed 语义出现、evidenceCount 非零或 blocked 信息缺失。 - -## 下一步 - -- 开发实现一个 summary guard 脚本,读取 ignored blocked JSON 和 ignored summary,检查上述状态不变量。 -- QA 为 guard 准备坏样例:summary 改成 passed、删除 `map-list-visual-smoke`、把 `evidenceCount` 改成非零、或把 JSON 目标 journey 改成 passed。 -- 将 guard 接入 V 组 blocked summary 生成后的本地复核路径,但不要把它接成视觉 smoke 通过条件。 -- DevTools 或真机入口恢复后,仍按 S 组地图列表视觉 smoke 执行真实观察;只有真实 evidence 补齐后,才允许把 journey 改为 `passed` 或 `failed`。 diff --git a/harness/map-list-summary-integrity-checklist.md b/harness/map-list-summary-integrity-checklist.md deleted file mode 100644 index 0ec5449..0000000 --- a/harness/map-list-summary-integrity-checklist.md +++ /dev/null @@ -1,260 +0,0 @@ -# 地图列表 blocked summary integrity guard QA 清单 - -范围:用于 X 组增强 `scripts/check-map-list-blocked-summary.mjs` 后的 QA 复核。guard 应继续守住 W 组 blocked 状态不变量,并新增 JSON 与 summary 之间的 branch、commit、目标 journey actual/followUp/blocker 摘要一致性检查。guard 通过只代表 ignored local JSON 与 ignored local summary 的 blocked 摘要一致,不代表 WeChat DevTools 或真机地图列表视觉 smoke 已经通过。 - -工作目录:`/tmp/street-tasks-iter-worktrees/map-list-summary-integrity` - -重要边界: - -- [ ] 只读取 ignored 的本地结果和本地摘要:`harness/manual-test-results.local*.json`、`harness/manual-test-summary.local*.md`。 -- [ ] 不修改 `harness/manual-test-results.example.json`,不提交 local JSON/local MD,不提交截图、录屏、日志或云端证据。 -- [ ] 正向和负向样例都只使用 local 临时文件,执行后清理。 -- [ ] guard 仍是证据一致性检查,不是 UI passed 证据;真实 safe area、原生 map 层、长标题、长正文、图片/无图、滚动和详情跳转仍需 DevTools 或真机观察。 - -## 1. 正向生成与 guard 命令 - -- [ ] 使用 V wrapper 生成同一轮 ignored blocked JSON 和 ignored sanitized summary。 - - ```bash - node scripts/prepare-map-list-blocked-summary.mjs \ - --reason "DevTools service port blocked; map-list visual smoke was not executed." \ - --results-out harness/manual-test-results.local-x-integrity.json \ - --summary-out harness/manual-test-summary.local-x-integrity.md \ - --force - ``` - - 期望: - - - 输出包含 `Created blocked result and sanitized summary.`。 - - 输出提示 summary 不是 UI passed evidence。 - - `harness/manual-test-results.local-x-integrity.json` 和 `harness/manual-test-summary.local-x-integrity.md` 都保持 ignored local 文件。 - -- [ ] 对同一对 local 产物运行增强后的 summary integrity guard。 - - ```bash - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.local-x-integrity.json \ - --summary harness/manual-test-summary.local-x-integrity.md - ``` - - 期望:命令成功,并输出 `Map-list blocked summary checks passed.`。 - -## 2. 保留 W 组不变量 - -guard 必须继续校验以下 W 组不变量,避免增强时放松原有 blocked 门禁。 - -- [ ] JSON 顶层 `summary.overallStatus` 必须等于 `blocked`。 -- [ ] JSON 中 `journeys[].id === "map-list-visual-smoke"` 必须存在且只存在一条。 -- [ ] JSON 中目标 journey 的 `status` 必须等于 `blocked`。 -- [ ] JSON 中全部 journey 的 `status === "passed"` 数量必须为 `0`。 -- [ ] JSON 中目标 journey 的 evidence 数量必须为 `0`。 -- [ ] summary 的 `Summary` 表必须有 `overallStatus=blocked`。 -- [ ] summary 的 `Journeys` 表中 `map-list-visual-smoke` 行必须存在且 `status=blocked`。 -- [ ] summary 的 `map-list-visual-smoke` 行 `evidenceCount` 必须为 `0`。 - -## 3. 新增一致性不变量 - -增强 guard 必须确认 summary 仍来自同一份 blocked JSON,而不是被人工拼接或替换。 - -- [ ] summary `Run` 表里的 `branch` 必须等于 JSON 顶层 `branch`。 -- [ ] summary `Run` 表里的 `commit` 必须等于 JSON 顶层 `commit`。 -- [ ] summary `Journeys` 表中目标 journey 行的 `actual` 必须包含 JSON 目标 `actual` 中的关键 blocked reason;至少要能发现该 cell 被替换为 `unrelated text`。 -- [ ] summary 目标 journey 行的 `followUp` 必须包含 JSON 目标 `followUp` 的关键短语,例如真实 JSON 中关于打开 DevTools 或真机、重新运行 `map-list-visual-smoke` 的短语。 -- [ ] summary 目标 journey 行的 `blocker` cell 必须非空;当 JSON 用 `risks` 承载 blocker/risk 摘要时,summary 的 `blocker` cell 也必须保留可读风险摘要。 -- [ ] 错误信息应说明失败字段和来源,例如 `branch mismatch`、`commit mismatch`、`actual missing blocked reason`、`followUp missing key phrase`、`blocker cell must not be empty`。 - -建议 QA 先人工查看结构: - -```bash -rg -n "## Run|## Journeys|branch|commit|map-list-visual-smoke" \ - harness/manual-test-summary.local-x-integrity.md -``` - -## 4. 负向样例 - -以下样例都应失败。每个样例执行前从正向产物复制临时 local 文件,执行后清理,不提交。 - -- [ ] summary branch 改错应失败。 - - ```bash - cp harness/manual-test-summary.local-x-integrity.md \ - harness/manual-test-summary.local-x-integrity-bad-branch.md - perl -0pi -e 's/(\\| branch \\| )([^|]+)( \\|)/${1}unrelated-branch${3}/' \ - harness/manual-test-summary.local-x-integrity-bad-branch.md - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.local-x-integrity.json \ - --summary harness/manual-test-summary.local-x-integrity-bad-branch.md - ``` - - 期望:失败;错误说明 summary `branch` 与 JSON `branch` 不一致。 - -- [ ] summary commit 改错应失败。 - - ```bash - cp harness/manual-test-summary.local-x-integrity.md \ - harness/manual-test-summary.local-x-integrity-bad-commit.md - perl -0pi -e 's/(\\| commit \\| )([^|]+)( \\|)/${1}badc0de${3}/' \ - harness/manual-test-summary.local-x-integrity-bad-commit.md - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.local-x-integrity.json \ - --summary harness/manual-test-summary.local-x-integrity-bad-commit.md - ``` - - 期望:失败;错误说明 summary `commit` 与 JSON `commit` 不一致。 - -- [ ] summary actual 替换为 unrelated text 应失败。 - - ```bash - cp harness/manual-test-summary.local-x-integrity.md \ - harness/manual-test-summary.local-x-integrity-bad-actual.md - node --input-type=module <<'NODE' - import { readFileSync, writeFileSync } from 'node:fs'; - - const file = 'harness/manual-test-summary.local-x-integrity-bad-actual.md'; - const markdown = readFileSync(file, 'utf8'); - const lines = markdown.split('\n').map((line) => { - if (!line.startsWith('| map-list-visual-smoke |')) return line; - const cells = line.split('|'); - cells[4] = ' unrelated text '; - return cells.join('|'); - }); - - writeFileSync(file, lines.join('\n')); - NODE - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.local-x-integrity.json \ - --summary harness/manual-test-summary.local-x-integrity-bad-actual.md - ``` - - 期望:失败;错误说明目标 journey actual 缺少 JSON blocked reason 的关键内容。 - -- [ ] summary followUp 清空应失败。 - - ```bash - cp harness/manual-test-summary.local-x-integrity.md \ - harness/manual-test-summary.local-x-integrity-empty-followup.md - node --input-type=module <<'NODE' - import { readFileSync, writeFileSync } from 'node:fs'; - - const file = 'harness/manual-test-summary.local-x-integrity-empty-followup.md'; - const markdown = readFileSync(file, 'utf8'); - const lines = markdown.split('\n').map((line) => { - if (!line.startsWith('| map-list-visual-smoke |')) return line; - const cells = line.split('|'); - cells[7] = ' - '; - return cells.join('|'); - }); - - writeFileSync(file, lines.join('\n')); - NODE - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.local-x-integrity.json \ - --summary harness/manual-test-summary.local-x-integrity-empty-followup.md - ``` - - 期望:失败;错误说明目标 journey followUp 缺少 JSON followUp 的关键短语。 - -- [ ] summary followUp 替换为 unrelated text 应失败。 - - ```bash - cp harness/manual-test-summary.local-x-integrity.md \ - harness/manual-test-summary.local-x-integrity-bad-followup.md - node --input-type=module <<'NODE' - import { readFileSync, writeFileSync } from 'node:fs'; - - const file = 'harness/manual-test-summary.local-x-integrity-bad-followup.md'; - const markdown = readFileSync(file, 'utf8'); - const lines = markdown.split('\n').map((line) => { - if (!line.startsWith('| map-list-visual-smoke |')) return line; - const cells = line.split('|'); - cells[7] = ' unrelated text '; - return cells.join('|'); - }); - - writeFileSync(file, lines.join('\n')); - NODE - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.local-x-integrity.json \ - --summary harness/manual-test-summary.local-x-integrity-bad-followup.md - ``` - - 期望:失败;错误说明目标 journey followUp 缺少 JSON followUp 的关键短语。 - -- [ ] summary blocker 清空应失败。 - - ```bash - cp harness/manual-test-summary.local-x-integrity.md \ - harness/manual-test-summary.local-x-integrity-empty-blocker.md - node --input-type=module <<'NODE' - import { readFileSync, writeFileSync } from 'node:fs'; - - const file = 'harness/manual-test-summary.local-x-integrity-empty-blocker.md'; - const markdown = readFileSync(file, 'utf8'); - const lines = markdown.split('\n').map((line) => { - if (!line.startsWith('| map-list-visual-smoke |')) return line; - const cells = line.split('|'); - cells[6] = ' - '; - return cells.join('|'); - }); - - writeFileSync(file, lines.join('\n')); - NODE - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.local-x-integrity.json \ - --summary harness/manual-test-summary.local-x-integrity-empty-blocker.md - ``` - - 期望:失败;错误说明目标 journey blocker/risk cell 不应为空。 - -- [ ] 非 local JSON 路径应失败。 - - ```bash - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.example.json \ - --summary harness/manual-test-summary.local-x-integrity.md - ``` - - 期望:失败;错误说明 results 必须匹配 `harness/manual-test-results.local*.json`。 - -- [ ] 非 local summary 路径应失败。 - - ```bash - cp harness/manual-test-summary.local-x-integrity.md harness/manual-test-summary.md - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.local-x-integrity.json \ - --summary harness/manual-test-summary.md - ``` - - 期望:失败;错误说明 summary 必须匹配 `harness/manual-test-summary.local*.md`。执行后删除这个非 local 临时文件,避免误入提交。 - -## 5. 清理与 Git 卫生 - -- [ ] 清理正向和负向 local JSON/local MD。 - - ```bash - rm -f harness/manual-test-results.local-x-integrity*.json - rm -f harness/manual-test-summary.local-x-integrity*.md - rm -f harness/manual-test-summary.md - ``` - -- [ ] 确认 local evidence 不进入提交。 - - ```bash - git status --short --ignored - ``` - - 期望:可提交改动只包含 X 组预期文件;`harness/manual-test-results.local*.json`、`harness/manual-test-summary.local*.md` 不出现在 staged 或 unstaged 待提交项。如果仍存在,只能显示为 ignored,且提交前应清理。 - -- [ ] guard 自身落地后,至少运行以下基础检查。 - - ```bash - node --check scripts/check-map-list-blocked-summary.mjs - git diff --check - ``` - -## 6. 最终报告口径 - -- [ ] 报告应明确写出:增强 guard 验证的是 blocked local JSON 与 sanitized local summary 的状态和内容摘要一致性。 -- [ ] 报告应列出正向命令、负向样例覆盖项、清理结果和仍未执行的 UI 验收。 -- [ ] 报告不得把 guard 通过写成 DevTools UI passed、真机 passed、地图列表视觉 smoke passed 或发布准入。 -- [ ] 如果真实 UI 没有执行,继续说明长标题、长正文、图片/无图、安全区、原生 map 层、滚动、marker/list/detail 链路仍未被真实观察。 diff --git a/harness/map-list-summary-integrity-product-brief.md b/harness/map-list-summary-integrity-product-brief.md deleted file mode 100644 index 640de01..0000000 --- a/harness/map-list-summary-integrity-product-brief.md +++ /dev/null @@ -1,82 +0,0 @@ -# 地图列表 Blocked Summary 同源完整性产品 Brief - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-summary-integrity` -- 角色:X 组产品 agent -- 对应 feature:`map-feed-001` - -## 用户问题 - -W 组新增 `scripts/check-map-list-blocked-summary.mjs` 后,已经守住 ignored blocked JSON 与 ignored summary 的关键状态不变量:`summary.overallStatus=blocked`、`map-list-visual-smoke=blocked`、`passed=0`,以及目标 journey 的 `evidenceCount=0`。 - -评测指出剩余缺口是“可信转述”的完整性还不够严格:summary 可能保留 blocked 状态和空 evidence 数量,却把 `branch`、`commit`、blocked reason 或 `followUp` 改成另一份结果的内容。这样会让评审看到一份看似合规的 Markdown,但无法确认它确实来自同一份 blocked JSON。 - -## 产品假设 - -如果 guard 进一步比对 summary 与 JSON 的 metadata、blocked reason 和 `followUp`,并要求这些字段同源完整,评审就可以把 ignored summary 当作同一份 blocked JSON 的可信转述。 - -X 组的价值只在于补齐证据链路的同源判断:确认 summary 没有跨 run、跨 commit、跨 blocker 或跨 follow-up 拼接。X 不执行 WeChat DevTools/真机,不修改业务 UI,不声明地图列表视觉 smoke 通过;只进一步确认 ignored summary 是同一份 blocked JSON 的可信转述。 - -## 范围 - -- 定义 blocked JSON 与 ignored summary 之间的 metadata 同源口径,至少覆盖 `branch`、`commit`,并保留 `testedAt`、`tester` 的可追踪关系。 -- 定义 blocked reason 同源口径,要求 summary 的 `blocker` 信息来自 JSON 中 `map-list-visual-smoke` 的 blocker 或 risks,不允许改写成泛化通过话术。 -- 定义 `followUp` 同源口径,要求 summary 的 `followUp` 与 JSON 目标 journey 的下一步一致,不允许替换成无关发布建议。 -- 要求同源完整性建立在 W 组已守住的 blocked 状态、`passed=0` 和 `evidenceCount=0` 之上。 -- 只面向 ignored local 文件:`harness/manual-test-results.local*.json` 与 `harness/manual-test-summary.local*.md`。 - -## 非目标 - -- X 组不执行 WeChat DevTools、DevTools CLI open/preview、真机调试或任何真实 UI smoke。 -- X 组不修改业务 UI、WXML、WXSS、JS、地图列表交互、数据模型或云端能力。 -- X 组不声明地图列表视觉 smoke、DevTools smoke、真机 smoke 或完整用户旅程通过。 -- X 组不把 readiness、static guard、helper 成功、JSON 校验、summary 生成或同源 guard 通过写成视觉 `passed`。 -- X 组不提交 ignored local JSON 或 ignored local summary;本 brief 只定义产品口径和验收边界。 - -## 与 U/V/W/L 的关系 - -| 组别 | 已提供能力 | X 组补齐的产品口径 | -| --- | --- | --- | -| U | 生成 ignored blocked local JSON,写入当前 `branch`、`commit`,并让 `map-list-visual-smoke` 保持 blocked、空 evidence 和明确 follow-up | X 组要求 summary 的 run metadata 与这份 JSON 同源,不能换成别的分支、commit 或人工摘要 | -| V | 串联 blocked JSON helper 与 sanitized summary 生成,让评审更容易阅读 blocked 结论 | X 组要求这份可读摘要不仅状态正确,还必须完整转述同一份 JSON 的 blocker 与下一步 | -| W | 新增 blocked summary guard,守住 `overallStatus=blocked`、`map-list-visual-smoke=blocked`、`passed=0`、`evidenceCount=0` | X 组把 guard 的目标从状态一致性推进到同源完整性,补上 metadata、reason、`followUp` 比对 | -| L | 从 ignored local JSON 生成 ignored local Markdown 摘要,并只展示 evidence 数量,不暴露 raw evidence | X 组沿用 L 的脱敏边界,但要求脱敏后的字段仍能追溯回同一份 JSON | - -## Metadata/Reason/FollowUp 通过边界 - -同源完整性可以通过的条件: - -- JSON 与 summary 都是 ignored local 路径,并且已满足 W 组 blocked 状态不变量。 -- summary `## Run` 中的 `branch` 与 JSON 顶层 `branch` 一致,`commit` 与 JSON 顶层 `commit` 一致。 -- 如果 summary 展示 `testedAt` 或 `tester`,它们必须来自 JSON 顶层同名字段;缺失时应按既有生成器的空值规则处理,而不是填入人工猜测值。 -- summary `map-list-visual-smoke` 行的 `blocker` 必须来自 JSON 目标 journey 的 blocker 或 risks;文本可接受 Markdown 转义差异,但不能改变阻塞原因的实质含义。 -- summary `map-list-visual-smoke` 行的 `followUp` 必须来自 JSON 目标 journey 的 `followUp`,并继续指向恢复 DevTools/真机后重跑真实视觉 smoke。 -- summary 仍然只证明 blocked JSON 被可信转述,不增加任何真实 UI evidence。 - -## Metadata/Reason/FollowUp 失败边界 - -同源完整性应失败的条件: - -- summary 的 `branch` 或 `commit` 与 JSON 顶层字段不一致,或 summary 缺少这些可追踪 metadata。 -- summary 的 `blocker` 丢失、被清空,或被改成“已验证”“可发布”“无阻塞”等削弱 blocked 事实的文字。 -- summary 的 `blocker` 来自另一份 run、另一条 journey,或无法对应 JSON 中 `map-list-visual-smoke` 的 blocker/risks。 -- summary 的 `followUp` 丢失、被清空,或不再要求恢复 DevTools/真机后重跑 `map-list-visual-smoke`。 -- summary 通过人工拼接保留了 `blocked` 和 `evidenceCount=0`,但 metadata、reason、`followUp` 任一项无法证明来自同一份 blocked JSON。 - -核心边界:同源 guard 通过只表示“ignored summary 是同一份 blocked JSON 的可信脱敏转述”;它不是地图列表视觉 smoke passed,也不是业务 UI failed。 - -## 成功标准 - -- guard 能在状态一致之外,发现 summary 与 JSON 的 `branch`、`commit` 不一致。 -- guard 能发现 summary 丢失或改写 `map-list-visual-smoke` 的 blocked reason。 -- guard 能发现 summary 丢失或替换 `map-list-visual-smoke` 的 `followUp`。 -- 评审看到 guard 通过时,只能得出“blocked 摘要同源可信且未制造 evidence”的结论。 -- 真实发布判断仍要求 WeChat DevTools UI 或真机执行地图列表视觉 smoke,并补齐真实观察证据。 - -## 下一步 - -- 开发在 `scripts/check-map-list-blocked-summary.mjs` 中补齐 summary 与 JSON 的 run metadata 比对。 -- 开发补齐 `map-list-visual-smoke` 行 `blocker` 与 JSON blocker/risks 的同源比对,允许 Markdown 转义差异但不允许语义替换。 -- 开发补齐 `map-list-visual-smoke` 行 `followUp` 与 JSON `followUp` 的同源比对。 -- QA 准备坏样例:换 branch、换 commit、改 blocker、删 blocker、改 `followUp`、删 `followUp`,确认 guard 必须失败。 -- DevTools 或真机入口恢复后,仍按 S 组地图列表视觉 smoke 执行真实观察;只有真实 evidence 补齐后,才允许把 journey 改为 `passed` 或 `failed`。 diff --git a/harness/map-list-summary-postedit-guard-checklist.md b/harness/map-list-summary-postedit-guard-checklist.md deleted file mode 100644 index b7b5541..0000000 --- a/harness/map-list-summary-postedit-guard-checklist.md +++ /dev/null @@ -1,127 +0,0 @@ -# 地图列表 blocked summary post-edit guard QA 清单 - -范围:用于 Z 组验证 blocked JSON 和 ignored local summary 生成后,如果执行者又手工编辑了 summary,必须重新运行 `scripts/check-map-list-blocked-summary.mjs`。本清单只验证生成后编辑风险、guard 复跑要求和报告口径;它不代表 WeChat DevTools 或真机地图列表视觉 smoke 已经执行或通过。 - -工作目录:`/tmp/street-tasks-iter-worktrees/map-list-summary-postedit-guard` - -重要边界: - -- [ ] 只使用 ignored local 产物:`harness/manual-test-results.local*.json`、`harness/manual-test-summary.local*.md`。 -- [ ] 不修改 `harness/manual-test-results.example.json`,不提交 local JSON/local MD,不提交截图、录屏、日志或云端证据。 -- [ ] wrapper 生成时自动跑 guard,只能证明当时生成出的 JSON 与 summary 一致;若 summary 后续被手工改动,之前的 guard 结果立即失效。 -- [ ] guard 通过不是 UI passed 证据,不得写成 DevTools passed、真机 passed、地图列表视觉 smoke passed 或发布准入。 - -## 1. 正向:生成后立即通过 guard - -- [ ] 运行 guarded wrapper,生成同一轮 ignored blocked JSON 和 ignored local summary。 - - ```bash - node scripts/prepare-map-list-blocked-summary.mjs \ - --reason "DevTools service port blocked; map-list visual smoke was not executed." \ - --results-out harness/manual-test-results.local-z-postedit.json \ - --summary-out harness/manual-test-summary.local-z-postedit.md \ - --force - ``` - - 期望: - - - 输出包含 `Map-list blocked evidence draft created.`。 - - 输出包含 `Manual summary draft created.`。 - - 输出包含 `Map-list blocked summary checks passed.`。 - - 输出包含 `Blocked summary guard passed.`。 - - 输出包含 `Post-edit rerun guard`,并打印针对当前 JSON/MD 路径的 `node scripts/check-map-list-blocked-summary.mjs --results ... --summary ...` 命令。 - - 输出继续提醒 summary 不是 UI passed evidence。 - -- [ ] 对刚生成的同一对 local 产物手动再跑 guard。 - - ```bash - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.local-z-postedit.json \ - --summary harness/manual-test-summary.local-z-postedit.md - ``` - - 期望:输出 `Map-list blocked summary checks passed.`;这只说明当前这一刻的 blocked JSON 与 summary 仍保持状态和同源一致。 - -## 2. 负向:生成后篡改 summary 必须失败 - -- [ ] 复制正向 summary,模拟执行者在 wrapper 成功后把 `map-list-visual-smoke` 行改成 `passed`。 - - ```bash - cp harness/manual-test-summary.local-z-postedit.md \ - harness/manual-test-summary.local-z-postedit-bad-status.md - perl -0pi -e 's/(\\| map-list-visual-smoke \\|[^\\n]*?\\| )blocked( \\|)/${1}passed${2}/' \ - harness/manual-test-summary.local-z-postedit-bad-status.md - ``` - -- [ ] 对篡改后的 summary 重新运行 guard。 - - ```bash - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.local-z-postedit.json \ - --summary harness/manual-test-summary.local-z-postedit-bad-status.md - ``` - - 期望:命令失败,错误说明 `map-list-visual-smoke` summary row 不能是 `passed` 或必须保持 `blocked`。 - -- [ ] 复制正向 summary,模拟执行者在 wrapper 成功后只改 `commit`。 - - ```bash - cp harness/manual-test-summary.local-z-postedit.md \ - harness/manual-test-summary.local-z-postedit-bad-commit.md - perl -0pi -e 's/(\\| commit \\| )([^|]+)( \\|)/${1}badc0de${3}/' \ - harness/manual-test-summary.local-z-postedit-bad-commit.md - ``` - -- [ ] 对 commit 被改坏的 summary 重新运行 guard。 - - ```bash - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.local-z-postedit.json \ - --summary harness/manual-test-summary.local-z-postedit-bad-commit.md - ``` - - 期望:命令失败,错误说明 summary `commit` 必须匹配 blocked results JSON 的 `commit`。 - -## 3. 正向:后编辑后的修复路径 - -- [ ] 如果 summary 已经被手工改动,不沿用 wrapper 成功时的旧输出作为证据;必须选择以下任一修复方式。 -- [ ] 方式一:放弃手工改动,用 `--force` 重新生成同一对 local JSON/MD,再确认 wrapper 输出里 guard 通过。 -- [ ] 方式二:保留手工改动,但在报告前重新运行 `scripts/check-map-list-blocked-summary.mjs --results --summary `,并只在 guard 重新通过后引用该 summary。 -- [ ] 若后编辑是为了更正 blocker/followUp/actual,可优先修改源 local JSON 后重新生成 summary,避免 summary 与 JSON 失去同源关系。 - -## 4. 不能证明 UI 通过 - -- [ ] 报告必须明确:`Blocked summary guard passed.` 只代表 ignored blocked JSON 与 ignored local summary 的状态和同源完整性通过 guard。 -- [ ] 不得把 `overallStatus=blocked`、`map-list-visual-smoke=blocked` 或 `passed=0` 包装成“地图列表视觉 smoke 已通过”。 -- [ ] 不得把自动脚本通过写成 WeChat DevTools UI 通过、真机通过、用户任务链路通过或可发布准入。 -- [ ] 如果真实 UI 没有执行,报告仍应保留阻塞原因,例如 DevTools service port blocked 或无真机访问。 - -## 5. 未验证项 - -以下内容不由本清单证明,除非后续有真实 DevTools 或真机证据: - -- [ ] 地图列表抽屉在真实小程序环境中的 safe area、底部 tabBar 遮挡和滚动体验。 -- [ ] 长标题、长正文、图片/无图任务卡在真实 cover-view/map 层上的截断和布局表现。 -- [ ] marker 点击、列表点击、详情跳转和返回后的选中态保持。 -- [ ] 定位授权允许/拒绝后的地图中心、任务距离和查找附近任务行为。 -- [ ] 真实截图、录屏、日志、云端记录或任务 id 的脱敏审查。 - -## 6. 清理与报告 - -- [ ] 清理本清单生成的 ignored local 产物。 - - ```bash - rm -f harness/manual-test-results.local-z-postedit*.json - rm -f harness/manual-test-summary.local-z-postedit*.md - ``` - -- [ ] 确认 local evidence 不进入提交范围。 - - ```bash - git status --short --ignored - ``` - - 期望:可提交改动不包含 `harness/manual-test-results.local*.json` 或 `harness/manual-test-summary.local*.md`。 - -- [ ] 汇报时列出正向 wrapper 输出、手工篡改后 guard 失败输出、清理结果和仍未执行的 UI/真机验证项。 -- [ ] 若 Z 组还修改 wrapper 输出,主 agent 应额外确认输出中明确提示“如果 local JSON 或 local MD 生成后被编辑,必须重新运行 guard”。 diff --git a/harness/map-list-summary-postedit-guard-product-brief.md b/harness/map-list-summary-postedit-guard-product-brief.md deleted file mode 100644 index b40b5cd..0000000 --- a/harness/map-list-summary-postedit-guard-product-brief.md +++ /dev/null @@ -1,26 +0,0 @@ -# 地图列表阻塞摘要后编辑保护产品 Brief - -## 背景 - -Y 轮已经把 `scripts/check-map-list-blocked-summary.mjs` 接入 `scripts/prepare-map-list-blocked-summary.mjs`,生成本地阻塞 JSON 和脱敏摘要时会自动跑 guard。这能防止刚生成的 `harness/*.local*.json` 与 `harness/*.local*.md` 在状态、提交、阻塞原因和后续动作上脱节。 - -剩余风险在生成之后:如果评审前有人手工编辑了本地 JSON 或 Markdown 摘要,wrapper 当时跑过的 guard 不再覆盖最新文件内容。此时必须重新运行 summary guard,才能把这组本地阻塞材料作为“DevTools 端口阻塞”的证据链引用。 - -## 产品目标 - -- 让阻塞摘要的可信边界更明确:自动生成后的内容可引用,手工编辑后的内容必须重新校验。 -- 降低评审误判风险:评审人看到本地阻塞摘要时,能知道它只证明“手测被阻塞且记录完整”,不证明地图列表 UI 已通过。 -- 保持低成本操作:生成后如果需要修正文案,只要求重跑单条 guard 命令,不要求重新执行真实 DevTools 或真机手测。 - -## 验收标准 - -- 产品 brief 明确说明 Y 轮 wrapper 自动跑 guard 的能力边界。 -- 产品 brief 明确说明生成后的 JSON 或 Markdown 只要被手工编辑,就必须重新运行 `node scripts/check-map-list-blocked-summary.mjs --results <本地结果JSON> --summary <本地摘要MD>`。 -- 产品 brief 明确说明重新跑 guard 通过后,本地阻塞证据才可被评审引用。 -- 产品 brief 明确保留证据语义:`map-list-visual-smoke=blocked`、`passed=0`、`evidenceCount=0` 不应被改成通过态。 - -## 非目标 - -- 不把阻塞摘要当作地图列表 UI 通过证据。 -- 不替代 WeChat DevTools 或真机上的真实视觉冒烟测试。 -- 不扩大本轮范围到 guard 校验逻辑、真实手测执行或通过态证据生成;脚本层只允许补充成功后的复跑提示。 diff --git a/harness/map-list-summary-preflight-checklist.md b/harness/map-list-summary-preflight-checklist.md deleted file mode 100644 index 7a04540..0000000 --- a/harness/map-list-summary-preflight-checklist.md +++ /dev/null @@ -1,190 +0,0 @@ -# 地图列表 blocked summary preflight QA 清单 - -范围:用于 AA 组验证一键 preflight 能扫描 `harness/` 下 ignored local blocked summary/result 对,并逐对复跑 `scripts/check-map-list-blocked-summary.mjs`。本清单只验证 blocked evidence/summary 的评审前一致性检查;它不代表 WeChat DevTools 或真机地图列表视觉 smoke 已经执行或通过。 - -工作目录:`/tmp/street-tasks-iter-worktrees/map-list-summary-preflight` - -重要边界: - -- [ ] 只使用 ignored local 产物:`harness/manual-test-results.local*.json`、`harness/manual-test-summary.local*.md`。 -- [ ] 不修改 `harness/manual-test-results.example.json`,不提交 local JSON/local MD,不提交截图、录屏、日志或云端证据。 -- [ ] preflight 只应扫描 local blocked summary/result 对并复跑已有 guard;不应生成真实 UI 通过证据。 -- [ ] preflight 通过不是 UI passed 证据,不得写成 DevTools passed、真机 passed、地图列表视觉 smoke passed 或发布准入。 - -## 1. 基线与命令 - -- [ ] 确认 AGENTS 基线已通过。 - - ```bash - pwd - git log --oneline -5 - bash harness/init.sh - ``` - -- [ ] 确认一键 preflight 脚本语法通过。 - - ```bash - node --check scripts/check-map-list-blocked-summary-preflight.mjs - ``` - -- [ ] 后续所有场景都以该命令为评审前入口。 - - ```bash - node scripts/check-map-list-blocked-summary-preflight.mjs - ``` - -## 2. 无 local summary 时通过 - -- [ ] 清理本轮可能遗留的 local blocked summary/result。 - - ```bash - rm -f harness/manual-test-results.local-aa-preflight*.json - rm -f harness/manual-test-summary.local-aa-preflight*.md - ``` - -- [ ] 在没有匹配 local summary 的情况下运行 preflight。 - - ```bash - node scripts/check-map-list-blocked-summary-preflight.mjs - ``` - - 期望: - - - 命令通过。 - - 输出明确说明没有发现需要检查的 ignored local blocked summary/result 对,或 `checked=0`。 - - 不创建新的 local JSON、local MD、截图、录屏或日志。 - - 不暗示地图列表视觉 smoke 已通过。 - -## 3. 正向:扫描一对 blocked JSON/MD 通过 - -- [ ] 使用 wrapper 生成一对 ignored blocked result 和 ignored local summary。 - - ```bash - node scripts/prepare-map-list-blocked-summary.mjs \ - --reason "DevTools service port blocked; map-list visual smoke was not executed." \ - --results-out harness/manual-test-results.local-aa-preflight.json \ - --summary-out harness/manual-test-summary.local-aa-preflight.md \ - --force - ``` - -- [ ] 运行一键 preflight。 - - ```bash - node scripts/check-map-list-blocked-summary-preflight.mjs - ``` - - 期望: - - - 命令通过。 - - 输出点名被扫描的 `harness/manual-test-summary.local-aa-preflight.md`。 - - 输出能看出对应 result 为 `harness/manual-test-results.local-aa-preflight.json`。 - - 对该 pair 复跑 `scripts/check-map-list-blocked-summary.mjs` 并通过。 - - 输出保留 `blocked` 语义,例如 `overallStatus=blocked`、`map-list-visual-smoke=blocked` 或等价摘要。 - - 不把 `blocked` 结果写成 `passed`。 - -## 4. 负向:summary 缺结果 JSON 必须失败 - -- [ ] 保留 local summary,但删除它对应的 local result JSON。 - - ```bash - rm -f harness/manual-test-results.local-aa-preflight.json - ``` - -- [ ] 运行一键 preflight。 - - ```bash - node scripts/check-map-list-blocked-summary-preflight.mjs - ``` - - 期望: - - - 命令失败。 - - 错误点名缺失的 results JSON 路径,或明确说明 summary 找不到匹配的 blocked results JSON。 - - 不跳过该 summary 后继续报告整体成功。 - - 不生成替代 JSON,也不自动改写 summary。 - -## 5. 负向:summary 被改 passed 必须失败 - -- [ ] 重新生成一对正向 local JSON/MD。 - - ```bash - node scripts/prepare-map-list-blocked-summary.mjs \ - --reason "DevTools service port blocked; map-list visual smoke was not executed." \ - --results-out harness/manual-test-results.local-aa-preflight.json \ - --summary-out harness/manual-test-summary.local-aa-preflight.md \ - --force - ``` - -- [ ] 模拟生成后人工把 `map-list-visual-smoke` 行改成 `passed`。 - - ```bash - perl -0pi -e 's/(\| map-list-visual-smoke \|[^\n]*?\| )blocked( \|)/${1}passed${2}/' \ - harness/manual-test-summary.local-aa-preflight.md - ``` - -- [ ] 运行一键 preflight。 - - ```bash - node scripts/check-map-list-blocked-summary-preflight.mjs - ``` - - 期望: - - - 命令失败。 - - 错误来自或等价于 `scripts/check-map-list-blocked-summary.mjs` 的状态守门。 - - 错误说明 `map-list-visual-smoke` summary row 不能是 `passed`,或必须与 blocked JSON 保持一致。 - - 不允许把其他 local pair 的通过结果掩盖该失败。 - -## 6. 清理 local 产物 - -- [ ] 清理本清单生成的 ignored local 产物。 - - ```bash - rm -f harness/manual-test-results.local-aa-preflight*.json - rm -f harness/manual-test-summary.local-aa-preflight*.md - ``` - -- [ ] 确认 local evidence 没有进入提交范围。 - - ```bash - git status --short --ignored - ``` - - 期望:可提交改动不包含 `harness/manual-test-results.local*.json` 或 `harness/manual-test-summary.local*.md`。 - -## 7. 报告口径不能写 UI passed - -- [ ] 汇报 preflight 结果时只写“blocked summary/result pair guard passed”或“评审前一致性检查通过”。 -- [ ] 不得写“地图列表视觉 smoke passed”“DevTools UI passed”“真机 passed”“用户路径 passed”或“可以发布”。 -- [ ] 若 preflight 失败,报告需包含失败 pair、失败原因和是否已清理 local 产物。 -- [ ] 若没有 local summary,报告需写“无 local blocked summary 需要检查”,而不是“地图列表无问题”。 - -## 8. 未验证项 - -以下内容不由本清单证明,除非后续有真实 DevTools 或真机证据: - -- [ ] 地图列表抽屉在真实小程序环境中的 safe area、底部 tabBar 遮挡和滚动体验。 -- [ ] 长标题、长正文、图片/无图任务卡在真实 cover-view/map 层上的截断和布局表现。 -- [ ] marker 点击、列表点击、详情跳转和返回后的选中态保持。 -- [ ] 定位授权允许/拒绝后的地图中心、任务距离和查找附近任务行为。 -- [ ] 真实截图、录屏、日志、云端记录或任务 id 的脱敏审查。 - -## 9. 主 agent 收尾建议 - -- [ ] 运行语法和核心正负向验证。 - - ```bash - node --check scripts/check-map-list-blocked-summary-preflight.mjs - node scripts/check-map-list-blocked-summary-preflight.mjs - node scripts/prepare-map-list-blocked-summary.mjs \ - --reason "DevTools service port blocked; map-list visual smoke was not executed." \ - --results-out harness/manual-test-results.local-aa-preflight.json \ - --summary-out harness/manual-test-summary.local-aa-preflight.md \ - --force - node scripts/check-map-list-blocked-summary-preflight.mjs - rm -f harness/manual-test-results.local-aa-preflight.json - node scripts/check-map-list-blocked-summary-preflight.mjs - ``` - -- [ ] 恢复正向 pair 后改坏 summary,再确认 preflight 失败。 -- [ ] 清理 local 产物后运行 `git diff --check` 和 `bash harness/init.sh`。 diff --git a/harness/map-list-summary-preflight-product-brief.md b/harness/map-list-summary-preflight-product-brief.md deleted file mode 100644 index 3d885b0..0000000 --- a/harness/map-list-summary-preflight-product-brief.md +++ /dev/null @@ -1,29 +0,0 @@ -# 地图列表 blocked summary preflight 产品 brief - -## 目标 - -AA 轮提供一个评审前一键 preflight:自动扫描 ignored local 的 blocked result / summary 配对,并对每一对复跑 `scripts/check-map-list-blocked-summary.mjs`。 - -它要降低 reviewer 只看到本地 blocked summary、却漏跑单文件 guard 命令的风险。preflight 的职责是把 Z 轮“生成后编辑必须复跑 guard”的流程提醒,提升为评审前可统一执行的主动检查入口。 - -## 用户价值 - -- 对执行者:不用逐个回忆每份 local JSON/Markdown 对应的 guard 命令,评审前跑一个入口即可发现明显不一致。 -- 对 reviewer:看到 blocked summary 前,可以要求先跑 preflight,减少 summary 被后编辑、跨 run 拼接或误改成 passed 后仍被引用的风险。 -- 对项目维护者:blocked 证据链继续保持“本地可生成、可脱敏、可校验、不可冒充 UI passed”的边界,便于长期迭代。 - -## 验收标准 - -- preflight 能在 `harness/` 下识别 ignored local blocked result 与 local summary 配对,例如 `manual-test-results.local*.json` 和 `manual-test-summary.local*.md`。 -- preflight 对每个配对复用现有 blocked summary guard,检查 blocked 状态、`map-list-visual-smoke` 状态、evidence count、Run branch/commit、actual/followUp/blocker 同源内容。 -- 如果 summary 被改成 passed、commit 与 JSON 不一致、目标 journey 丢失或 evidenceCount 非 0,preflight 必须失败,并指出失败的配对。 -- 如果没有可检查的 local 配对,preflight 应给出清晰口径:没有发现可校验的 ignored local blocked summary/result 对,而不是默默通过成 UI 证据。 -- 输出必须明确:preflight 通过只说明 blocked local summary/result 对通过一致性检查,不等于地图列表 UI passed evidence。 -- 验证仍需覆盖 `bash harness/init.sh`、新增脚本语法检查、正向 local 配对、后编辑负向样例、`git diff --check`。 - -## 非目标 - -- 不恢复 WeChat DevTools service port 9420,不处理 CLI open/preview timeout。 -- 不替代 DevTools 或真机地图列表视觉 smoke,不证明长标题、长正文、图片加载、safe area、地图原生层或详情点击已通过。 -- 不提交 ignored local JSON/Markdown 作为正式证据。 -- 不改动地图页 UI、数据模型、发布流程或详情页信任逻辑。 diff --git a/harness/map-list-summary-readiness-preflight-checklist.md b/harness/map-list-summary-readiness-preflight-checklist.md deleted file mode 100644 index 3f4587d..0000000 --- a/harness/map-list-summary-readiness-preflight-checklist.md +++ /dev/null @@ -1,209 +0,0 @@ -# 地图列表 blocked summary readiness preflight QA 清单 - -范围:用于 AB 组验证 AA 轮新增的 `scripts/check-map-list-blocked-summary-preflight.mjs` 是否已经接入更默认的检查入口,降低评审或收尾前忘记复跑 local blocked summary guard 的风险。默认入口可以是 `bash harness/init.sh`、`node --no-warnings scripts/check-devtools-readiness.mjs`,或开发本轮明确接入的同等级入口。 - -本清单只验证 ignored local blocked evidence/summary 的一致性守门是否会被默认入口带起;它不代表 WeChat DevTools 或真机地图列表视觉 smoke 已执行或通过。 - -工作目录:`/tmp/street-tasks-iter-worktrees/map-list-summary-readiness-preflight` - -重要边界: - -- [ ] 只使用 ignored local 产物:`harness/manual-test-results.local*.json`、`harness/manual-test-summary.local*.md`。 -- [ ] 不修改 `harness/manual-test-results.example.json`,不提交 local JSON/local MD,不提交截图、录屏、日志或云端证据。 -- [ ] 默认入口应复用 AA 轮 preflight 或等价逻辑,不应绕过 `scripts/check-map-list-blocked-summary.mjs` 的 blocked summary 同源检查。 -- [ ] 默认入口通过不是 UI passed 证据,不得写成 DevTools passed、真机 passed、地图列表视觉 smoke passed 或发布准入。 - -## 1. 基线与默认入口确认 - -- [ ] 确认 AGENTS 基线已通过。 - - ```bash - pwd - git log --oneline -5 - bash harness/init.sh - ``` - -- [ ] 确认本轮开发实际接入的默认入口。 - - ```bash - git diff -- harness/init.sh scripts/check-devtools-readiness.mjs scripts/check-map-list-blocked-summary-preflight.mjs - ``` - - 期望: - - - 能看出默认入口会运行 `scripts/check-map-list-blocked-summary-preflight.mjs`,或等价地扫描 `harness/manual-test-summary.local*.md` 并逐对运行 blocked summary guard。 - - 入口输出保留“preflight 不是 UI passed evidence”的口径,或在失败时不会让人误以为真实 UI 已通过。 - -- [ ] 后续场景优先使用本轮接入的默认入口验证。 - - ```bash - bash harness/init.sh - node --no-warnings scripts/check-devtools-readiness.mjs - ``` - - 如果本轮只接入其中一个入口,记录实际接入项,并只把该入口作为必须通过/失败的判定对象。 - -## 2. 无 local summary 时默认入口通过 - -- [ ] 清理本轮可能遗留的 local blocked summary/result。 - - ```bash - rm -f harness/manual-test-results.local-ab-readiness*.json - rm -f harness/manual-test-summary.local-ab-readiness*.md - ``` - -- [ ] 运行本轮接入的默认入口。 - - ```bash - bash harness/init.sh - node --no-warnings scripts/check-devtools-readiness.mjs - ``` - - 期望: - - - 已接入 preflight 的默认入口通过。 - - 输出说明没有发现需要检查的 ignored local blocked summary/result 对,或等价地显示 `checked=0` / `nothing checked`。 - - 不创建新的 local JSON、local MD、截图、录屏或日志。 - - 不暗示地图列表视觉 smoke 已通过。 - -## 3. 正向 pair 存在时默认入口通过 - -- [ ] 使用 wrapper 生成一对 ignored blocked result 和 ignored local summary。 - - ```bash - node scripts/prepare-map-list-blocked-summary.mjs \ - --reason "DevTools service port blocked; map-list visual smoke was not executed." \ - --results-out harness/manual-test-results.local-ab-readiness.json \ - --summary-out harness/manual-test-summary.local-ab-readiness.md \ - --force - ``` - -- [ ] 运行本轮接入的默认入口。 - - ```bash - bash harness/init.sh - node --no-warnings scripts/check-devtools-readiness.mjs - ``` - - 期望: - - - 已接入 preflight 的默认入口通过。 - - 输出点名或可追踪到 `harness/manual-test-summary.local-ab-readiness.md` 和 `harness/manual-test-results.local-ab-readiness.json`。 - - 能看出 pair 已复跑 blocked summary guard,且 `map-list-visual-smoke` 保持 `blocked`、`passed=0`、`evidenceCount=0`。 - - 不把 blocked result、blocked summary 或 readiness preflight 写成 UI passed。 - -## 4. 缺 results 时默认入口失败 - -- [ ] 保留 local summary,但删除对应的 local result JSON。 - - ```bash - rm -f harness/manual-test-results.local-ab-readiness.json - ``` - -- [ ] 运行本轮接入的默认入口。 - - ```bash - bash harness/init.sh - node --no-warnings scripts/check-devtools-readiness.mjs - ``` - - 期望: - - - 已接入 preflight 的默认入口失败。 - - 错误点名缺失的 results JSON 路径,或明确说明 summary 找不到匹配的 blocked results JSON。 - - 不跳过该 summary 后继续报告整体成功。 - - 不生成替代 JSON,也不自动改写 summary。 - -## 5. summary 改 passed 时默认入口失败 - -- [ ] 重新生成一对正向 local JSON/MD。 - - ```bash - node scripts/prepare-map-list-blocked-summary.mjs \ - --reason "DevTools service port blocked; map-list visual smoke was not executed." \ - --results-out harness/manual-test-results.local-ab-readiness.json \ - --summary-out harness/manual-test-summary.local-ab-readiness.md \ - --force - ``` - -- [ ] 模拟生成后人工把 `map-list-visual-smoke` 行改成 `passed`。 - - ```bash - perl -0pi -e 's/(\| map-list-visual-smoke \|[^\n]*?\| )blocked( \|)/${1}passed${2}/' \ - harness/manual-test-summary.local-ab-readiness.md - ``` - -- [ ] 运行本轮接入的默认入口。 - - ```bash - bash harness/init.sh - node --no-warnings scripts/check-devtools-readiness.mjs - ``` - - 期望: - - - 已接入 preflight 的默认入口失败。 - - 错误来自或等价于 `scripts/check-map-list-blocked-summary.mjs` 的状态守门。 - - 错误说明 `map-list-visual-smoke` summary row 不能是 `passed`,或必须与 blocked JSON 保持一致。 - - 不允许其他 local pair 的通过结果掩盖该失败。 - -## 6. 清理 local 产物 - -- [ ] 清理本清单生成的 ignored local 产物。 - - ```bash - rm -f harness/manual-test-results.local-ab-readiness*.json - rm -f harness/manual-test-summary.local-ab-readiness*.md - ``` - -- [ ] 确认可提交改动不包含 local evidence。 - - ```bash - git status --short --ignored - ``` - - 期望: - - - 可提交改动不包含 `harness/manual-test-results.local*.json` 或 `harness/manual-test-summary.local*.md`。 - - ignored 列表里若短暂出现本轮 local 产物,收尾前必须清理。 - -## 7. 报告口径不能写 UI passed - -- [ ] 汇报默认入口结果时只写“readiness/default entry 已复跑 blocked summary preflight”或“blocked summary/result pair guard 通过”。 -- [ ] 不得写“地图列表视觉 smoke passed”“DevTools UI passed”“真机 passed”“用户路径 passed”“可发布”或“发布准入通过”。 -- [ ] 如果默认入口失败,报告需包含失败 pair、失败原因、失败入口和是否已清理 local 产物。 -- [ ] 如果没有 local summary,报告需写“无 local blocked summary 需要检查”,而不是“地图列表无问题”。 - -## 8. 未验证项 - -以下内容不由本清单证明,除非后续有真实 DevTools 或真机证据: - -- [ ] 地图列表抽屉在真实小程序环境中的 safe area、底部 tabBar 遮挡和滚动体验。 -- [ ] 长标题、长正文、图片/无图任务卡在真实 cover-view/map 层上的截断和布局表现。 -- [ ] marker 点击、列表点击、详情跳转和返回后的选中态保持。 -- [ ] 定位授权允许/拒绝后的地图中心、任务距离和查找附近任务行为。 -- [ ] 真实截图、录屏、日志、云端记录或任务 id 的脱敏审查。 - -## 9. 主 agent 收尾建议 - -- [ ] 运行语法、默认入口和核心正负向验证。 - - ```bash - node --check scripts/check-map-list-blocked-summary-preflight.mjs - node --check scripts/check-devtools-readiness.mjs - bash harness/init.sh - node --no-warnings scripts/check-devtools-readiness.mjs - node scripts/prepare-map-list-blocked-summary.mjs \ - --reason "DevTools service port blocked; map-list visual smoke was not executed." \ - --results-out harness/manual-test-results.local-ab-readiness.json \ - --summary-out harness/manual-test-summary.local-ab-readiness.md \ - --force - bash harness/init.sh - node --no-warnings scripts/check-devtools-readiness.mjs - rm -f harness/manual-test-results.local-ab-readiness.json - bash harness/init.sh - node --no-warnings scripts/check-devtools-readiness.mjs - ``` - -- [ ] 恢复正向 pair 后改坏 summary,再确认已接入 preflight 的默认入口失败。 -- [ ] 清理 local 产物后运行 `git diff --check` 和 `bash harness/init.sh`。 diff --git a/harness/map-list-summary-readiness-preflight-product-brief.md b/harness/map-list-summary-readiness-preflight-product-brief.md deleted file mode 100644 index 0f5d72e..0000000 --- a/harness/map-list-summary-readiness-preflight-product-brief.md +++ /dev/null @@ -1,37 +0,0 @@ -# AB 轮产品 brief:blocked summary readiness preflight - -## 背景 - -AA 轮已经提供 `scripts/check-map-list-blocked-summary-preflight.mjs`,可以一键扫描 ignored local blocked summary/result 对并逐对复跑 guard。用户评测认为它有效,但风险仍在:评审或交接前需要人主动记得运行这条命令。 - -AB 轮要把这条 preflight 接到更默认的检查入口里,例如 `harness/init.sh` 或 `scripts/check-devtools-readiness.mjs`,让常规启动或 readiness 检查自然覆盖 blocked summary 守门。 - -## 目标 - -- 降低评审前漏跑 blocked summary preflight 的概率。 -- 让默认入口在存在 ignored local blocked summary/result 时自动检查它们是否仍为可信 blocked 证据。 -- 让无 local summary 的干净工作树清晰通过,不给开发者制造额外噪音。 -- 保持报告口径谨慎:preflight 通过只说明 blocked summary/result 的结构与同源关系可信。 - -## 用户价值 - -- 对评审者:打开默认检查结果即可知道本地 blocked summary 是否被后编辑、缺 result、或错误标成 passed。 -- 对迭代 agent:不用额外记忆一条单独命令,也能在常规检查中发现 blocked evidence 链路破损。 -- 对项目维护者:继续保持 ignored local evidence 不入库,同时让它们在本地被引用前更容易被守住。 - -## 验收标准 - -- 默认入口会运行 blocked summary preflight,并在输出中能看出该检查已执行。 -- 当没有 `harness/manual-test-summary.local*.md` 时,默认入口应清晰通过,并提示没有 local blocked summary 需要检查。 -- 当存在 matching `harness/manual-test-summary.local*.md` 与 `harness/manual-test-results.local*.json` 时,默认入口应逐对复用现有 preflight/guard 逻辑。 -- 当 summary 缺少对应 results JSON、summary 被改成 `passed`、或 summary 与 JSON 的 branch/commit/blocker/followUp 不一致时,默认入口应失败。 -- 默认入口输出必须保留边界说明:该 preflight 不是 UI passed evidence,不能替代真实 WeChat DevTools 或真机视觉 smoke。 -- `bash harness/init.sh`、JSON 检查、harness 检查仍应通过;如接入 `scripts/check-devtools-readiness.mjs`,该 readiness 检查也应通过。 - -## 非目标 - -- 不恢复 WeChat DevTools 9420 服务端口,也不声明 DevTools CLI 可用。 -- 不把 ignored local blocked summary/result 提交入库。 -- 不证明地图列表 UI 已通过、DevTools 已通过或真机已通过。 -- 不修改地图页、详情页、发布页等用户界面行为。 -- 不取代真实的 map-list visual smoke;它仍然需要 WeChat DevTools 或真机观察后单独记录。 diff --git a/harness/map-list-summary-wrapper-guarded-checklist.md b/harness/map-list-summary-wrapper-guarded-checklist.md deleted file mode 100644 index 03c63a1..0000000 --- a/harness/map-list-summary-wrapper-guarded-checklist.md +++ /dev/null @@ -1,162 +0,0 @@ -# 地图列表 blocked summary wrapper guarded QA 清单 - -范围:用于 Y 组验证 `scripts/prepare-map-list-blocked-summary.mjs` 在生成 ignored blocked JSON 和 ignored local summary 后,会默认串联运行 X guard `scripts/check-map-list-blocked-summary.mjs`。本清单只验证 wrapper 生成链路、local 路径边界和 JSON/summary 同源完整性;它不代表 WeChat DevTools 或真机地图列表视觉 smoke 已经执行。 - -工作目录:`/tmp/street-tasks-iter-worktrees/map-list-summary-wrapper-guarded` - -重要边界: - -- [ ] 只使用 ignored local 产物:`harness/manual-test-results.local*.json`、`harness/manual-test-summary.local*.md`。 -- [ ] 不修改 `harness/manual-test-results.example.json`,不提交 local JSON/local MD,不提交截图、录屏、日志或云端证据。 -- [ ] wrapper 成功只说明 blocked evidence helper、summary generator 和 X guard 都跑通;不能写成地图列表 UI passed。 -- [ ] 本清单不要求 wrapper 支持复杂增量修复。已有损坏 summary 的场景,以重新生成或无 `--force` 拒绝覆盖的既有策略处理。 - -## 1. 正向:wrapper 生成后自动跑 guard - -- [ ] 运行 guarded wrapper,生成同一轮 ignored blocked JSON 和 ignored local summary。 - - ```bash - node scripts/prepare-map-list-blocked-summary.mjs \ - --reason "DevTools service port blocked; map-list visual smoke was not executed." \ - --results-out harness/manual-test-results.local-y-wrapper-guarded.json \ - --summary-out harness/manual-test-summary.local-y-wrapper-guarded.md \ - --force - ``` - - 期望: - - - 输出包含 blocked evidence helper 的成功信息:`Map-list blocked evidence draft created.`。 - - 输出包含 summary generator 的成功信息:`Manual summary draft created.`。 - - 输出包含 X guard 的成功信息:`Map-list blocked summary checks passed.`。 - - wrapper 最终退出码为 `0`,且没有把 guard 通过描述成 UI passed。 - - 输出仍保留 `Summary is not UI passed evidence` 或等价提醒。 - -- [ ] 正向产物保持 ignored local 路径。 - - ```bash - git status --short --ignored - ``` - - 期望:`harness/manual-test-results.local-y-wrapper-guarded.json` 和 `harness/manual-test-summary.local-y-wrapper-guarded.md` 不出现在 staged 或普通 unstaged 待提交列表;如仍存在,只能显示为 ignored。 - -- [ ] 对 wrapper 本次生成的同一对产物手动再跑 X guard,确认同源校验仍通过。 - - ```bash - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.local-y-wrapper-guarded.json \ - --summary harness/manual-test-summary.local-y-wrapper-guarded.md - ``` - - 期望:输出 `Map-list blocked summary checks passed.`;说明 JSON 与 summary 的 `overallStatus`、`map-list-visual-smoke` 状态、`passed=0`、`evidenceCount=0`、branch、commit、actual、followUp 和 blocker/risk 摘要保持同源。 - -## 2. 负向:非 local summary 路径前置失败 - -- [ ] summary 输出路径不是 ignored local 文件时,wrapper 应在前置路径检查阶段失败。 - - ```bash - node scripts/prepare-map-list-blocked-summary.mjs \ - --reason "negative summary path check" \ - --results-out harness/manual-test-results.local-y-wrapper-bad-summary-path.json \ - --summary-out harness/manual-test-summary.md \ - --force - ``` - - 期望: - - - 命令失败,错误说明 summary output 必须匹配 `harness/manual-test-summary.local*.md`。 - - 不应出现 `Map-list blocked evidence draft created.`、`Manual summary draft created.` 或 `Map-list blocked summary checks passed.`。 - - 不应生成 `harness/manual-test-results.local-y-wrapper-bad-summary-path.json`;如果有异常残留,应清理并记录为失败。 - -## 3. 负向:已有损坏 summary 不做增量修复 - -- [ ] 先生成一组正向 local 产物,再手动损坏 summary。 - - ```bash - node scripts/prepare-map-list-blocked-summary.mjs \ - --reason "DevTools service port blocked; map-list visual smoke was not executed." \ - --results-out harness/manual-test-results.local-y-wrapper-existing.json \ - --summary-out harness/manual-test-summary.local-y-wrapper-existing.md \ - --force - - cp harness/manual-test-summary.local-y-wrapper-existing.md \ - harness/manual-test-summary.local-y-wrapper-existing-damaged.md - perl -0pi -e 's/(\\| map-list-visual-smoke \\|[^\\n]*?\\| )blocked( \\|)/${1}passed${2}/' \ - harness/manual-test-summary.local-y-wrapper-existing-damaged.md - ``` - -- [ ] 直接运行 X guard 对损坏 summary 应失败。 - - ```bash - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.local-y-wrapper-existing.json \ - --summary harness/manual-test-summary.local-y-wrapper-existing-damaged.md - ``` - - 期望:命令失败,错误说明 `map-list-visual-smoke` summary row 不能是 `passed` 或必须保持 `blocked`。这证明 wrapper 串联没有削弱 X guard 的独立判定。 - -- [ ] 不带 `--force` 再跑同名 wrapper 时,不要求修复已损坏的 summary。 - - ```bash - node scripts/prepare-map-list-blocked-summary.mjs \ - --reason "DevTools service port blocked; map-list visual smoke was not executed." \ - --results-out harness/manual-test-results.local-y-wrapper-existing.json \ - --summary-out harness/manual-test-summary.local-y-wrapper-existing-damaged.md - ``` - - 期望: - - - 命令失败在现有输出覆盖策略上,例如 blocked evidence helper 拒绝覆盖已有 results JSON。 - - 不要求 wrapper 检测并增量修复已有 damaged summary。 - - 需要修复时,执行者应使用 `--force` 重新生成同一轮 JSON 和 summary,再由自动串联的 X guard 校验新产物。 - -## 4. 回归:X guard 仍能拦截被改坏的 summary - -- [ ] 复制正向 summary 并改坏同源字段,直接运行 X guard。 - - ```bash - cp harness/manual-test-summary.local-y-wrapper-guarded.md \ - harness/manual-test-summary.local-y-wrapper-guarded-bad-commit.md - perl -0pi -e 's/(\\| commit \\| )([^|]+)( \\|)/${1}badc0de${3}/' \ - harness/manual-test-summary.local-y-wrapper-guarded-bad-commit.md - - node scripts/check-map-list-blocked-summary.mjs \ - --results harness/manual-test-results.local-y-wrapper-guarded.json \ - --summary harness/manual-test-summary.local-y-wrapper-guarded-bad-commit.md - ``` - - 期望:命令失败,错误说明 summary `commit` 与 JSON `commit` 不一致。该回归证明 wrapper 自动调用 guard 后,手动使用 X guard 的失败能力仍保留。 - -## 5. 清理与 Git 卫生 - -- [ ] 清理本清单生成的 local JSON、local MD 和可能的 `/tmp` 临时输出。 - - ```bash - rm -f harness/manual-test-results.local-y-wrapper-*.json - rm -f harness/manual-test-summary.local-y-wrapper-*.md - rm -f harness/manual-test-summary.md - rm -rf /tmp/street-tasks-map-list-summary-wrapper-guarded-* - ``` - -- [ ] 确认 local evidence 不进入提交范围。 - - ```bash - git status --short --ignored - ``` - - 期望:可提交改动只包含 Y 组预期文件;`harness/manual-test-results.local*.json`、`harness/manual-test-summary.local*.md` 和 `/tmp` 输出不进入 staged 或普通 unstaged 待提交列表。 - -- [ ] wrapper 代码落地后,至少运行以下基础检查。 - - ```bash - node --check scripts/prepare-map-list-blocked-summary.mjs - node --check scripts/check-map-list-blocked-summary.mjs - git diff --check - ``` - -## 6. 最终报告口径 - -- [ ] 报告应区分三件事:blocked JSON helper 已生成、summary generator 已生成、X guard 已校验同源完整性。 -- [ ] 报告应列出正向 wrapper 输出、非 local summary 路径失败、damaged summary 失败、清理结果和 git 状态。 -- [ ] 报告必须明确:`Map-list blocked summary checks passed.` 只代表 wrapper 产物的 blocked 状态和同源完整性通过 guard。 -- [ ] 不得把 wrapper+guard 成功写成 DevTools UI passed、真机 passed、地图列表视觉 smoke passed 或发布准入。 -- [ ] 如果真实 UI 没有执行,继续说明长标题、长正文、图片/无图、安全区、原生 map 层、滚动、marker/list/detail 链路仍未被真实观察。 diff --git a/harness/map-list-summary-wrapper-guarded-product-brief.md b/harness/map-list-summary-wrapper-guarded-product-brief.md deleted file mode 100644 index 417bd8d..0000000 --- a/harness/map-list-summary-wrapper-guarded-product-brief.md +++ /dev/null @@ -1,69 +0,0 @@ -# 地图列表 blocked summary wrapper guard 产品 brief - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-summary-wrapper-guarded` -- 角色:Y 组产品 agent - -## 用户问题 - -X 组已经增强 `scripts/check-map-list-blocked-summary.mjs`,可以校验 blocked JSON 与 summary 的状态和同源完整性。当前剩余风险是:执行者使用 V wrapper `scripts/prepare-map-list-blocked-summary.mjs` 生成 JSON 和 summary 后,可能忘记再手动运行 X guard,导致未校验、状态不一致或非同源的产物进入后续交接。 - -## 产品假设 - -- 生成 blocked JSON 与 summary 的默认路径应该在产物生成后立即自检。 -- 只要 V wrapper 默认自动调用 X guard,就能显著降低“生成 summary 后漏跑 guard”的人为风险。 -- guard 失败应反映到 wrapper 的退出结果中,避免出现“文件已生成,所以流程已完成”的误判。 -- 这是流程安全补强,不是地图列表业务体验或视觉验收的变化。 - -## 范围 - -- 将 `scripts/check-map-list-blocked-summary.mjs` 接入 `scripts/prepare-map-list-blocked-summary.mjs` 的生成后流程。 -- 默认校验 wrapper 本次生成的 blocked JSON 与 summary。 -- 在 guard 失败时让 wrapper 失败退出,并保留清楚的错误输出,方便执行者定位。 -- 保留执行者单独手动运行 X guard 的能力。 - -## 非目标 - -- Y 不执行 WeChat DevTools/真机。 -- Y 不修改业务 UI。 -- Y 不声明地图列表视觉 smoke 通过。 -- 不重写 V wrapper 的输入语义、产物格式或 X guard 的核心规则。 -- 不扩展新的地图列表视觉、交互或数据验收范围。 -- 本目标只减少生成 summary 后漏跑 guard 的风险。 - -## 与 V/W/X 的关系 - -- V:提供生成 blocked JSON 与 summary 的 wrapper。Y 的产品要求是让该 wrapper 在生成后默认触发 X guard。 -- W:作为后续消费、复核或交接产物的协作方,应默认拿到已经过状态与同源校验的 wrapper 输出。 -- X:提供 `scripts/check-map-list-blocked-summary.mjs` guard。Y 不改变 X 的判定标准,只要求把它接入默认生成链路。 - -## Wrapper Guard 成功边界 - -- wrapper 成功生成 blocked JSON 与 summary。 -- wrapper 随后自动调用 X guard 校验同一轮生成产物。 -- X guard 返回成功,且 wrapper 最终以成功状态结束。 -- 输出能让执行者看出 guard 已运行且通过。 -- 成功只代表 JSON 与 summary 的状态和同源完整性通过 guard,不代表地图列表视觉 smoke 通过。 - -## Wrapper Guard 失败边界 - -- wrapper 生成的 JSON 或 summary 缺失、不可读或格式不符合 X guard 要求。 -- blocked JSON 与 summary 的状态、来源或同源关系不一致。 -- X guard 执行异常或返回失败。 -- guard 失败时,wrapper 不应吞掉错误或继续声明生成流程成功。 -- 失败边界不包含 WeChat DevTools、真机、业务 UI 或地图列表视觉 smoke 的判定。 - -## 成功标准 - -- 默认执行 `scripts/prepare-map-list-blocked-summary.mjs` 后,会自动运行 `scripts/check-map-list-blocked-summary.mjs`。 -- guard 通过时,wrapper 返回成功,并留下可读的通过证据。 -- guard 失败时,wrapper 返回失败,并留下可读的失败原因。 -- 手动运行 X guard 的路径仍然可用。 -- 变更不触碰业务 UI,不引入地图列表视觉验收结论,也不把 WeChat DevTools/真机验证包装成已完成。 - -## 下一步 - -- 执行者在 `scripts/prepare-map-list-blocked-summary.mjs` 中接入生成后 guard。 -- 为成功路径和 guard 失败路径补充聚焦验证,确认退出状态会正确透传。 -- 将实际命令与结果记录到 harness 进度或特性状态文件中。 -- 需要视觉或真机结论时,由对应验证负责人另行执行并记录,Y brief 不替代该类验收。 diff --git a/harness/map-list-visual-evidence-checklist.md b/harness/map-list-visual-evidence-checklist.md deleted file mode 100644 index c27e9a1..0000000 --- a/harness/map-list-visual-evidence-checklist.md +++ /dev/null @@ -1,216 +0,0 @@ -# 地图列表真实视觉 smoke 检查清单 - -日期:2026-06-14 - -范围:用于 `codex/iter-map-list-visual-evidence` 分支后续在 WeChat DevTools UI 或真机中执行地图列表真实视觉 smoke。P/Q/R 已经覆盖地图列表静态 guard、readiness 接入和手测准备 helper;本清单只固定真实看屏幕时的观察项、数据变体和证据记录口径,不代表 DevTools 或真机已经通过。 - -工作目录:`/tmp/street-tasks-iter-worktrees/map-list-visual-evidence` - -重要边界: - -- [ ] 当前已知 WeChat DevTools service port `9420` 仍可能 blocked;如果无法打开、预览或连接 DevTools,不得把任何视觉项写成 `passed`。 -- [ ] 真实手测结果只能写入 ignored 的 `harness/manual-test-results.local*.json`;不要把真实结果写进 `harness/manual-test-results.example.json`。 -- [ ] 原始截图、录屏、二维码、完整 Console/Network 日志、云端记录和真实图片只放 ignored 的 `harness/manual-evidence-artifacts/` 或外部安全位置,不提交到仓库。 -- [ ] 未执行或环境不可用时,结果写 `not_covered` 或 `blocked`;只有真实打开目标页面并观察到预期表现,才能写 `passed`。 - -## 1. 准备 - -- [ ] 确认工作树与分支。 - - ```bash - pwd - git branch --show-current - git rev-parse --short HEAD - git status --short - ``` - - 期望:工作树为 `/tmp/street-tasks-iter-worktrees/map-list-visual-evidence`,分支为 `codex/iter-map-list-visual-evidence`;待提交范围只包含本轮预期 QA 清单或后续脱敏摘要。 - -- [ ] 跑基础 harness。 - - ```bash - bash harness/init.sh - ``` - - 期望:JSON 检查和 harness 自检通过。若失败,先记录为基础 blocker,不继续声称视觉 smoke 通过。 - -- [ ] 准备 ignored 的本地手测结果文件。 - - ```bash - node scripts/prepare-manual-test-run.mjs --out harness/manual-test-results.local-s-map-list.json --force - ``` - - 期望:helper 输出 readiness preflight 已运行,且提醒 preflight 不等于 DevTools 或真机视觉验收;生成的 local JSON 保持 ignored。 - -- [ ] 阅读 readiness 输出,确认地图列表 static guard 已包含在 preflight 中。 -- [ ] 打开 WeChat DevTools UI 时使用本 worktree,保留 `project.config.json` 的公开 `appid: touristappid`;真实 AppID 只在本机 `project.private.config.json` 中使用,不能提交。 -- [ ] 若 DevTools CLI 或 UI 因 `9420`、服务端口、登录态、项目导入失败等原因不可用,在 local JSON 中把对应 journey 写为 `blocked`,并记录最小错误摘要。 - -## 2. 环境记录 - -每次真实视觉 smoke 至少记录以下字段;不要写敏感原始值。 - -- [ ] `checkedAt`:执行时间,包含时区或本地日期。 -- [ ] `branch`:`codex/iter-map-list-visual-evidence`。 -- [ ] `commit`:`git rev-parse --short HEAD` 的短 SHA。 -- [ ] `worktree`:本 worktree 的路径摘要,不需要写个人用户目录之外的敏感路径。 -- [ ] `runner`:QA 执行人或 agent 标识。 -- [ ] `entry`:WeChat DevTools UI、真机预览、真机调试或其他入口。 -- [ ] `devtoolsVersion`:DevTools 版本;无法获取时写 `not_covered`。 -- [ ] `baseLibrary`:基础库版本;无法获取时写 `not_covered`。 -- [ ] `device`:模拟器机型或真机型号摘要,例如 iPhone 带安全区、Android 常见宽度。 -- [ ] `viewport`:宽高、像素比或屏幕宽度档位;不要依赖截图文件名承载这些信息。 -- [ ] `safeArea`:是否带 Home 指示条、底部安全区、状态栏/胶囊影响。 -- [ ] `network`:在线、弱网、断网或未覆盖。 -- [ ] `locationPermission`:允许、拒绝、未触发或未覆盖。 -- [ ] `dataSource`:mock/local storage、CloudBase、临时测试数据或未确认。 -- [ ] `resultFile`:例如 `harness/manual-test-results.local-s-map-list.json`,确认该文件 ignored。 -- [ ] `evidenceRefs`:只写脱敏附件编号,例如 `S-map-01`、`S-map-scroll-02`,不写原始截图路径。 - -## 3. 地图列表视觉项 - -### 3.1 首屏与地图原生层 - -- [ ] 地图页首屏显示默认中心或定位后的地图,不出现白屏、无限 loading 或不可理解的空状态。 -- [ ] 原生 `map` 层与上层 `cover-view`/列表抽屉层级正确;抽屉打开后列表内容位于地图之上,不被地图瓦片、marker 或定位控件盖住。 -- [ ] 抽屉关闭或半开时,地图 marker 可见且不会与底部 tabBar、列表入口、浮层按钮产生遮挡。 -- [ ] 抽屉打开后滚动列表时,手势优先滚动列表,不应误触发地图拖动或缩放。 -- [ ] 地图原生层偶发 `WAServiceMainContext timeout` 时,若页面仍可操作,只能记录为已知 console 风险;若阻断渲染或交互,则记录 `failed` 或 `blocked`,不能忽略。 - -### 3.2 抽屉、安全区与底部区域 - -- [ ] 列表抽屉顶部、筛选条、滚动区域和底部边界清晰,抽屉高度铺到 tabBar 上沿,不压住 tabBar。 -- [ ] iPhone 带 Home 指示条设备上,抽屉底部、tabBar 和系统安全区之间没有文字、按钮或卡片被裁切。 -- [ ] Android 常见屏宽下,抽屉底部不留异常空洞,也不把最后一张卡片压在 tabBar 后方。 -- [ ] 横向较窄设备上,抽屉头部计数、筛选条和列表内容不互相重叠。 -- [ ] 切换分类筛选时,抽屉高度和滚动容器稳定,不出现布局跳动到地图背后。 - -### 3.3 卡片信息层级 - -- [ ] 卡片阅读顺序清楚:标题、分类/状态标签、正文摘要、底部统计、时间距离、详情入口。 -- [ ] 长标题可以自然换行或截断,不覆盖分类标签、状态标签、缩略图、底部统计或详情入口。 -- [ ] 长正文最多占用预期摘要高度,省略后不遮挡下一张卡片,也不把底部统计挤出卡片。 -- [ ] 标题后同时出现分类、失物/招领方向和非 active 状态时,标签可换行但仍归属于标题区域。 -- [ ] 底部左侧“确认/过时”统计轻量可读,右侧时间/距离右对齐且必要时截断,左右两侧不重叠。 -- [ ] 计数为两位数或三位数时,卡片高度变化可接受,详情入口仍保持固定触摸区域。 -- [ ] 空列表状态、单条列表和多条列表状态都没有视觉塌陷或异常留白。 - -### 3.4 图片与无图 - -- [ ] 无图卡片不保留缩略图空位,正文列自然占满可用宽度。 -- [ ] 单图卡片缩略图比例稳定,加载前后不造成标题、标签或详情入口横跳。 -- [ ] 多图任务如果列表只取封面图,封面图呈现稳定;如列表展示多图入口,也不得挤压正文和详情入口。 -- [ ] 图片加载失败时,卡片仍可读、可滚动、可进入详情;失败占位不遮挡文字。 -- [ ] 竖图、横图、深色图和浅色图都不影响文字可读性;列表不依赖图片内容承载关键信息。 - -### 3.5 列表滚动 - -- [ ] 列表超过一屏时,只滚动列表区域;抽屉头部、筛选条和底部 tabBar 保持稳定。 -- [ ] 快速上滑/下滑不会出现卡片重叠、空白闪烁、地图层穿透或点击区域错位。 -- [ ] 滚动到最后一条时,最后一张卡片底部完整可见,不被 tabBar、安全区或系统手势区遮挡。 -- [ ] 滚动过程中图片懒加载不会导致已阅读位置明显跳动。 -- [ ] 切换分类后滚动位置表现可理解;若回到顶部,应记录为预期或后续产品问题,不混写成视觉通过。 - -## 4. 交互项 - -- [ ] 点击 marker 后,选中态、列表对应任务或详情入口关系清楚;marker 对应的任务标题与列表/详情一致。 -- [ ] 点击列表卡片主体时,若预期为聚焦地图或选中任务,地图和列表反馈一致,不误进入错误详情。 -- [ ] 点击卡片右侧详情入口时,进入对应任务详情页,详情页标题、分类、正文与列表卡片一致。 -- [ ] 从详情页返回地图页后,地图列表状态可恢复;不出现白屏、抽屉丢失、滚动锁死或 marker 丢失。 -- [ ] 筛选分类后,marker、列表卡片和详情页仍指向同一任务集合;不出现列表有任务但 marker/detail 不匹配。 -- [ ] 点击“查找附近任务”或定位按钮后,列表仍可打开并显示与地图中心一致的任务;定位失败时保留可浏览状态。 -- [ ] 在列表滚动中点击详情入口,事件不应被滚动手势吞掉;若误触或无响应,需要记录复现手势和设备。 -- [ ] 详情页出现“任务不存在”时,记录为链路失败;不要只用列表渲染正常来判定 smoke 通过。 - -## 5. 数据变体 - -执行真实视觉 smoke 时至少覆盖以下变体;无法准备的数据写 `not_covered`,不要补写为通过。 - -- [ ] 长标题:40 到 80 个中文字符;另测一条连续英文/数字长串。 -- [ ] 长正文:超过 120 个中文字符,包含时间、地点、补充说明和标点。 -- [ ] 无图任务:使用默认 mock/local storage 样本或明确无图片的测试任务。 -- [ ] 有图任务:至少覆盖 1 张图片;如果条件允许,再覆盖多图、加载慢和加载失败。 -- [ ] 状态组合:active、stale、resolved、expired 至少各一条;hidden 不应出现在普通列表。 -- [ ] 分类组合:求助、跑腿、失物招领、地点动态等常见分类;失物招领需覆盖 lost/found 方向。 -- [ ] 计数组合:确认/过时为 0、1、两位数;高计数无法准备时写 `not_covered`。 -- [ ] 时间距离组合:刚刚/小时级/日期级,近距离/较远距离;异常距离文案写明数据条件。 -- [ ] 列表数量:0 条、1 条、超过一屏;超过一屏用于滚动和底部安全区观察。 -- [ ] 宽度组合:320-360、375-414、较大屏宽;真机 iOS/Android 条件不足时分别写 `not_covered` 或 `blocked`。 - -## 6. 失败 / blocked 记录 - -遇到失败或阻塞时,用可复查摘要记录,不要只写“样式异常”。 - -- [ ] `status` 只使用 `failed`、`blocked`、`not_covered` 或真实观察后的 `passed`。 -- [ ] `blockedReason`:例如 `9420 service port timeout`、DevTools 未登录、项目无法导入、真机不可用、测试数据无法准备。 -- [ ] `dataCase`:长标题、有图、无图、安全区、原生 map 覆盖、滚动、marker/list/detail 链路等。 -- [ ] `steps`:最小复现步骤,从打开地图页、打开列表、切换筛选、滚动、点击 marker/详情开始写。 -- [ ] `expected`:明确期望,例如“不重叠、可截断、详情入口可点、返回后列表恢复”。 -- [ ] `actual`:实际表现摘要,例如“标题覆盖详情箭头”“滚动时地图层穿透”“点击详情进入任务不存在”。 -- [ ] `impact`:阻断核心链路、影响某设备、纯视觉瑕疵、证据不足或环境阻塞。 -- [ ] `evidence`:脱敏附件编号和证据类型,不写真实路径、完整日志或云端 id。 -- [ ] `nextAction`:需要恢复 DevTools 端口、补测试数据、调整 WXSS、排查 map 原生层、补详情 fallback 或真机复测。 - -示例 blocked 摘要: - -```json -{ - "journey": "map-list-visual-smoke", - "status": "blocked", - "blockedReason": "WeChat DevTools service port 9420 timeout; UI visual smoke was not executed.", - "evidence": ["命令摘要本地留存,未提交原始日志"], - "nextAction": "在 DevTools UI 确认服务端口开启后重新执行真实视觉 smoke" -} -``` - -## 7. 证据卫生 - -- [ ] 本地真实结果文件必须匹配 `harness/manual-test-results.local*.json`,并保持 ignored。 -- [ ] 本地脱敏摘要草稿必须匹配 `harness/manual-test-summary.local*.md`,并保持 ignored。 -- [ ] 原始附件只放 `harness/manual-evidence-artifacts/` 或外部安全位置;该目录 ignored,不要 `git add -f`。 -- [ ] 可提交文档只写观察结论和附件编号,例如“截图 S-map-03 本地留存,显示长标题未遮挡详情入口”。 -- [ ] 不提交真实截图、录屏、二维码、完整 Console/Network、云函数日志、数据库截图或 Storage 权限截图。 -- [ ] 不写真实 AppID、openId、unionId、头像 URL、昵称、手机号、精确经纬度、CloudBase env id、request id、完整 `cloud://`、token、cookie 或本机绝对路径。 -- [ ] 地点和用户使用角色化描述,例如“默认中心附近”“测试 POI”“用户 A”“游客态”,不要写可识别真实个人或住址的信息。 -- [ ] 如果需要分享失败画面,先裁剪或打码;提交前仍只保留脱敏摘要,原图不进仓库。 -- [ ] 提交前用 `git status --short --ignored` 确认 local JSON、local summary 和附件目录没有进入待提交列表。 - -## 8. 收尾验证 - -真实视觉 smoke 执行完成或被阻塞后,至少跑以下检查并记录结果。 - -- [ ] 校验本地手测结果结构。 - - ```bash - node scripts/check-manual-evidence.mjs harness/manual-test-results.local-s-map-list.json - ``` - - 若手测未执行,local JSON 应保留 `blocked` 或 `not_covered`,不要为了通过检查改成 `passed`。 - -- [ ] 校验证据卫生。 - - ```bash - node scripts/check-evidence-hygiene.mjs - ``` - -- [ ] 跑基础 harness。 - - ```bash - bash harness/init.sh - ``` - -- [ ] 检查 Markdown 和代码 diff 空白。 - - ```bash - git diff --check - ``` - -- [ ] 检查工作树。 - - ```bash - git status --short --ignored - ``` - - 期望:可提交改动只包含预期 QA 清单或脱敏报告;`harness/manual-test-results.local*.json`、`harness/manual-test-summary.local*.md` 和 `harness/manual-evidence-artifacts/` 仍为 ignored。 - -- [ ] 最终报告必须区分三类结果:自动 readiness/preflight 已通过、真实 DevTools/真机视觉 smoke 已通过、真实视觉 smoke 因环境或数据被 `blocked/not_covered`。不得用第一类替代第二类。 diff --git a/harness/map-list-visual-evidence-product-brief.md b/harness/map-list-visual-evidence-product-brief.md deleted file mode 100644 index f56d60f..0000000 --- a/harness/map-list-visual-evidence-product-brief.md +++ /dev/null @@ -1,70 +0,0 @@ -# 地图列表视觉 Smoke 证据结构产品 Brief - -- 日期:2026-06-14 -- 分支:`codex/iter-map-list-visual-evidence` -- 角色:S 组产品 agent -- 对应 feature:`map-feed-001` - -## 用户问题 - -P/Q/R 已经补齐地图列表静态结构 guard、readiness 集成和手测准备入口,但这些都只能说明“准备工作和结构约束通过”。评测仍缺真实视觉证据:长标题、长正文、带图/无图、安全区、原生地图遮挡、列表滚动,以及 marker/list/detail 点击链路,都需要有固定记录位置,避免执行者用自动脚本或模板生成结果代替真实观察。 - -## 产品假设 - -如果手测结果文件中为地图列表视觉 smoke 预留明确证据槽位,执行者会更容易逐项观察、记录 blocked 原因和补充脱敏截图/录屏引用;评审也能快速判断地图列表是否真的在 DevTools UI 或真机中被看过。 - -## 范围 - -- 定义地图列表视觉 smoke 的证据结构和记录口径。 -- 覆盖 `long_title`、`long_body`、`with_image`、`without_image`、`safe_area`、`native_map_overlay`、`drawer_scroll`、`marker_tap`、`list_card_tap`、`detail_entry_tap`。 -- 每个观察项都必须记录环境、步骤、实际结果、状态、证据引用和风险备注。 -- 状态只允许写 `passed`、`failed`、`blocked`、`not_covered`。 -- 继续使用 ignored 的本地结果文件和本地附件目录;可提交文档只放脱敏摘要和结构定义。 - -## 非目标 - -- 不修改业务代码、WXML、WXSS、脚本或 JSON 模板。 -- 不恢复 WeChat DevTools 9420 服务端口,不执行 quit/open/kill/cache/config 操作。 -- 不新增真实截图、录屏、local JSON 或任何原始 evidence 附件。 -- 不声明地图列表、发布、详情或完整用户旅程已经通过。 -- 不把 P/Q/R 的自动门禁结果升级成视觉验收结论。 - -## 成功/Blocked 判定 - -- `passed`:只能来自 DevTools UI 或真机观察;必须包含设备/模拟器、屏宽或机型、DevTools/基础库版本、操作步骤、实际结果和可复核证据引用。 -- `failed`:DevTools UI 或真机可运行,但观察到重叠、遮挡、无法滚动、点击无效、详情跳转错误等用户可见问题。 -- `blocked`:无法完成真实观察,例如 9420 服务端口仍 blocked、DevTools 无法打开项目、真机不可用、测试数据无法准备、地图原生层阻断操作。 -- `not_covered`:本轮未执行该观察项;不能写成通过,也不能用自动脚本结果补位。 - -自动脚本、readiness、static guard、example JSON、local JSON 生成和 summary 模板通过,都不等于视觉通过。只有 DevTools UI 或真机中的真实观察可以写 `passed`。 - -## 证据要求 - -每次地图列表视觉 smoke 至少记录: - -- `environment`:分支、commit、DevTools 版本、基础库版本、设备/模拟器、屏宽、是否真机、网络和定位授权状态。 -- `data_setup`:用于观察的任务数据说明,至少覆盖长标题、长正文、带图、无图、不同状态和 lost/found 方向;不能包含真实用户隐私或完整云端路径。 -- `observations.long_title`:标题和分类/状态标签是否换行正常,详情入口是否仍可见。 -- `observations.long_body`:正文摘要是否限制行数,卡片高度是否影响列表扫读。 -- `observations.with_image` 与 `observations.without_image`:图片加载前后骨架是否稳定,无图卡片是否仍对齐。 -- `observations.safe_area`:抽屉底部、最后一张卡片和 tabBar/安全区是否互不遮挡。 -- `observations.native_map_overlay`:原生 map 层是否压住抽屉、按钮、列表滚动或点击热区。 -- `observations.drawer_scroll`:列表可滚动,顶部筛选和底部卡片在滚动中不错位。 -- `observations.marker_tap`、`observations.list_card_tap`、`observations.detail_entry_tap`:marker 聚焦、列表卡片交互和详情入口跳转是否能到正确任务详情。 -- `artifacts`:脱敏截图/录屏/日志摘要引用;原始附件必须留在 ignored 本地目录或受控系统。 - -## 与 P/Q/R 的关系 - -- P 组定义并实现地图列表 WXML/WXSS static guard,降低结构和样式回归风险。 -- Q 组把 P 的 guard 接入 readiness preflight,降低候选验证漏跑风险。 -- R 组让手测准备 helper 显式展示 readiness 和地图列表 guard,降低执行入口误解风险。 -- S 组补齐真实视觉 smoke 的证据结构,明确哪些观察项必须有固定记录位置,以及什么条件下才能写 `passed`。 - -P/Q/R/S 共同目标是让地图列表更接近真实验收,但前三者和本 brief 都不代表已经完成真实视觉 smoke。当前已知 9420 服务端口仍可能 blocked;若真实 UI 无法打开,应记录 `blocked`,不要改写成通过。 - -## 下一步建议 - -- QA/开发 agent 可在后续任务中扩展 local manual results 结构或 checklist,把本 brief 的观察项落成可校验字段。 -- 端口恢复后,优先用 DevTools UI 执行一次地图列表视觉 smoke,并为每个观察项填写状态和脱敏证据引用。 -- 若发现视觉失败,先记录 failed 证据和最小复现步骤,再由开发 agent 做小范围修复。 -- 若端口仍 blocked,继续记录 blocker 细节和恢复尝试来源,不要新增“视觉 passed”结论。 diff --git a/harness/package-readiness-gate-checklist.md b/harness/package-readiness-gate-checklist.md deleted file mode 100644 index a9836cd..0000000 --- a/harness/package-readiness-gate-checklist.md +++ /dev/null @@ -1,36 +0,0 @@ -# AC 轮 package readiness gate QA checklist - -## 目标 - -把 AB 轮已接入默认路径的 blocked summary preflight 暴露到 npm 层,让本地人工验证和自动化都能用稳定命令调用同一组门禁。 - -## 预期 npm 入口 - -- `npm run check:json`:只运行 JSON 严格语法检查。 -- `npm run check:harness`:只运行 harness 结构检查。 -- `npm run check:blocked-summary`:运行 ignored local blocked summary preflight。 -- `npm run check:readiness`:运行 DevTools readiness 脚本,并通过 AB 轮默认入口带起 blocked summary preflight,同时保留“不代表 DevTools 或真机 UI 通过”的口径。 -- `npm run check`:组合运行 `check:json`、`check:harness` 和 `check:readiness`;其中 `check:readiness` 会覆盖 blocked summary preflight,避免重复跑同一检查。 - -## 自动验证清单 - -- 无 `harness/manual-test-summary.local*.md` 时,`npm run check` 应通过,并能看到 blocked summary preflight 的空跑提示。 -- 无 local summary 时,分别运行 `npm run check:json`、`npm run check:harness`、`npm run check:blocked-summary`、`npm run check:readiness` 均应通过。 -- 使用 blocked summary wrapper 生成一组匹配的 local results/summary 后,`npm run check:blocked-summary` 应通过。 -- 存在一组匹配的 local results/summary 后,`npm run check` 应通过,且不把 blocked summary 写成 UI passed 证据。 -- 删除 matching `harness/manual-test-results.local*.json` 后,`npm run check` 应失败,并提示缺失 results JSON。 -- 将 matching `harness/manual-test-summary.local*.md` 中 `map-list-visual-smoke` 改成 `passed` 后,`npm run check` 应失败,并提示该 journey 不能是 passed。 -- 负向验证后必须清理本轮产生的 `harness/manual-test-results.local*.json` 和 `harness/manual-test-summary.local*.md` local 产物。 - -## 报告口径 - -- AC 轮只能证明 npm 级门禁入口更容易被人工和自动化调用。 -- blocked summary preflight 只检查 ignored local blocked summary 的结构和同源一致性。 -- 任何 `npm run check*` 通过都不能写成地图列表 UI、DevTools 或真机视觉验收通过。 - -## 未验证项 - -- WeChat DevTools 真实打开项目、编译、地图列表抽屉视觉 smoke。 -- 真机 safe area、原生地图层覆盖、滚动、长标题/长正文、带图/无图卡片。 -- marker/list/detail 点击链路和定位授权允许、拒绝、超时路径。 -- CI 或提交钩子是否强制运行 `npm run check`。 diff --git a/harness/package-readiness-gate-product-brief.md b/harness/package-readiness-gate-product-brief.md deleted file mode 100644 index 1341768..0000000 --- a/harness/package-readiness-gate-product-brief.md +++ /dev/null @@ -1,34 +0,0 @@ -# AC 轮产品 Brief:npm 级检查入口 - -## 目标 - -为仓库新增统一的 npm 检查入口,让人和自动化可以通过更常见的命令运行现有静态、harness、readiness 和 blocked summary preflight 检查。 - -当前 `package.json` 只有 `check:json`。AC 轮希望把 AB 轮已接入默认路径的检查能力暴露为更易发现、可复用的 npm scripts,例如 `npm run check` 覆盖 JSON、harness、DevTools readiness/default preflight 等默认门禁。 - -## 用户价值 - -- 新接手的 agent 或开发者不需要记住多条 Node 命令,先运行 `npm run check` 即可完成基础准入检查。 -- 自动化、评审和本地开发可以共用同一个入口,减少“只跑了 JSON 检查、漏掉 harness 或 blocked summary preflight”的风险。 -- 失败输出仍来自现有脚本,便于定位是 JSON、harness、readiness 还是 ignored local blocked summary 证据不一致。 -- npm 入口让 package 层显式表达项目质量门禁,降低后续接入 CI 或提交前检查的成本。 - -## 验收标准 - -- `package.json` 保留现有 `check:json`,并新增统一检查入口,优先命名为 `check`。 -- `npm run check` 至少运行: - - `node scripts/check-json.mjs` - - `node harness/check-harness.mjs` - - `node --no-warnings scripts/check-devtools-readiness.mjs` -- 如果存在 `harness/manual-test-summary.local*.md`,默认检查必须触发 blocked summary preflight,并在缺失 matching results JSON 或 summary 被改成 `passed` 时失败。 -- 无 ignored local summary 时,`npm run check` 应通过,并清楚输出该 preflight 没有真实 UI passed 含义。 -- 需要保留或补充一个轻量命令,方便只跑 JSON 检查;不得破坏现有 `npm run check:json`。 -- 主 agent 验证时应覆盖正向无 local summary、正向 local blocked pair、缺 results JSON 负向、summary 改 `passed` 负向。 - -## 非目标 - -- 不恢复 WeChat DevTools 9420 服务端口,也不改变 DevTools 打开、预览或真机调试方式。 -- 不把 blocked summary、readiness 或 npm check 结果写成真实 UI passed、DevTools passed 或真机 passed。 -- 不新增 UI、业务行为、云函数或数据模型改动。 -- 不引入 npm 依赖、构建框架、CI 配置或 git hook;本轮只定义 package 级本地检查入口。 -- 不提交 ignored local evidence 文件;local JSON/Markdown 仍只用于本地阻塞证据演练和评审前检查。 diff --git a/harness/profile-design-brief.md b/harness/profile-design-brief.md deleted file mode 100644 index ddea137..0000000 --- a/harness/profile-design-brief.md +++ /dev/null @@ -1,47 +0,0 @@ -# 个人中心设计简报 - -## 目标 - -个人中心首页需要从“入口列表”升级为“我的状态面板”:用户进入后能在 3 秒内知道自己当前有什么待关注状态,并看到一个最自然的下一步动作。风格继续沿用 Street Tasks 的克制本地生活工具感,避免营销化大标题和装饰性强的视觉。 - -## 现状观察 - -- `pages/me/me.wxml` 已有头像信息、4 项统计、管理员提示和“我的发布 / 参与记录”两个入口。 -- `pages/my-posts/my-posts.wxml` 已能表达发布任务的状态、地点、确认数和过期时间。 -- `pages/activities/activities.wxml` 已能表达用户的确认、标记过时、举报行为。 -- 个人中心首页的问题不是缺入口,而是缺少“此刻最该看什么 / 做什么”的聚合表达。 - -## 界面建议 - -1. 将 4 项统计升级为“状态摘要”而不是纯数字宫格。 - - 保留 `stat-panel` 的紧凑结构,但优先呈现用户关心的状态:进行中、即将过期、已解决、参与过。 - - 若有异常状态,可用 `Warning surface` 或 `Danger surface` 提醒,例如“2 条快过期”“1 条被标记过时”。 - - 数字下方文案控制在 2-4 个汉字,适合小屏:`进行中`、`快过期`、`已解决`、`参与过`。 - -2. 在头像卡片下增加一条“下一步建议”行动条。 - - 位置建议放在 profile panel 和统计区之间,使用轻量 `panel`,不要做大 hero。 - - 根据当前状态切换主文案:没有发布时显示“从身边的一件小事开始”,有快过期发布时显示“有任务快到期了”,有参与记录时显示“看看你帮过的附近任务”。 - - 右侧只放一个短按钮:`去发布`、`去处理`、`看记录`。按钮高度至少 72rpx,符合当前 TDesign 式触控要求。 - -3. 合并“我的发布 / 参与记录”为带状态预览的两张入口卡。 - - 入口标题继续使用 `我的发布`、`参与记录`,但副文案从泛泛计数改成状态化摘要,例如“3 条进行中,1 条快过期”或“确认 4 次,举报 1 次”。 - - 卡片右侧箭头保留,左侧可增加一枚小型状态 pill,例如 `待处理`、`已参与`,使用现有语义色,不新增花哨图标。 - - 每张卡最多两行文字,避免在 375px 等窄屏上拥挤。 - -4. 为首页增加一个空状态优先路径。 - - 当用户没有发布且没有参与记录时,不要只显示两个空入口;首页直接给出一句短提示和主行动:`还没有附近动态` / `去地图看看` 或 `发布一条`。 - - 文案保持生活化但克制:不用“立即开启你的旅程”这类营销语。 - - 空状态可以复用 `empty-panel` 的语言和二级页按钮样式,降低开发成本。 - -5. 管理员入口继续弱化,避免抢占普通用户路径。 - - 只有 `isAdmin` 或 `showAdminLogin` 时才显示现有管理卡片,普通用户默认看不到管理信息。 - - 管理卡片建议排在状态摘要之后,避免压过“我现在有什么状态”的主目标。 - - 若需要提示权限状态,文案保持短句:`管理权限已开启`、`去巡检`。 - -## 视觉与文案约束 - -- 继续使用 `DESIGN_SYSTEM.md` 的背景、表面、主色、警告色和危险色,不新增单次使用色值。 -- 卡片圆角不超过 `16rpx`,信息密度应接近现有 `profile-link-card` 和 `stat-panel`。 -- 中文文案以“状态 + 动作”为主,少用解释句。 -- 小屏优先:每个首页模块最多承载一个主要信息目标,按钮文案不超过 3 个汉字。 -- 不建议引入新依赖或 TDesign 组件库;当前阶段用原生 WXML/WXSS 按 TDesign 规则模拟即可。 diff --git a/harness/profile-product-brief.md b/harness/profile-product-brief.md deleted file mode 100644 index 461bcf1..0000000 --- a/harness/profile-product-brief.md +++ /dev/null @@ -1,36 +0,0 @@ -# profile-activity-001 产品迭代简报 - -## 已有能力 - -- 个人中心已展示头像、昵称、登录状态和管理员入口。 -- 已有四项统计:我发布、处理中、已关闭、参与。 -- 已有“我的发布”“参与记录”“反馈”入口。 -- 二级页已经能展示我的发布列表、参与记录列表和空状态引导。 - -## 本轮最高价值目标 - -个人中心首页不再只是入口列表,而是让用户一眼知道“我现在有什么状态”和“下一步该做什么”。本轮优先把已有数据聚合成状态看板,不扩展新业务流程。 - -## 建议落地点 - -1. 增加纯状态构建器,统一生成统计、入口文案、下一步建议和最近动态预览。 -2. 首页展示一张“下一步建议”卡片,按游客、待补资料、无发布、处理中任务、参与记录等状态切换主行动。 -3. 首页展示最近 2 条参与动态摘要,降低进入二级页查状态的成本。 -4. “我的发布”和“参与记录”入口副文案从泛泛计数改为状态化摘要。 -5. 保持二级页和数据存储接口不变,避免影响地图、发布和详情闭环。 - -## 不做范围 - -- 不新增云端接口、消息通知、关注订阅或复杂时间线。 -- 不改发布、详情、地图、管理台页面。 -- 不重做登录、头像昵称、管理员暗入口。 -- 不引入新依赖或组件库。 - -## 验收清单 - -- 游客进入个人中心时,下一步建议应引导微信登录。 -- 已登录但未补资料时,应优先提示补头像和名称。 -- 已登录且无发布、无参与时,应给出发布或去地图的清晰路径。 -- 有处理中发布时,应优先引导查看我的发布。 -- 有参与记录时,首页应展示最近 2 条动态并可进入参与记录。 -- 统计、入口文案和最近动态由同一 helper 生成,并有脚本覆盖关键状态。 diff --git a/harness/quality-document.md b/harness/quality-document.md deleted file mode 100644 index d4c1109..0000000 --- a/harness/quality-document.md +++ /dev/null @@ -1,43 +0,0 @@ -# 质量文档 - -每个产品领域和架构层的质量快照。agent 和人都能通过这份文档快速了解项目哪里强、哪里弱。 - -**更新时机:** 每轮重要会话结束后,或开始新一阶段工作前。 - -**评级标准:** - -- **A**:验证全部通过,架构干净,agent 能读懂,测试稳定 -- **B**:基础验证通过,结构基本清楚,可读性或测试覆盖有少量缺口 -- **C**:部分可用,有已知验证缺口,部分代码 agent 不容易判断边界 -- **D**:不可用,或存在重大结构问题 - -## 产品领域 - -| 领域 | 评级 | 验证状态 | Agent 可读性 | 测试稳定性 | 关键缺口 | 上次更新 | -| --- | --- | --- | --- | --- | --- | --- | -| 地图浏览 | C | 需要 WeChat DevTools 手动验证 | 页面边界清楚 | 仅 JSON 检查 | 缺自动化/手动证据 | 2026-05-13 | -| 发布任务 | C | 需要 WeChat DevTools 手动验证 | 表单逻辑较集中 | 仅 JSON 检查 | 登录、图片、云端 fallback 需证据 | 2026-05-13 | -| 详情与信任动作 | C | 需要 WeChat DevTools 手动验证 | 状态规则在文档中清楚 | 仅 JSON 检查 | 重复动作、隐藏、过期需证据 | 2026-05-13 | -| 管理后台 | C | 需要 WeChat DevTools 手动验证 | 权限路径需结合云函数读 | 仅 JSON 检查 | admins 集合缺失和管理员态需证据 | 2026-05-13 | -| 我的/动态/反馈 | C | 需要 WeChat DevTools 手动验证 | 页面入口清楚 | 仅 JSON 检查 | 本地状态持久化需证据 | 2026-05-13 | -| Harness | B | `npm run check:json` 和 `node harness/check-harness.mjs` 已通过 | 文件职责清楚 | 有自检脚本 | 还没有小程序 E2E 自动化 | 2026-05-13 | - -## 架构层 - -| 层级 | 评级 | 边界执行 | Agent 可读性 | 关键缺口 | 上次更新 | -| --- | --- | --- | --- | --- | --- | -| 小程序页面层 | C | 页面按 tab/detail/admin 分离 | 文件名清楚,业务改动较多 | 缺页面级自动化验证 | 2026-05-13 | -| `utils/*` 共享层 | C | 存储、配置、格式化、认证集中 | 可读,但云端/本地双路径增加复杂度 | 缺 store 行为测试 | 2026-05-13 | -| 云函数层 | C | `posts` 与 `getMyRole` 分离 | 输入清洗较明确 | 缺云函数本地/集成验证 | 2026-05-13 | -| 配置与项目元数据 | B | `project.config.json` 保持公开占位 appid | 简单清楚 | 需持续防止 private config 入库 | 2026-05-13 | -| Harness 层 | B | 根入口 + `harness/` 状态文件 | 清楚 | 需后续把常见人工 QA 转成可执行检查 | 2026-05-13 | - -## 变更历史 - -### 2026-05-13 - -- 变更内容:创建初始 harness 质量快照 -- 提升:新增统一初始化、功能清单、进度日志、交接和评审文件 -- 下降:无 -- 新发现的缺口:当前没有可自动化的小程序 E2E 或 store 单元测试 -- 已关闭的缺口:agent 开工入口和状态文件缺失 diff --git a/harness/sanitized-summary-checklist.md b/harness/sanitized-summary-checklist.md deleted file mode 100644 index c89445e..0000000 --- a/harness/sanitized-summary-checklist.md +++ /dev/null @@ -1,66 +0,0 @@ -# 手测结果脱敏摘要 QA 检查清单 - -本清单用于检查“本地手测 JSON -> 脱敏 Markdown 摘要”生成器。摘要面向用户评测 agent 阅读,只能呈现结论、状态和可复核的低敏元信息,不能把本地原始证据、真实路径或账号环境细节带入提交物。 - -## 允许进入摘要的字段 - -- 顶层元信息:`schemaVersion`、`branch`、短 `commit`、`testedAt` 日期或时间段、`tester` 的非实名代号。 -- 环境概况:WeChat DevTools 版本或“已手动打开”、基础库版本、设备类型或模拟器/真机标记、网络类型、定位权限状态、CloudBase 是否启用、云函数是否部署、存储是否就绪、本地 storage 是否清理或复用。 -- 总体结论:`summary.overallStatus`、发布/评测建议、已脱敏的摘要备注。 -- journey 概况:`id`、`title`、`status`、高层步骤名称、预期结果摘要、实际结果的一句话结论、风险摘要、后续动作。 -- 证据摘要:证据数量、证据类型、验证命令名称、是否通过、截图或日志是否存在的布尔结论。 -- 聚合统计:通过/失败/阻塞/未覆盖的 journey 数量、最高风险 journey 列表、仍需人工验证的项目。 -- 低敏业务标识:页面路由、功能名、分类名、状态枚举、占位或脱敏后的 post id,例如 ``。 - -## 不允许进入摘要的内容 - -- 未脱敏的 `actual` 原文、原始 `evidence`、日志、截图文字识别内容、控制台完整输出、异常堆栈全文。 -- 本地绝对路径、真实用户名、机器名、工作区私有路径、临时文件路径。 -- 具体 CloudBase 文件 ID、对象存储路径、环境密钥、`cloud://` 后的真实资源标识。 -- token、cookie、session、authorization、password、private key、访问密钥、二维码内容、预览链接中的凭证参数。 -- 手机号、微信号、邮箱、openid、unionid、真实姓名、头像 URL、精确地址、精确经纬度。 -- 生产或类生产环境的数据库记录内容、评论正文、反馈正文、图片内容描述中可能识别个人的信息。 -- 未脱敏的失败复现数据,例如真实 post id、真实用户昵称、真实云环境 id、真实文件名。 -- 会让读者误以为手测已通过的占位内容;示例文件中的 `not_covered` 不能被摘要改写为 `passed`。 - -## 生成前检查 - -- 确认输入文件是被忽略的本地结果文件,例如 `harness/manual-test-results.local*.json`,不要读取或覆盖示例文件作为真实证据。 -- 运行 `node scripts/check-manual-evidence.mjs `,失败时不得生成摘要草稿。 -- 运行 `node scripts/check-evidence-hygiene.mjs`,确认已知证据文件和忽略规则没有卫生问题。 -- 确认 `.gitignore` 已覆盖本地手测 JSON 和 `harness/manual-evidence-artifacts/`。 -- 确认摘要输出路径是 ignored 的本地 Markdown 草稿路径,例如 `harness/manual-test-summary.local*.md`,不包含原始本地 JSON 或附件内容。 -- 逐项映射字段白名单;任何未在白名单中的输入字段默认丢弃。 -- 写入 local JSON 前,应把 `actual`、`risks`、`followUp` 整理成人工可读的脱敏概括;脚本只拦截明显敏感模式,不替代人工脱敏判断。 -- 对路径、资源 ID、账号、联系方式、坐标和正文类数据先替换为占位符,再进入摘要渲染。 -- 若 journey 状态为 `passed`,确认输入里有实际观察和环境信息;若缺失,只能摘要为“证据不足”或“未覆盖”。 - -## 生成后人工检查 - -- 通读 Markdown,确认没有出现本地绝对路径、真实 cloud 资源 ID、token、手机号、账号标识或精确位置。 -- 检查每个 journey 的 `status` 与输入 JSON 一致,没有把 `blocked`、`failed`、`not_covered` 美化成通过。 -- 检查摘要只说明证据“类型和结论”,不包含日志原文、截图内容、评论正文或反馈正文。 -- 检查所有失败和阻塞项都有下一步处理建议,且建议不会要求提交本地 JSON 或附件。 -- 检查仍需 DevTools/真机验证的项目被明确列出,不能用自动脚本通过替代手测通过。 -- 检查占位符清晰一致,例如 ``、``、``、``。 -- 对摘要文件再运行文本敏感词扫描;如未来脚本覆盖该文件,应确保脚本通过后再交付。 -- 用 `git status --short --ignored` 确认 local Markdown 草稿保持 ignored;若要把结论写入可提交报告,再用对应报告 diff 确认没有夹带本地结果 JSON、截图或附件。 - -## 失败时如何处理 - -- 结构校验失败:停止生成,修正本地 JSON;不要在摘要里补造缺失字段。 -- 卫生扫描失败:删除或替换敏感片段,重新运行检查;不要用注释解释敏感内容。 -- 发现摘要含敏感信息:立即删除生成的摘要,重新从脱敏后的中间数据生成。 -- 发现状态与输入不一致:以输入 JSON 和手测记录为准,修正摘要结论;无法确认时标为“证据不足”。 -- 发现手测证据不足:保留 `blocked` 或 `not_covered`,写清阻塞原因和下一步,不得标记为 `passed`。 -- 发现生成器需要更多字段:先更新白名单和脱敏规则,再生成摘要;不要临时透传未知字段。 - -## 仍需手动 DevTools/真机验证的声明 - -脱敏摘要只是阅读材料,不是手测本身。即使摘要生成和自动检查全部通过,以下项目仍必须以 WeChat DevTools 或真机结果为准: - -- 小程序编译、首屏渲染、地图列表和详情跳转。 -- 定位授权允许、拒绝、超时和重试路径。 -- 发布表单状态、底部动作、键盘遮挡、安全区、图片上传失败回滚和发布后详情跳转。 -- TrustInsight 面板、信任动作刷新、评论入口、只读状态和云端评论路径。 -- CloudBase 云函数部署、存储权限、图片跨用户可见性和真实网络条件。 diff --git a/harness/sanitized-summary-product-brief.md b/harness/sanitized-summary-product-brief.md deleted file mode 100644 index e8d0dce..0000000 --- a/harness/sanitized-summary-product-brief.md +++ /dev/null @@ -1,59 +0,0 @@ -# L 组手测结果脱敏摘要产品简报 - -日期:2026-06-14 - -分支:`codex/iter-sanitized-summary` - -对象:已经产生本地手测结果 JSON,并需要给评测或评审提供可读摘要的 agent。 - -## 1. 问题 - -K 组 helper 会生成被 `.gitignore` 忽略的 `harness/manual-test-results.local*.json`,I 组和 J 组分别要求手测证据完整、提交前证据卫生。但评测或评审仍需要一份可读的摘要,快速判断测了哪些旅程、结论是什么、还有哪些风险。 - -直接提交本地手测 JSON 或原始附件不安全:里面可能带有截图原始路径、CloudBase fileID、request id、token、手机号、真实本机路径、精确位置或设备痕迹。L 组要补的是从本地 JSON 生成“脱敏摘要草稿”的产品口径,让可提交内容保留结论,不带入敏感原始材料。 - -## 2. 用户/评审价值 - -- 评审者能不打开本地附件,也能看懂每条手测旅程的状态、结果和发布影响。 -- 后续 agent 能基于摘要继续补测,而不是重新解读完整本地证据包。 -- 提交内容更安全,只保留脱敏后的产品事实、覆盖范围和剩余风险。 -- blocked、not_covered、failed 和 passed 的边界更清楚,减少“自动检查通过”被误读成“真实手测通过”。 - -## 3. 范围内 - -- 从 ignored 的 `harness/manual-test-results.local*.json` 读取手测结果,生成一份 ignored 的 `harness/manual-test-summary.local*.md` 脱敏摘要草稿,供评测/评审阅读。 -- 生成命令示例:`node scripts/create-manual-summary.mjs --input harness/manual-test-results.local.json --out harness/manual-test-summary.local.md`。 -- 摘要应保留:候选分支、提交标识的脱敏展示、测试环境类别、旅程状态、关键步骤摘要、期望/实际差异、发布影响、未覆盖范围、下一步动作。 -- 摘要应把附件写成类型和观察结论,例如“截图本地留存,显示发布后进入详情页”,不输出原文件名、绝对路径或可反查云资源的标识。 -- 摘要应使用稳定但不可反查的占位符关联同一轮数据,例如 `post_redacted_001`、`request_redacted_a`。 -- 摘要应和现有 `scripts/check-manual-evidence.mjs`、`scripts/check-evidence-hygiene.mjs` 串联:先确认本地结果结构,再检查生成内容没有敏感字段。 - -## 4. 非目标 - -- L 组不代表真实 WeChat DevTools 或真机手测已经完成。 -- 不替代 K 组手测入口 helper、I 组证据完整性检查或 J 组证据卫生规则。 -- 不读取、提交、上传或改写原始截图、录屏、完整日志、HAR、数据库截图或 Storage 权限截图。 -- 不把 example JSON、mock 数据、自动脚本输出或人工占位内容包装成真实 evidence。 -- 不新增小程序功能,不改变手测旅程判定标准;只允许为本地摘要草稿补充忽略规则,不改变原始手测 JSON 的忽略策略。 - -## 5. 成功标准 - -- 生成的摘要能让评审者判断:哪些旅程 `passed`、`failed`、`blocked`、`not_covered`,以及每项对发布候选的影响。 -- 摘要不包含真实截图路径、CloudBase fileID、request id、trace id、token、手机号、openid、精确经纬度、真实本机绝对路径、完整设备唯一标识或认证信息。 -- 对每个 `passed` 项,摘要说明本地 evidence 类型和脱敏观察结论,但不泄露原始附件。 -- 对每个 `failed`、`blocked`、`not_covered` 项,摘要说明原因、影响和下一步补测条件。 -- 如果输入是 example、空结果、结构缺失或仅有自动检查,摘要必须明确标为非真实手测证据或证据不足,不能生成“已通过”的口吻。 -- 生成后的摘要能通过脚本内置敏感扫描;若要把摘要结论写入可提交报告,必须先人工复核,且提交时不附带本地 JSON、local Markdown 草稿或原始附件。 - -## 6. 必须明确的边界 - -- 自动检查通过只说明基础脚本或结构校验通过,不说明 WeChat DevTools 或真机旅程通过。 -- `harness/manual-test-results.example.json` 只能作为模板,不是真实手测 evidence。 -- `manual-test-results.local*.json` 是本地原始结果,默认不提交;摘要只能引用脱敏后的产品结论。 -- 原始截图、录屏、控制台日志、云端日志和数据库截图可以作为本地留存证据,但摘要不得暴露它们的原始路径、文件名或可反查 id。 -- CloudBase 未部署、DevTools CLI 超时、定位授权未测、图片上传未测、真机未测等情况必须写为 `blocked` 或 `not_covered`,不能因为生成了摘要而升级为 `passed`。 -- 如果某条结论来自人工占位、样例数据或非真实运行,应在摘要中显式标注“非真实 evidence”,并从发布准入判断中排除。 - -## 7. L 组核心结论 - -L 组定义的是手测结果的脱敏摘要生成口径,不是新的手测结论。工具应帮助 agent 把本地 JSON 中的真实执行结果转成可评审的 local Markdown 草稿,同时拦截明显敏感原始材料。只有真实 DevTools 或真机旅程执行完成,并通过 I 组完整性与 J 组卫生复核后,摘要中的 `passed` 才能支撑发布判断。 diff --git a/harness/session-handoff.md b/harness/session-handoff.md deleted file mode 100644 index c65499b..0000000 --- a/harness/session-handoff.md +++ /dev/null @@ -1,31 +0,0 @@ -# 会话交接 - -## 当前已验证 - -- 现在明确可用的部分:仓库已有 `npm run check:json` 作为 JSON 配置检查;harness 基础文件已放在 `harness/` -- 这轮实际跑过的验证:反馈云端修复后,`node --check utils/feedback.js`、`node --check pages/feedback/feedback.js`、`node --check pages/admin/admin.js`、`node --check cloudfunctions/posts/index.js` 均通过;`git diff --check` 通过;`npm run check:json` 输出 `Checked 11 JSON files.`;`node harness/check-harness.mjs` 输出 `Harness OK: 6 features checked.` - -## 本轮改动 - -- 新增了哪些代码或行为:反馈建议现在优先写入 CloudBase `feedback_items` 集合,并通过 `posts` 云函数的 `createFeedback`/`listFeedback` action 读写;管理台管理员视图集中读取云端反馈;云函数或集合配置异常时会显式提示,不再把真实反馈静默留在本地 -- 基础设施或 harness 发生了哪些变化:`harness/claude-progress.md` 新增反馈云端修复记录 - -## 仍损坏或未验证 - -- 已知缺陷:未发现由本轮代码改动引入的语法错误 -- 未验证路径:WeChat DevTools 编译、普通用户提交反馈、管理员打开管理台看到跨设备反馈、地图/发布/详情/我的其他流程 -- 下一轮会话需要注意的风险:反馈建议线上可用前需要部署更新后的 `posts` 云函数并创建 `feedback_items` 集合;集合缺失时提交和管理台会显式失败,这是有意设计,避免管理员误以为已经集中收到反馈 - -## 下一步最佳动作 - -- 当前用户请求后续动作:部署 `posts` 云函数,创建 `feedback_items` 集合,然后验证真实反馈跨用户可见 -- 为什么它是下一步:代码实现已完成自动化检查,但反馈的核心价值是管理员能看到其他用户设备提交的内容,必须通过云端环境确认 -- 什么结果才算 passing:普通用户在“我的”-“反馈”提交后,管理员账号进入“管理”tab 的“用户反馈”区域能看到类型、内容、昵称和联系方式 -- 这一步中哪些东西不要动:不要把 `MISSING_COLLECTION` 静默回落为本地成功;这会重新引入管理员看不到真实反馈的问题 - -## 命令 - -- 初始化命令:`bash harness/init.sh` -- 基础验证命令:`npm run check:json`,`node harness/check-harness.mjs` -- 语法检查命令:`node --check utils/feedback.js`,`node --check pages/feedback/feedback.js`,`node --check pages/admin/admin.js`,`node --check cloudfunctions/posts/index.js` -- 手动验证入口:WeChat DevTools 打开 `/Users/bytedance/git/x` diff --git a/harness/viral-attribution-events-checklist.md b/harness/viral-attribution-events-checklist.md deleted file mode 100644 index a31b9d6..0000000 --- a/harness/viral-attribution-events-checklist.md +++ /dev/null @@ -1,85 +0,0 @@ -# 传播归因事件设计 / QA Checklist - -日期:2026-06-17 - -## 范围 - -- [ ] 本清单只覆盖传播归因事件的设计、QA 和证据边界,不代表 UI passed evidence。 -- [ ] 本轮不要求新增用户操作入口、弹层、阻断确认或额外分享按钮。 -- [ ] 归因事件用于理解传播链路,不用于识别联系人、微信群或私人关系。 -- [ ] CloudBase 不可用、事件写入失败或网络异常时,不阻断用户打开详情、分享、评论、确认或返回页面。 - -## 允许记录的进入事件 - -- [ ] 低风险 active 任务从 `source=timeline` 进入详情时,可以记录 timeline 进入事件。 -- [ ] 普通分享进入详情时,可以记录 share 进入事件。 -- [ ] 二跳接收者进入详情时,可以记录 receiver 进入事件。 -- [ ] 从评论接力入口进入详情时,可以记录 comment 进入事件。 -- [ ] 从确认接力入口进入详情时,可以记录 confirm 进入事件。 -- [ ] 进入事件只记录必要归因字段,如 post id、入口 source、receiverAction、时间、粗粒度状态和是否低风险。 -- [ ] 进入事件不改变现有落地页文案、按钮顺序或首屏阅读节奏。 - -## 转化事件 - -- [ ] 从分享接收侧完成 confirm 后,可以记录 confirm 转化事件。 -- [ ] 从分享接收侧完成 comment 后,可以记录 comment 转化事件。 -- [ ] 转化事件应能区分来源:timeline、普通 share、receiver、comment、confirm。 -- [ ] confirm/comment 成功后的原有提示优先级不变,不因为打点多弹确认或增加下一步阻力。 -- [ ] 转化事件写入失败时,用户看到的成功反馈、评论刷新、确认数量更新仍按原流程继续。 - -## 风险和关闭态边界 - -- [ ] weak stale、weak report、stale、hidden、resolved、expired、unknown task 都不得因为归因事件而出现鼓励扩散语气。 -- [ ] 风险态或关闭态可以记录谨慎进入或关闭态进入,但不得记录成有效扩散成功。 -- [ ] 关闭态 confirm/comment 不应产生转化事件;如果页面已有只读保护,归因逻辑不得绕开它。 -- [ ] hidden 或权限失败场景不得暴露可推断任务内容、位置或传播对象的信息。 -- [ ] 后续报告中要把低风险 active 与风险 / 关闭态分开统计,避免把“不鼓励扩散”的访问解释为传播增长。 - -## 隐私字段红线 - -- [ ] 事件字段不得包含评论正文。 -- [ ] 事件字段不得包含手机号、微信号、门牌号、详细住址或其他联系方式。 -- [ ] 事件字段不得包含联系人姓名、好友关系、openid / unionid 明文、群名或微信群标识。 -- [ ] 事件字段不得包含精确经纬度;如确需位置语义,只能用粗粒度距离段、城市级或已脱敏区域。 -- [ ] 事件字段不得包含图片 URL、原始分享文案全文、用户输入全文或可反推出个人身份的组合字段。 -- [ ] payload、日志、截图和录屏在提交前也要按同一隐私红线检查。 - -## 用户可见体验 - -- [ ] 记录事件不增加用户点击、授权、确认、等待或重试成本。 -- [ ] 分享、接收、评论、确认的按钮文案和路径保持现有体验,不为了归因暴露技术字段。 -- [ ] CloudBase 慢、不可用或写入报错时,不显示与归因相关的错误 toast。 -- [ ] 低端网络下事件写入不应拖慢详情首屏、评论提交完成态或确认按钮反馈。 -- [ ] 窄屏下不得新增遮挡首屏 guide、正文、评论入口、action strip 或底部按钮的可见元素。 - -## QA 观察点 - -- [ ] 低风险 active timeline 进入:观察落地页仍是“先看状态和评论,再决定确认或补线索”,没有新增阻力。 -- [ ] 普通 share 进入:观察 query / payload 中 source 语义正确,落地页不被 timeline 文案覆盖。 -- [ ] receiver 进入:观察二跳 `source=receiver` 和 `receiverAction` 可区分 confirm/comment。 -- [ ] comment 进入:观察评论接力来源可记录,但不携带评论正文。 -- [ ] confirm 进入:观察确认接力来源可记录,但不把确认信号写成事实证明。 -- [ ] confirm 后转化:观察确认成功、数量更新、后续提示和事件 payload 的顺序与字段。 -- [ ] comment 后转化:观察评论成功、列表刷新、后续提示和事件 payload 的顺序与字段。 -- [ ] 风险 / 关闭态:观察不显示鼓励扩散文案,不产生有效转化事件。 -- [ ] CloudBase 不可用:断网或模拟云失败时,用户动作不被阻断,控制台可见降级或静默失败证据。 - -## 手测证据要求 - -- [ ] 这份 checklist 不是 UI passed evidence;通过静态检查或文档 review 也不能写成 DevTools / 真机通过。 -- [ ] DevTools 或真机仍需要截图 / 录屏记录首屏、后续提示、风险态、窄屏和失败降级表现。 -- [ ] 需要记录关键 query:入口路径、`from=share`、`source`、`receiverAction` 等,但不要记录私人联系人或群信息。 -- [ ] 需要记录关键 payload:事件名、归因字段、状态字段、失败降级字段;payload 必须先脱敏。 -- [ ] 需要记录窄屏观察:guide、action strip、评论入口、底部按钮没有重叠或被新增内容挤压。 -- [ ] 如果无法检查真实 payload,要写明原因、替代观察方式和剩余风险,不能用“看起来正常”替代。 -- [ ] 如果 CloudBase 不可用路径未跑,要标记 `unverified` 或 `blocked`,不能默认通过。 - -## 自动检查建议 - -- [ ] `node --check pages/detail/detail.js` -- [ ] `node --check utils/store.js` -- [ ] `node --check utils/share-receiver.js` -- [ ] `node --check utils/receiver-conversion.js` -- [ ] `node scripts/check-json.mjs` -- [ ] `node harness/check-harness.mjs` -- [ ] `git diff --check -- harness/viral-attribution-events-checklist.md` diff --git a/harness/viral-attribution-events-product-brief.md b/harness/viral-attribution-events-product-brief.md deleted file mode 100644 index 9ca0c30..0000000 --- a/harness/viral-attribution-events-product-brief.md +++ /dev/null @@ -1,138 +0,0 @@ -# 自发裂变传播归因事件产品 Brief - -## 背景与目标 - -Street Tasks 已支持用户把任务详情分享给他人或朋友圈。为了评估“用户侧自发裂变使用”是否带来后续有效动作,需要记录从分享入口进入详情后的最小归因事件。 - -本 brief 只定义产品事件口径,不改业务代码。核心回答: - -- 用户从 `from=share&source=timeline` 进入详情后,是否产生评论、确认、再次转发。 -- 用户从二跳来源 `source=receiver`、`source=comment`、`source=confirm` 进入详情后,是否继续产生评论、确认、再次转发。 -- 后续如何按朋友圈、一跳接收者、评论触发转发、确认触发转发等来源评估 `confirm` / `comment` / `relay`。 - -## 非目标 - -- 不声称真实转化率、评论率或确认率已经提升。 -- 不替代 WeChat DevTools、真机、真实分享链路 evidence。 -- 不读取联系人、微信群、会话名称、好友关系。 -- 不采集评论正文、图片内容、精确经纬度、通讯录、设备指纹。 -- 不把归因事件用于广告定向、用户画像扩展或跨产品追踪。 - -## 最小归因模型 - -一次分享落地页访问形成一个 `attribution_session_id`。它从详情页 `onLoad` 的分享参数生成,并在当前详情页生命周期内串联后续动作。 - -推荐分享路径参数: - -- `from=share`:标记来自分享。 -- `source=timeline|receiver|comment|confirm`:标记这次分享按钮出现或触发的场景。 -- `post_id`:任务 ID。 -- `share_id`:本次分享实例 ID。若无法生成,至少记录 `source` 级别归因。 -- `parent_share_id`:二跳转发时带上上一跳 `share_id`,用于判断 relay 链路。 -- `share_depth`:`1` 表示从原始分享进入,`2` 表示接收者再次转发进入;超过 2 可统一记为 `2_plus`。 - -最小评估口径: - -- 朋友圈带来的有效动作:`entry_source=timeline` 的落地 session 内成功发生 `confirm_success`、`comment_success` 或 `relay_share_success`。 -- 二跳带来的有效动作:存在 `parent_share_id` 或 `share_depth>=2` 的落地 session 内成功发生上述动作。 -- 动作归因窗口:默认只归因当前详情页 session;如需要跨天复访,另行定义短期窗口,不能默认为真实转化提升。 - -## 事件类型 - -| 事件 | 触发含义 | 用途 | -| --- | --- | --- | -| `share_detail_landing` | 用户带 `from=share` 打开详情页,且解析到分享来源参数 | 统计朋友圈、一跳、二跳入口量 | -| `share_detail_loaded` | 详情任务成功加载,且任务未被隐藏 | 排除打开失败或无效任务 | -| `share_detail_blocked` | 分享详情打开但任务不可见、已隐藏或加载失败 | 评估分享落地损耗 | -| `share_confirm_success` | 分享落地 session 内确认任务仍有效成功 | 评估分享带来的信任动作 | -| `share_comment_success` | 分享落地 session 内评论提交成功 | 评估分享带来的线索/提问动作 | -| `share_relay_intent` | 用户在分享落地详情页点击或唤起再次分享 | 评估再次传播意图 | -| `share_relay_success` | 分享路径参数生成并交给微信分享能力 | 评估可归因 relay,不代表接收者已打开 | - -说明:`share_relay_success` 只能表示用户完成分享动作的客户端侧准备,不能代表好友或朋友圈实际曝光。 - -## 字段白名单 - -所有事件只允许采集以下字段。未列入字段默认禁止采集。 - -| 字段 | 示例 | 说明 | -| --- | --- | --- | -| `event_type` | `share_detail_landing` | 事件名 | -| `event_time_ms` | `1781683200000` | 客户端事件时间 | -| `attribution_session_id` | `attr_...` | 当前分享落地 session ID | -| `post_id` | `post_001` | 任务 ID | -| `post_category` | `lost_found` | 任务分类 | -| `post_status` | `active` | 事件发生时任务状态 | -| `from` | `share` | 入口来源,只允许 `share` | -| `entry_source` | `timeline` | 只允许 `timeline`、`receiver`、`comment`、`confirm` | -| `share_id` | `sh_...` | 当前分享实例 ID | -| `parent_share_id` | `sh_...` | 上一跳分享实例 ID,可为空 | -| `share_depth` | `1` / `2` / `2_plus` | 传播深度分桶 | -| `action_result` | `success` / `blocked` / `failed` | 动作结果 | -| `blocked_reason` | `closed_post` | 失败或拦截原因枚举 | -| `is_publisher` | `true` / `false` | 当前用户是否发布者,布尔值 | -| `user_id_hash` | `u_hash_...` | 可选,使用现有用户 ID 的不可逆哈希;禁止原始 openid | -| `distance_bucket` | `0_500m` | 可选粗粒度距离分桶;禁止经纬度 | -| `app_version` | `0.1.0` | 可选,用于排查版本差异 | - -禁止字段: - -- 评论正文、评论图片内容、输入框草稿。 -- 联系人、微信群、聊天会话、好友昵称、群名称。 -- 精确经纬度、详细地址、实时轨迹。 -- 原始 `openid`、手机号、微信号、设备指纹、广告标识。 -- 分享接收者身份或实际曝光人数。 - -## 触发时机 - -### 进入详情 - -- 在详情页 `onLoad` 解析到 `from=share` 且 `source` 属于白名单时,生成 `attribution_session_id`,记录 `share_detail_landing`。 -- 任务数据成功加载后,记录 `share_detail_loaded`,带上 `post_category`、`post_status`、`is_publisher`。 -- 如果任务不存在、隐藏、云端读取失败或本地 fallback 仍失败,记录 `share_detail_blocked`,只带枚举化 `blocked_reason`。 - -### 后续动作 - -- 用户成功确认任务仍有效后,记录 `share_confirm_success`。重复确认被拦截时可以记录 `action_result=blocked` 和 `blocked_reason=duplicate_action`,不记录额外用户信息。 -- 用户成功提交评论后,记录 `share_comment_success`。只记录提交成功,不记录正文长度以外的可识别内容;如需要质量判断,优先使用是否成功提交。 -- 用户从分享落地详情页唤起再次分享时,记录 `share_relay_intent`。 -- 客户端生成新的分享路径参数并交给微信分享能力后,记录 `share_relay_success`,新分享应带 `parent_share_id` 和递增后的 `share_depth`。 - -### 不触发事件 - -- 普通地图、列表、个人页进入详情,不记录本 brief 的分享归因事件。 -- 关闭页、滚动、停留时长、复制文本、查看评论正文,不纳入本版。 -- 微信实际把分享展示给谁、谁所在群聊、是否被朋友圈曝光,本版不记录也不推断。 - -## 评估问题与看板口径 - -最小看板应回答: - -- `entry_source=timeline` 的 `share_detail_loaded` 数量,以及后续 `confirm/comment/relay` 成功率。 -- `entry_source=receiver|comment|confirm` 的二跳落地量,以及后续 `confirm/comment/relay` 成功率。 -- `share_depth=1` 与 `share_depth=2/2_plus` 的动作差异。 -- `share_detail_blocked` 占比,用于发现已隐藏、已关闭、过期或读取失败导致的传播损耗。 - -口径提醒: - -- `share_relay_success` 不等于二跳落地,只有新接收者打开并触发 `share_detail_landing` 才算二跳进入。 -- `confirm/comment` 只能说明分享落地 session 内发生了动作,不能单独证明长期留存或真实线下完成。 -- 没有 `share_id` 时只能做 `source` 级别粗归因,不能做链路级 relay 分析。 - -## 成功验收标准 - -- 产品 brief 文件存在于 `harness/viral-attribution-events-product-brief.md`。 -- brief 明确列出事件类型、字段白名单、触发时机、评估口径、成功验收标准和风险边界。 -- brief 明确覆盖 `from=share&source=timeline` 与 `source=receiver/comment/confirm`。 -- brief 明确不采集评论正文、联系人/群、精确位置、原始 openid 等敏感信息。 -- brief 明确不声称真实转化提升,不替代 DevTools/真机 evidence。 -- 后续研发可按本 brief 在 `utils/store.js` 或独立 analytics 边界内实现,页面代码只负责触发事件,不重复归因逻辑。 - -## 风险边界 - -- 隐私风险:任何新增字段必须先进入白名单;默认拒绝联系人、群聊、正文、精确位置和原始身份标识。 -- 误归因风险:分享参数可能被转发、截图或手动拼接;结果只能作为产品趋势参考。 -- 平台能力边界:微信客户端不提供真实朋友圈曝光、群接收者列表和好友打开明细,不能用客户端事件反推。 -- 数据质量风险:离线、云函数失败、用户清缓存会导致事件缺失;看板需要展示缺失或失败比例。 -- 用户体验风险:事件上报必须异步、失败静默,不阻塞详情加载、评论、确认和分享。 -- 合规边界:若未来需要跨 session 归因、用户级长期分析或更细位置分桶,必须重新评审字段和告知方式。 diff --git a/harness/viral-blocked-evidence-capture-checklist.md b/harness/viral-blocked-evidence-capture-checklist.md deleted file mode 100644 index 9799713..0000000 --- a/harness/viral-blocked-evidence-capture-checklist.md +++ /dev/null @@ -1,276 +0,0 @@ -# 传播链 blocked evidence capture QA Checklist - -- 日期:2026-06-16 -- 分支:`codex/iter-viral-blocked-evidence-capture` -- 角色:Q 组设计/QA agent - -范围:本清单验收“把当前 DevTools port/smoke blocker 写入 ignored local viral journey result JSON,并立即运行 `scripts/check-viral-journey-manual-evidence.mjs `”的 blocked evidence capture 命令。它只记录环境阻塞和证据结构,不执行真实 WeChat DevTools UI 或真机传播链手测,也不能声明 UI passed。 - -## 0. 验收边界 - -- [ ] `blocked` 只表示 DevTools service port、smoke、设备、权限、数据或环境阻塞了真实手测;它不是产品功能 failed,也不是 UI passed。 -- [ ] capture 命令只能写 ignored/local viral journey result JSON,不能写 example 文件、可提交 fixture、截图、录屏或原始日志。 -- [ ] capture 命令必须在写入后自动运行: - - ```bash - node --no-warnings scripts/check-viral-journey-manual-evidence.mjs - ``` - -- [ ] checker 通过只证明 local JSON schema、分支、commit、环境字段、journey 唯一性、blocked 字段和聚合状态合法;不证明任何页面渲染、点击、分享 payload 或真机链路通过。 -- [ ] 若当前 blocker 来自 DevTools port/smoke,七条传播 journeys 全部 `blocked` 是合理结果,因为真实首跳、确认、评论、二跳、风险态、朋友圈菜单、timeline payload 和单页模式观察均无法执行。 - -## 1. 运行前检查 - -- [ ] 确认目录、分支、提交和工作树状态。 - - ```bash - pwd - git branch --show-current - git rev-parse --short HEAD - git status --short --ignored - ``` - - 期望:目录对应 `/tmp/street-tasks-iter-worktrees/viral-blocked-evidence-capture`,分支是 `codex/iter-viral-blocked-evidence-capture`。macOS 可能显示 `/private/tmp/...`,记录时仍使用本轮约定 worktree。 - -- [ ] 跑基础 harness,确认不是仓库基线异常。 - - ```bash - bash harness/init.sh - ``` - -- [ ] 确认 viral manual evidence checker 当前可运行,且无 local 文件时不会误报 passed。 - - ```bash - node --no-warnings scripts/check-viral-journey-manual-evidence.mjs - ``` - - 允许输出 `No viral journey manual evidence files found; nothing checked.`,但必须同时说明这不是 UI passed evidence。 - -- [ ] 确认 `.gitignore` 或本地 exclude 忽略目标输出路径。 - - ```bash - git check-ignore -v harness/manual-test-results.local-viral-journey-blocked.json - ``` - -## 2. 推荐命令 - -以 Q 组新增命令的实际文件名为准,建议命令形态如下: - -```bash -node scripts/capture-viral-journey-blocked-evidence.mjs \ - --out harness/manual-test-results.local-viral-journey-blocked.json \ - --blocker "DevTools service port 9420 unavailable; smoke check blocked" \ - --follow-up "Enable WeChat DevTools service port, reopen the project, then rerun viral journey manual smoke." \ - --force -``` - -验收期望: - -- [ ] 输出文件路径必须匹配 ignored/local viral journey result 模式,例如 `harness/manual-test-results.local-viral-journey*.json`。 -- [ ] 若目标文件已存在且未传 `--force`,命令必须拒绝覆盖。 -- [ ] 命令不得修改真实 UI 代码、example JSON、CloudBase 数据、WeChat DevTools 配置或非 local evidence 文件。 -- [ ] 命令输出必须清楚写明:blocked capture is not UI passed evidence。 -- [ ] 命令完成后必须自动运行 checker,并在输出中显示被检查的 local JSON 路径。 - -## 3. 输出文件必须是 ignored/local - -- [ ] 正向路径示例: - - ```bash - harness/manual-test-results.local-viral-journey-blocked.json - harness/manual-test-results.local-viral-journey-q-blocked.json - ``` - -- [ ] 这些路径必须被 git ignore。 - - ```bash - git check-ignore -v harness/manual-test-results.local-viral-journey-blocked.json - git status --short --ignored - ``` - -- [ ] `git status --short` 的可提交区不得出现 local result JSON、截图、录屏、payload 或原始日志。 -- [ ] 不允许输出到: - - `harness/viral-journey-manual-results.example.json` - - `harness/manual-test-results.viral-journey-blocked.json` - - `harness/viral-blocked-evidence.json` - - 仓库根目录、`scripts/`、`pages/` 或任何未 ignored 路径。 - -## 4. 结果 JSON 关键字段 - -顶层字段: - -- [ ] `schemaVersion` 必须是 checker 接受的真实结果 schema,例如 `viral-journey-manual-results.v1`,不能沿用 example schema。 -- [ ] `branch` 必须等于当前分支 `codex/iter-viral-blocked-evidence-capture`。 -- [ ] `commit` 必须等于当前 HEAD full SHA 或 short SHA。 -- [ ] `testedAt` 必须是可解析时间戳。 -- [ ] `tester` 必须是具体执行者或明确的 local capture 标识。 -- [ ] `environment` 必须记录 DevTools/base library、设备或模拟器、是否真机、网络、CloudBase 状态和数据准备状态。blocked 场景可以写“not reached because DevTools smoke was blocked”,但不能留占位符。 -- [ ] `summary.overallStatus` 必须是 `blocked`。 -- [ ] `summary.recommendation` 或 `summary.notes` 必须声明:没有真实 UI evidence,不能写 UI passed。 -- [ ] `journeys` 必须包含且只包含每个 required journey 一次: - - `first-hop-share-entry` - - `receiver-confirm-conversion` - - `receiver-comment-conversion` - - `second-hop-receiver-source` - - `ordinary-and-risk-entries` - - `timeline-share-channel` - - `timeline-risk-gating` - -每条 journey: - -- [ ] `status` 必须是 `blocked`。 -- [ ] `blocker` 必须非空,并指向当前 DevTools port/smoke blocker。 -- [ ] `followUp` 必须非空,并说明恢复端口、重开 DevTools、换端口、换设备、准备账号/数据 fixture 或重新执行真实手测。 -- [ ] `actual` 必须说明未执行真实 journey 或未观察到 UI,不能写“正常”“通过”“passed”等结论。 -- [ ] `evidence` 可以为空;若填写,只能是 blocker 诊断摘要、local log 摘要或命令输出摘要,不能伪造截图、录屏、payload 或页面观察。 -- [ ] `risks` 应保留或补充真实 UI 未验证风险,例如分享 payload、系统分享面板、二跳 query、评论链路、风险态隐藏 CTA、窄屏换行仍未观察。 - -## 5. 七条 journeys 均 blocked 的合理性 - -- [ ] `first-hop-share-entry` blocked:DevTools port/smoke 未通过时,无法打开首跳分享入口并观察 receiver guide/action strip。 -- [ ] `receiver-confirm-conversion` blocked:无法点击接收者确认,也无法产生真实二跳分享 payload。 -- [ ] `receiver-comment-conversion` blocked:无法提交真实评论,也无法确认评论后的 conversion prompt 或 payload。 -- [ ] `second-hop-receiver-source` blocked:无法从真实系统分享卡片或可信 route 观察二跳接力语境。 -- [ ] `ordinary-and-risk-entries` blocked:无法逐个验证普通入口、stale/report signal、stale/resolved/expired/hidden fixture 是否隐藏接收侧扩散 CTA。 -- [ ] `timeline-share-channel` blocked:无法打开真实系统菜单、检查朋友圈入口、inspect `onShareTimeline` payload 或验证单页模式首屏。 -- [ ] `timeline-risk-gating` blocked:无法逐个验证风险/闭合 fixture 的真实系统菜单是否缺少 `shareTimeline`,也无法记录谨慎标题或 payload 反证。 -- [ ] 全 blocked 时 `summary.overallStatus=blocked` 是唯一合理聚合;`overallStatus=passed` 或任意 journey `passed` 都必须被视为误报。 - -## 6. Capture 后复跑 guard - -每次生成或手工编辑 local JSON 后复跑: - -```bash -node --no-warnings scripts/check-viral-journey-manual-evidence.mjs \ - harness/manual-test-results.local-viral-journey-blocked.json -``` - -随后复跑 readiness 和基础 guard: - -```bash -node --no-warnings scripts/check-devtools-readiness.mjs -node scripts/check-json.mjs -node harness/check-harness.mjs -git diff --check -``` - -收尾前确认 local evidence 仍未进入可提交改动: - -```bash -git status --short --ignored -git check-ignore -v harness/manual-test-results.local-viral-journey-blocked.json -``` - -## 7. 负向测试 - -每个负向测试都应非 0 退出,并给出能定位问题的错误信息。测试后清理对应 local 临时文件。 - -- [ ] 未 ignored 输出路径必须失败。 - - ```bash - node scripts/capture-viral-journey-blocked-evidence.mjs \ - --out harness/viral-blocked-evidence.json \ - --blocker "DevTools service port 9420 unavailable" \ - --follow-up "Enable service port and rerun." - ``` - -- [ ] 已有文件未传 `--force` 必须失败。 - - ```bash - node scripts/capture-viral-journey-blocked-evidence.mjs \ - --out harness/manual-test-results.local-viral-journey-blocked.json \ - --blocker "DevTools service port 9420 unavailable" \ - --follow-up "Enable service port and rerun." \ - --force - - node scripts/capture-viral-journey-blocked-evidence.mjs \ - --out harness/manual-test-results.local-viral-journey-blocked.json \ - --blocker "DevTools service port 9420 unavailable" \ - --follow-up "Enable service port and rerun." - ``` - -- [ ] 篡改 `summary.overallStatus=passed` 必须被 checker 拒绝。 - - ```bash - cp harness/manual-test-results.local-viral-journey-blocked.json \ - harness/manual-test-results.local-viral-journey-bad-overall.json - node --input-type=module <<'NODE' - import { readFileSync, writeFileSync } from 'node:fs'; - const file = 'harness/manual-test-results.local-viral-journey-bad-overall.json'; - const results = JSON.parse(readFileSync(file, 'utf8')); - results.summary.overallStatus = 'passed'; - writeFileSync(file, `${JSON.stringify(results, null, 2)}\n`); - NODE - node --no-warnings scripts/check-viral-journey-manual-evidence.mjs \ - harness/manual-test-results.local-viral-journey-bad-overall.json - ``` - -- [ ] 删除任一 required journey 必须被 checker 拒绝。 - - ```bash - cp harness/manual-test-results.local-viral-journey-blocked.json \ - harness/manual-test-results.local-viral-journey-bad-missing.json - node --input-type=module <<'NODE' - import { readFileSync, writeFileSync } from 'node:fs'; - const file = 'harness/manual-test-results.local-viral-journey-bad-missing.json'; - const results = JSON.parse(readFileSync(file, 'utf8')); - results.journeys = results.journeys.filter((journey) => journey.id !== 'second-hop-receiver-source'); - writeFileSync(file, `${JSON.stringify(results, null, 2)}\n`); - NODE - node --no-warnings scripts/check-viral-journey-manual-evidence.mjs \ - harness/manual-test-results.local-viral-journey-bad-missing.json - ``` - -- [ ] 把任一 journey 从 `blocked` 改成 `passed` 且无真实 evidence 必须被 checker 拒绝。 - - ```bash - cp harness/manual-test-results.local-viral-journey-blocked.json \ - harness/manual-test-results.local-viral-journey-bad-passed.json - node --input-type=module <<'NODE' - import { readFileSync, writeFileSync } from 'node:fs'; - const file = 'harness/manual-test-results.local-viral-journey-bad-passed.json'; - const results = JSON.parse(readFileSync(file, 'utf8')); - const journey = results.journeys.find((item) => item.id === 'first-hop-share-entry'); - journey.status = 'passed'; - journey.actual = 'Passed'; - journey.evidence = []; - results.summary.overallStatus = 'passed'; - writeFileSync(file, `${JSON.stringify(results, null, 2)}\n`); - NODE - node --no-warnings scripts/check-viral-journey-manual-evidence.mjs \ - harness/manual-test-results.local-viral-journey-bad-passed.json - ``` - -- [ ] 清空 blocked journey 的 `blocker` 或 `followUp` 必须被 checker 拒绝。 -- [ ] 写入占位文本,例如 `TODO`、`placeholder`、`待填写`、`Not run`,必须被 checker 拒绝。 -- [ ] 把 example JSON 当成真实结果传给 checker 必须失败。 - - ```bash - node --no-warnings scripts/check-viral-journey-manual-evidence.mjs \ - harness/viral-journey-manual-results.example.json - ``` - -## 8. 汇报口径 - -可以写: - -- [ ] `已生成 ignored local blocked viral journey result JSON,并自动通过 manual evidence checker;该结果只记录 DevTools port/smoke blocker。` -- [ ] `七条 viral journeys 均为 blocked,因为当前环境没有执行真实 WeChat DevTools UI 或真机传播链观察。` -- [ ] `blocked evidence capture 不包含截图、录屏、真实分享 payload 或 UI passed 结论;下一步是恢复 DevTools service port/smoke 后重跑真实手测。` -- [ ] `复跑了 check-viral-journey-manual-evidence、check-devtools-readiness、check-json、check-harness 和 git diff --check。` - -不得写: - -- [ ] `UI passed` -- [ ] `DevTools smoke passed` -- [ ] `七条传播链通过` -- [ ] `checker 通过,所以真实分享 payload 通过` -- [ ] `blocked draft 通过,所以可以发布` -- [ ] `没有截图但视为通过` -- [ ] `端口诊断/ready/preflight 通过,所以用户可见链路通过` - -建议最终交付摘要: - -```markdown -已验收 blocked evidence capture:输出为 ignored local JSON,branch/commit/environment/summary/journeys 字段完整,七条 journeys 均为 blocked,blocker/followUp 非空,evidence 未伪造;capture 后自动运行 viral manual evidence checker。该结果不能声明 UI passed,真实传播链仍需恢复 DevTools service port/smoke 后手测。 -``` diff --git a/harness/viral-blocked-evidence-capture-product-brief.md b/harness/viral-blocked-evidence-capture-product-brief.md deleted file mode 100644 index cd87663..0000000 --- a/harness/viral-blocked-evidence-capture-product-brief.md +++ /dev/null @@ -1,114 +0,0 @@ -# 传播链 DevTools Blocked Evidence 自动落盘 Product Brief - -- 日期:2026-06-16 -- 分支:`codex/iter-viral-blocked-evidence-capture` -- 角色:Q 组产品 agent -- 关联能力:P 组 DevTools 手测准备包,O 组传播链 ignored local 手测证据 gate - -## 目标 - -新增一个由用户明确触发的 capture 命令:当传播链真实 DevTools/真机手测被当前环境阻塞时,把“真实 blocker 诊断”自动写入 ignored local viral journey result JSON,并立刻运行 `scripts/check-viral-journey-manual-evidence.mjs` 校验结构。 - -这个命令要解决的问题不是让 UI 测试自动通过,而是让无法进入真实手测的状态也有可复核、可交接、不会误报 `passed` 的本地证据。capture 后,七条传播 journey 必须全部是 `blocked`,`summary.overallStatus` 必须是 `blocked`,并且输出必须反复说明:这仍不代表 WeChat DevTools 或真机 UI passed。 - -## 非目标 - -- 不修改传播链业务 UI、分享路径、评论、确认、接力提示或风险态逻辑。 -- 不替代 P 组的无副作用准备命令;P 的默认命令仍然不写文件。 -- 不替代 O 组的真实 manual evidence checker;capture 写完后必须继续使用 O 的 checker 验证。 -- 不自动 quit/open WeChat DevTools,不杀进程,不清缓存,不修改 service port、AppID、项目配置、本地 storage 或 CloudBase 数据。 -- 不把 blocked evidence 转成 `passed`、`failed` 或发布通过结论。 -- 不提交 ignored local JSON、截图、录屏、payload 或原始日志。 - -## 目标用户 - -- 准备执行传播链真实手测,但被 DevTools service port、smoke access、设备、项目入口或数据 fixture 阻塞的开发、QA、产品 agent。 -- 需要把“为什么没法测”交接给下一位 agent 的执行者。 -- 评审 blocked 证据的人:他们需要快速判断当前是环境 blocker,而不是产品 journey 已失败或已通过。 - -## 什么时候应该 Capture - -只有在用户明确运行 capture 命令时才应该写文件。推荐场景: - -- P 组准备命令已经显示当前真实环境 blocked,例如 `9420 no listener`、service port 未声明、端口连接失败或 smoke access blocked。 -- 执行者准备开始七条传播 journey,但 DevTools UI、真机、系统分享入口、目标 worktree 或必要 fixture 无法进入。 -- 当前 blocker 已经足够具体,可以写清“阻塞阶段、诊断事实、影响范围、下一步恢复动作”。 -- 需要留下一份 ignored local JSON,让 O 组 checker 证明 blocked evidence 的结构合规。 - -## 什么时候不应该 Capture - -- 只是想预览准备包、列出七条 journey 或检查端口状态;这应继续使用 P 组无副作用命令。 -- 已经进入真实 UI 并观察到产品行为与期望相反;这应写 `failed`,不能用环境 blocked 掩盖真实缺陷。 -- 已经完成真实 DevTools 或真机观察并有证据;这应填写真实 `passed` 或 `failed` evidence,而不是生成全 blocked 文件。 -- blocker 只有猜测,没有可复核诊断事实,例如“感觉 DevTools 有问题”。 -- 输出路径不是 ignored local result,或会覆盖他人的本地结果且未显式 `--force`。 -- 期望把 capture 结果作为 CI 通过、发布通过、UI passed 或真实手测完成的证明。 - -## Blocked Evidence 字段要求 - -capture 生成的 JSON 应复用 O 组 schema:`schemaVersion`、`branch`、`commit`、`testedAt`、`tester`、`environment`、`summary`、`journeys`。它必须写入 ignored local 路径,例如 `harness/manual-test-results.local-viral-journey.json` 或同模式文件。 - -顶层和 `summary` 应包含: - -- 当前分支和 commit,必须匹配运行 capture 时的 HEAD。 -- `testedAt` 使用 capture 发生的 ISO 时间。 -- `tester` 表达本次是 local blocked capture,而不是真实 UI tester 结果。 -- `summary.overallStatus: "blocked"`。 -- `summary.recommendation` 明确说明需要先恢复 DevTools/真机/fixture,再执行真实七条 journey。 -- `summary.notes` 明确说明:所有 journey 都未通过真实 UI 验收,capture 只是 blocked 证据落盘。 - -`environment` 应尽量记录真实诊断,而不是占位符: - -- DevTools service port:端口号、目标 host、是否有 `ide-http-port` 声明、声明值、是否有 listener、IPv4/IPv6 连接结果。 -- port forensics:P 组 `inspect-devtools-port-state` 的 `status`、关键 diagnosis、可见进程摘要、项目路径、命令状态。 -- smoke access:P 组 smoke probe 的 `status`、timeout、HTTP/连接错误、stdout/stderr 摘要。 -- DevTools/project:目标 worktree、是否确认打开过项目、无法确认的原因。 -- device/runtime:模拟器或真机是否可用;无法读取 DevTools 版本、基础库版本、设备型号时,写成“unknown because blocked by ...”,不要留模板占位。 -- CloudBase/data setup:是否已确认 CloudBase、目标 active post、风险态 fixture;若未能进入 UI 或准备 fixture,写明未确认原因。 - -每条 journey 都必须: - -- 保留七个固定 ID:`first-hop-share-entry`、`receiver-confirm-conversion`、`receiver-comment-conversion`、`second-hop-receiver-source`、`ordinary-and-risk-entries`、`timeline-share-channel`、`timeline-risk-gating`。前五个是 O/P 组 receiver 传播基线,后两个是 W 组追加的 timeline 渠道和风险态 no-timeline 证据。 -- `status: "blocked"`。 -- 保留原始 `steps` 和 `expected`,便于恢复后继续真实手测。 -- `actual` 写清未发生真实 UI 观察,例如 DevTools service port/smoke blocked,所以未打开目标页面、未点击 confirm/comment、未检查系统分享 payload、未验证风险态。 -- `evidence` 可放脱敏命令摘要、diagnosis 文本、port/smoke 输出摘要或本地日志路径;不能放 example 文案或虚构截图。 -- `blocker` 写具体阻塞事实,例如 `port 9420 had no listener`、`project did not declare ide-http-port`、`smoke access timed out`。 -- `risks` 写本 journey 因 blocked 仍未判断的用户风险。 -- `followUp` 写下一步恢复条件和恢复后要重跑的命令或真实 journey。 - -## 与 P/O 的关系 - -P/W 组已有 `scripts/prepare-viral-journey-devtools-run.mjs`:它默认无副作用,只输出 port forensics、smoke blocked、dry-run、七条 journey 和下一步。Q 的 capture 命令应消费或复用该诊断口径,但不能改变默认不写文件的安全边界。 - -O/W 组已有 `scripts/prepare-viral-journey-manual-evidence.mjs` 和 `scripts/check-viral-journey-manual-evidence.mjs`:它们负责 ignored local result 的 schema、七条 journey、状态聚合、branch/commit 和 evidence 规则。Q 的 capture 命令应写出 checker 可接受的 blocked JSON,并在写完后自动运行 checker。checker passed 只说明 blocked JSON 合规,不说明 UI passed。 - -Q 的定位是二者之间的显式桥接:把 P 发现的真实环境 blocker,落成 O 能校验的 ignored local blocked result。 - -## 成功、Blocked、Failed 语义 - -`success` 指 capture 命令本身成功完成:读取当前诊断、写入 ignored local JSON、七条 journey 均为 `blocked`、自动运行 manual evidence checker 且通过。success 不代表真实 UI passed,也不代表传播链产品通过。 - -`blocked` 指当前无法执行或完成真实传播链 DevTools/真机 journey。blocked 是结果文件里的产品验收状态,说明发布判断仍不可得,需要先恢复环境或数据条件。blocked 不是产品失败。 - -`failed` 只应出现在已经进入真实 UI 并观察到与 expected 相反的产品行为时。capture 命令不应生成 `failed` journey;如果执行者已经有真实失败观察,应手工填写 failed evidence 并跑 O checker,而不是运行 blocked capture。 - -## 验收标准 - -- 新增一个明确命名的 capture 入口;默认 P 组准备命令仍不写文件。 -- capture 只在用户明确运行时写 ignored local viral journey result,输出路径必须被 git ignore,且默认不覆盖既有结果。 -- capture 能记录当前真实 blocker 诊断,至少覆盖 port forensics、`ide-http-port` 声明/缺失、listener/连接结果、smoke access 结果、项目路径、未能确认的 DevTools/runtime/data 原因。 -- 生成结果中七条 required journey 全部为 `blocked`,`summary.overallStatus` 为 `blocked`,没有任何 `passed` journey。 -- 生成结果保留七条 journey 的步骤和期望,并为每条 journey 写明 `actual`、`blocker`、`risks`、`followUp`。 -- 写入后自动运行 `node --no-warnings scripts/check-viral-journey-manual-evidence.mjs `,并在 checker 失败时返回 failed 命令状态。 -- 命令输出明确区分“capture 成功/blocked evidence 合规”和“真实 UI passed”;不得出现容易让人以为 UI 已通过的文案。 -- 不执行 quit/open/kill/cache/config/storage/CloudBase 修改等副作用动作。 - -## 误读风险 - -- 把 capture success 误读为传播链 passed:必须在命令输出和 JSON summary 中明确否认。 -- 把 blocked 误读为产品 failed:blocked 只说明环境或 fixture 阻止测试,不能据此判定用户体验失败。 -- 把 P 的 dry-run、port forensics 或 smoke blocked 误读为真实 UI evidence:它们只能作为 blocked diagnosis。 -- 把 O checker passed 误读为 evidence passed:checker 只校验结构和聚合状态,blocked 文件通过仍是 `overallStatus=blocked`。 -- 用全 blocked JSON 长期替代真实手测:followUp 必须要求恢复 DevTools/真机后重跑七条 journey,并补真实 evidence。 -- 写入未 ignored 文件或提交本地结果:capture 必须拦截未 ignored 路径,并提醒不要提交 local JSON。 diff --git a/harness/viral-candidate-design-checklist.md b/harness/viral-candidate-design-checklist.md deleted file mode 100644 index f0c9303..0000000 --- a/harness/viral-candidate-design-checklist.md +++ /dev/null @@ -1,41 +0,0 @@ -# 自传播组合候选设计 / QA Checklist - -日期:2026-06-16 -适用范围:发布成功扩散计划 + 普通详情分享提示 - -## 设计取舍 - -- 发布成功上下文更强,因此优先显示 C 组扩散计划。 -- 普通详情页保持轻量,只显示 A 组分享提示,不叠加发布者专属扩散计划。 -- 分享 payload 标题统一走 A 组谨慎 helper,减少不同入口口径漂移。 -- 分享接收页必须是普通详情页,不显示发布者专属卡。 - -## 自动检查 - -- [ ] `node --check pages/detail/detail.js` -- [ ] `node --check utils/share-message.js` -- [ ] `node --check utils/publish-spread.js` -- [ ] `node --check scripts/check-share-message.mjs` -- [ ] `node --check scripts/check-publish-spread.mjs` -- [ ] `node --check scripts/check-viral-candidate.mjs` -- [ ] `node --no-warnings scripts/check-share-message.mjs` -- [ ] `node --no-warnings scripts/check-publish-spread.mjs` -- [ ] `node scripts/check-viral-candidate.mjs` -- [ ] `node scripts/check-json.mjs` -- [ ] `node harness/check-harness.mjs` -- [ ] `git diff --check` -- [ ] `bash harness/init.sh` -- [ ] `npm run check` - -## 手测清单 - -- [ ] 从发布成功进入详情页,只看到扩散计划。 -- [ ] 从地图/列表/分享进入普通详情页,只看到普通分享提示。 -- [ ] 点击发布成功页“转发扩散”,接收路径不携带 `from=publish`。 -- [ ] 点击普通详情页“转发”,接收路径携带 `from=share`。 -- [ ] active、stale、resolved、expired、高举报任务的文案不夸大真实性。 -- [ ] 窄屏下扩散计划、分享提示、TrustInsight、评论区不互相挤压。 - -## 未验证不得声称通过 - -自动检查只证明组合逻辑、静态结构和 helper 输出。真实系统分享面板、接收侧页面、DevTools 编译、真机表现和转发率都需要单独验证。 diff --git a/harness/viral-candidate-product-brief.md b/harness/viral-candidate-product-brief.md deleted file mode 100644 index 768eab4..0000000 --- a/harness/viral-candidate-product-brief.md +++ /dev/null @@ -1,32 +0,0 @@ -# 自传播组合候选产品 Brief - -日期:2026-06-16 -分支:`codex/iter-viral-candidate` - -## 产品假设 - -第一轮评测里,C 组的发布后扩散计划最能抓住高意图裂变窗口,A 组的详情页分享提示最轻、最通用。把两者组合后,发布者刚发完任务时能立即扩散,普通浏览者稍后进入详情页时也能知道这条任务适合转给谁。 - -## 组合范围 - -- 保留 C 组 `from=publish` 专属扩散计划:只给发布者看“先转给 / 想得到 / 稍后回访”。 -- 保留 A 组普通详情分享提示:非发布成功入口显示“转给谁 / 为什么转 / 能帮什么”。 -- 分享 payload 使用 A 组谨慎标题;发布成功场景的分享 path 继续使用 C 组逻辑移除 `from=publish`,让接收者进入普通详情页。 - -## 非目标 - -- 不合入 B 组 relay block,避免同一详情页同时出现过多行动建议。 -- 不增加埋点、奖励、海报、服务端归因或 CloudBase 数据结构。 -- 不声称真实转发率提升;这仍需要手测和后续数据。 - -## 成功信号 - -- 发布成功详情页只显示扩散计划,不再叠加普通分享提示。 -- 普通详情页显示通用分享提示,并使用 `from=share` path。 -- 发布成功分享不会把 `from=publish` 带给接收者。 -- A/C 两组原有 helper 检查和组合候选检查都通过。 - -## 风险 - -- 两套模块共存后,详情页视觉密度仍需 WeChat DevTools 或真机确认。 -- 系统分享面板、接收路径、窄屏布局和带图任务仍未真实验证。 diff --git a/harness/viral-comment-relay-design-checklist.md b/harness/viral-comment-relay-design-checklist.md deleted file mode 100644 index e7f27a9..0000000 --- a/harness/viral-comment-relay-design-checklist.md +++ /dev/null @@ -1,40 +0,0 @@ -# 评论成功后接力提示设计 / QA Checklist - -## 触发与层级 - -- [ ] 只在用户成功提交评论后显示接力提示。 -- [ ] 页面初次进入、从分享进入、从发布成功进入、重新加载和单纯读取评论列表时默认不显示。 -- [ ] 提示模块是评论后的轻量 panel,不使用弹窗,不压住详情主内容、信任判断或评论列表。 -- [ ] 用户可以继续阅读评论;风险状态下没有公开转发 CTA。 - -## 文案与风险 - -- [ ] active 且低风险任务说明“最新线索”与“适合转给谁”。 -- [ ] 新评论正文会被摘要,长评论在窄屏中不撑破布局。 -- [ ] `commentCount` 会进入提示,帮助用户理解评论区线索数量。 -- [ ] `stale` 或 `staleCount >= 3` 时提醒先核对,不鼓励盲转。 -- [ ] `reportCount >= 2` 时提醒有举报风险,不鼓励公开扩散。 -- [ ] `resolved`、`expired`、`hidden` 只作为历史或管理线索,不鼓励继续公开传播。 - -## 视觉与窄屏 - -- [ ] panel 标题、摘要、三行说明和 note 都允许自然换行。 -- [ ] 按钮与说明在窄屏下不互相覆盖,长地名或长评论摘要可折行。 -- [ ] 低风险态和 warn/danger/done 态能被区分,但不使用过重警告样式抢主流程。 -- [ ] 接力按钮使用 `open-type="share"`;风险态使用非分享按钮或只读提示。 - -## 验证 - -- [ ] `node --check utils/comment-relay.js` -- [ ] `node --check pages/detail/detail.js` -- [ ] `node --check scripts/check-comment-relay.mjs` -- [ ] `node --check scripts/check-devtools-readiness.mjs` -- [ ] `node --check scripts/check-viral-candidate.mjs` -- [ ] `node --no-warnings scripts/check-comment-relay.mjs` -- [ ] 既有 share / publish / receiver / candidate 检查通过 -- [ ] `node scripts/check-json.mjs` -- [ ] `node harness/check-harness.mjs` -- [ ] `git diff --check` -- [ ] `npm run check` -- [ ] `bash harness/init.sh` -- [ ] WeChat DevTools 或真机确认评论成功后的 panel、分享按钮、风险态和窄屏换行;未执行时不得声称通过。 diff --git a/harness/viral-comment-relay-product-brief.md b/harness/viral-comment-relay-product-brief.md deleted file mode 100644 index 567d34a..0000000 --- a/harness/viral-comment-relay-product-brief.md +++ /dev/null @@ -1,23 +0,0 @@ -# 评论成功后接力提示产品 Brief - -## 目标 - -用户在任务详情里刚补充评论或线索时,参与意愿最高。本轮在评论提交成功后给出一个轻量接力提示,让用户知道可以把“刚补上的最新线索”转给更可能路过或能核对的人。 - -## 产品假设 - -如果评论成功后立刻提示“这条新线索适合转给谁、为什么要谨慎转、风险状态下不要盲转”,一次评论更可能转化成一次二次传播,同时不会把高风险或已关闭任务继续放大。 - -## 范围 - -- 仅在详情页评论提交成功后显示,不在页面初次加载、重新进入或单纯读取评论列表时显示。 -- 不改变评论持久化、CloudBase fallback、信任动作或普通分享提示。 -- 新增 `utils/comment-relay.js` 统一生成接力提示文案。 -- active 且低风险任务可鼓励接力转发;`stale`、高举报、`resolved`、`expired`、`hidden` 只做谨慎提醒,不鼓励公开扩散。 -- 分享接收路径继续使用 `from=share`,避免接收者看到评论者专属的成功提示。 - -## 风险 - -- 自动检查只能证明 helper 文案和页面静态结构,不证明真实分享面板、按钮层级、窄屏视觉或转化率。 -- 评论后插入 panel 可能改变详情页局部密度,仍需 WeChat DevTools 或真机确认不遮挡评论主流程。 -- 高风险状态的“不鼓励公开扩散”依赖本地状态字段和阈值,云端状态延迟时仍需要真实数据路径验证。 diff --git a/harness/viral-comment-source-design-checklist.md b/harness/viral-comment-source-design-checklist.md deleted file mode 100644 index b989bb4..0000000 --- a/harness/viral-comment-source-design-checklist.md +++ /dev/null @@ -1,38 +0,0 @@ -# 评论接力来源标识设计 / QA Checklist - -## 接收文案 - -- [ ] `entryFrom === 'share' && source === 'comment'` 时,接收侧明确提示“有人刚补了线索”或“先看最新评论”。 -- [ ] 文案里要能看出这不是普通分享,而是评论接力后的二跳入口。 -- [ ] 普通 `from=share` 仍保持原有接收侧语义,不被 comment source 覆盖。 - -## 风险态 - -- [ ] `reportCount >= 2` 时仍然保持谨慎,不因为 `source=comment` 而鼓励公开扩散。 -- [ ] `stale` 或 `staleCount >= 3` 时仍先核对最新情况,不盲转。 -- [ ] `resolved`、`expired`、`hidden` 仍只作为历史或管理参考,不放大为接力入口。 - -## 互斥与布局 - -- [ ] 普通分享面板仍只在 `!showPublishSuccess && !shareReceiverGuide && !commentRelayPrompt && shareMessage` 时显示。 -- [ ] 评论接力、分享接收和发布成功扩散不会在同一屏抢主 CTA。 -- [ ] `source=comment` 文案在窄屏下可自然换行,不压住按钮和说明。 - -## 验证 - -- [ ] `node --check utils/comment-relay.js` -- [ ] `node --check utils/share-receiver.js` -- [ ] `node --check pages/detail/detail.js` -- [ ] `node --check scripts/check-comment-relay.mjs` -- [ ] `node --check scripts/check-share-receiver.mjs` -- [ ] `node --check scripts/check-viral-candidate.mjs` -- [ ] `node --no-warnings scripts/check-comment-relay.mjs` -- [ ] `node --no-warnings scripts/check-share-receiver.mjs` -- [ ] `node --no-warnings scripts/check-viral-candidate.mjs` -- [ ] 既有 share / publish / receiver / candidate 检查通过 -- [ ] `node scripts/check-json.mjs` -- [ ] `node harness/check-harness.mjs` -- [ ] `git diff --check` -- [ ] `npm run check` -- [ ] `bash harness/init.sh` -- [ ] WeChat DevTools 或真机确认 source=comment 的接收文案和窄屏换行;未执行时不得声称通过。 diff --git a/harness/viral-comment-source-product-brief.md b/harness/viral-comment-source-product-brief.md deleted file mode 100644 index ca29223..0000000 --- a/harness/viral-comment-source-product-brief.md +++ /dev/null @@ -1,22 +0,0 @@ -# 评论接力来源标识产品 Brief - -## 目标 - -当用户从评论接力分享卡进入详情页时,让接收侧明确知道这条任务是“刚补了线索”后的二跳入口,而不是普通 `from=share` 引导。 - -## 产品假设 - -如果评论接力路径额外带上 `source=comment`,接收侧就能把“先看最新评论/评论区已有新线索”说得更清楚,二跳用户更容易继续确认或补充,而不是只看到泛化的分享提示。 - -## 范围 - -- 只改评论接力分享路径与接收侧文案,不改评论提交、评论列表、普通分享、发布成功扩散或信任动作。 -- 接收侧仅在 `entryFrom === 'share' && source === 'comment'` 时强化评论接力文案。 -- 高举报、过时、已关闭、已过期、已隐藏仍保持谨慎,不因为来源标识而鼓励盲转。 -- 普通分享面板的互斥规则不回退,仍然只在没有发布成功、分享接收和评论接力提示时显示。 - -## 风险 - -- 来源标识只存在于分享链路中,不能代表真实用户意图,仍需依赖当前任务状态判断文案强度。 -- 自动检查只能覆盖路径和文案字符串,不能证明系统分享面板、窄屏换行或真实二跳行为。 -- 如果未来还有其他接力来源,需要继续区分来源而不是把 `comment` 当成所有转发的默认语义。 diff --git a/harness/viral-confirm-relay-design-checklist.md b/harness/viral-confirm-relay-design-checklist.md deleted file mode 100644 index 756b7fb..0000000 --- a/harness/viral-confirm-relay-design-checklist.md +++ /dev/null @@ -1,29 +0,0 @@ -# 确认后接力设计 / QA Checklist - -## 信息层级 - -- [ ] 页面加载和评论加载时不显示确认接力提示 -- [ ] `confirm` 成功后才显示确认接力提示 -- [ ] `stale` / `report` 成功后只提示已记录和先核对,不提供公开分享 CTA -- [ ] 普通分享面板在 `actionRelayPrompt` 存在时隐藏 -- [ ] 评论接力、确认接力、分享接收和发布后扩散不会同屏竞争主 CTA - -## 文案规则 - -- [ ] 低风险 confirm 只说“确认信号”,不说“已证实” -- [ ] 高举报、过时、已隐藏、已关闭、已过期任务不鼓励公开扩散 -- [ ] `source=confirm` 接收页强调先看确认和评论 -- [ ] 窄屏下标题、摘要、三行说明和按钮能正常换行 - -## 验证 - -- [ ] `node --check utils/action-relay.js` -- [ ] `node --check pages/detail/detail.js` -- [ ] `node --check scripts/check-action-relay.mjs` -- [ ] `node --no-warnings scripts/check-action-relay.mjs` -- [ ] `node --no-warnings scripts/check-comment-relay.mjs` -- [ ] `node --no-warnings scripts/check-share-receiver.mjs` -- [ ] `node --no-warnings scripts/check-viral-candidate.mjs` -- [ ] `npm run check` -- [ ] `bash harness/init.sh` -- [ ] WeChat DevTools 中确认 confirm/stale/report 三种动作后的真实提示和分享面板行为 diff --git a/harness/viral-confirm-relay-product-brief.md b/harness/viral-confirm-relay-product-brief.md deleted file mode 100644 index a639e9c..0000000 --- a/harness/viral-confirm-relay-product-brief.md +++ /dev/null @@ -1,24 +0,0 @@ -# 确认后接力产品 Brief - -## 目标 - -在用户成功确认一条低风险 active 任务后,给出轻量接力提示,把“我确认过”转化为更可信的二次传播。 - -## 产品假设 - -确认动作代表用户刚刚投入了判断成本。如果确认成功后提示“可以转给更可能路过的人继续补线索”,用户更容易把可信信号带到下一位潜在协作者那里。 - -## 范围 - -- 只在信任动作成功后展示提示 -- `confirm` 且低风险 active 任务才提供公开分享 CTA -- `stale`、`report`、高举报、过时、已关闭、已过期、已隐藏只显示谨慎提示 -- 分享路径带 `source=confirm`,接收侧可以识别这是确认接力 -- 不改存储层、不新增依赖、不改变已有评论接力和发布扩散逻辑 - -## 风险 - -- 确认后立刻转发可能被理解成“已经完全属实”,文案必须只说确认信号 -- stale/report 后不应鼓励公开扩散 -- 同一屏不能和普通分享、评论接力、接收侧引导竞争主 CTA -- 真实分享面板、窄屏布局和接收页语境仍需 WeChat DevTools 或真机验证 diff --git a/harness/viral-devtools-journey-run-checklist.md b/harness/viral-devtools-journey-run-checklist.md deleted file mode 100644 index a0a6665..0000000 --- a/harness/viral-devtools-journey-run-checklist.md +++ /dev/null @@ -1,229 +0,0 @@ -# 传播链 DevTools 真实手测启动 QA Checklist - -日期:2026-06-16 - -范围:用于 P 组在 `codex/iter-viral-devtools-journey-launch` 分支验收“传播链真实手测 DevTools 启动诊断包”,并在 W 组扩展为七条 required journey 后继续作为 readiness 文档。本清单帮助测试者在进入真实 WeChat DevTools 或真机前,先读懂诊断输出、证据草稿和七条传播 journey,避免把 readiness、dry-run、blocked draft 或端口诊断误写成 UI passed。 - -## 0. 运行前检查 - -- [ ] 确认工作区和分支。 - - ```bash - pwd - git branch --show-current - git rev-parse --short HEAD - git status --short --ignored - ``` - - 期望:工作区是 `/tmp/street-tasks-iter-worktrees/viral-devtools-journey-launch`,分支是 `codex/iter-viral-devtools-journey-launch`。macOS 可能把 `/tmp` 显示为 `/private/tmp`,记录时仍写本轮约定 worktree 路径。 - -- [ ] 跑基础 harness,确认不是仓库基线异常。 - - ```bash - bash harness/init.sh - ``` - - 期望:JSON、harness 和既有 local blocked summary preflight 通过。若失败,先记录 baseline blocker,不继续把 DevTools 或 journey 写成 ready。 - -- [ ] 确认本轮命令默认无副作用。 - - 不 quit/open WeChat DevTools。 - - 不 preview/upload。 - - 不杀进程、不清缓存、不改 Service Port 设置。 - - 不改 AppID、`project.private.config.json`、本地 storage 或 CloudBase 数据。 - -- [ ] 确认真实证据落点只使用 ignored/local 路径。 - - 推荐结果文件:`harness/manual-test-results.local-viral-journey.json`。 - - 允许匹配:`harness/manual-test-results.local-viral-journey*.json` 或 `harness/local-viral-journey-results*.json`,前提是 `git check-ignore` 能确认被忽略。 - - 截图、录屏、payload、原始日志和临时草稿不得放入可提交路径;只写脱敏摘要到可提交文档。 - -## 1. 启动诊断命令输出该看什么 - -- [ ] 运行 P 轮准备命令。 - - ```bash - node scripts/prepare-viral-journey-devtools-run.mjs - ``` - - 若需要指定端口或输出草稿路径: - - ```bash - node scripts/prepare-viral-journey-devtools-run.mjs \ - --project /tmp/street-tasks-iter-worktrees/viral-devtools-journey-launch \ - --port 9420 \ - --out harness/manual-test-results.local-viral-journey.json - ``` - -- [ ] 看 `Read-Only DevTools Port Forensics` 分段。 - - `status: ready`:只表示 service port 看起来可连接,可以准备打开 DevTools UI 或真机继续手测;它不表示页面已渲染,也不表示任何 journey passed。 - - `status: blocked`:记录端口、进程、listener、connection refused 或多实例归属等环境 blocker;不要写成产品 UI failed。 - - `status: unknown`:记录信息不足或归属不清;先补只读诊断,不进入 UI passed 结论。 - - 关注 `no DevTools quit/open commands were run` 等安全边界,确认命令没有产生 GUI 副作用。 - -- [ ] 看 `Ignored Local Draft Dry Run` 分段。 - - `--dry-run` 只说明会准备哪个 ignored local draft、会填哪些字段、下一步怎么跑。 - - dry-run 不写文件,不代表 blocked draft 已创建,不代表真实手测已执行。 - - 若需要真实 blocked draft,必须由后续明确命令或人工创建,并仍放在 ignored/local 路径。 - -- [ ] 看 `Existing Ignored Local Evidence Scan` 分段。 - - `No viral journey manual evidence files found; nothing checked.` 是正常的“无本地结果”状态,但不是 UI passed。 - - 若检查到 local 结果文件通过,只说明 schema、分支、commit、环境字段、journey 唯一性、状态聚合和证据字段符合 gate;仍需人工复核截图、录屏、payload 或日志是否真实来自 DevTools/真机。 - - 若出现未 ignored 路径、模板文件、占位符、缺少 evidence 或 share payload 错误,先修本地结果文件,再复跑 checker。 - -- [ ] 看 `Viral Journey Manual Run Package` 和 `Next Steps`。 - - 必须列出七条 journey:`first-hop-share-entry`、`receiver-confirm-conversion`、`receiver-comment-conversion`、`second-hop-receiver-source`、`ordinary-and-risk-entries`、`timeline-share-channel`、`timeline-risk-gating`。 - - `Next Steps` 若要求先恢复端口,就保持 blocked,不进入 UI 手测结论。 - - 输出最后的提醒“not UI passed evidence”必须保留在汇报口径中。 - -## 2. Blocked 时怎么记录 - -- [ ] blocked 只能表示“无法继续真实手测或无法得出 UI 结论”,不能表示产品功能失败。 -- [ ] blocked 记录至少包含: - - `branch`:当前分支。 - - `commit`:当前 HEAD。 - - `worktree`:本轮 worktree。 - - `testedAt`:日期时间和时区。 - - `portStatus`:`blocked` 或 `unknown`。 - - `blocker`:具体原因,例如 `9420 no LISTEN`、`connection refused`、`multiple DevTools instances ambiguous`、`DevTools UI unavailable`、`share payload cannot be inspected on current device`。 - - `impact`:哪些 journey 未执行。 - - `followUp`:启用 Service Port、重开 DevTools、换端口、换机器、准备登录/数据 fixture、或改用真机复测。 - - `evidenceLocation`:ignored local 文件或本地附件编号。 - -- [ ] 若写入 local JSON,每个被阻塞 journey 使用 `status: "blocked"`,并填写非空 `blocker` 和 `followUp`。 -- [ ] `summary.overallStatus` 应随 journey 聚合为 `blocked`,不要手改成 `passed`。 -- [ ] blocked draft 的 `evidence` 可以为空;这表示没有真实 UI evidence,不要为了过审填假截图、假 payload 或占位路径。 -- [ ] 不要把以下内容写成 passed: - - 端口 forensics 成功。 - - readiness/preflight 成功。 - - dry-run 成功。 - - blocked draft 创建成功。 - - evidence checker 对 blocked 文件通过。 - -## 3. Ready 后进入七条 journey 要确认什么 - -- [ ] 进入真实手测前,记录环境字段。 - - WeChat DevTools 版本、基础库版本、模拟器/真机型号、微信版本或设备系统、网络、CloudBase 是否启用、`posts` 云函数是否部署。 - - 记录测试数据来源:mock/local storage/CloudBase;记录 active 低风险任务、stale signal、report signal、stale/resolved/expired/hidden fixture 的 post id。 - -- [ ] `first-hop-share-entry`:首跳从分享进入低风险 active 任务。 - - 打开 `/pages/detail/detail?id=&from=share`。 - - 确认 receiver guide 可见。 - - 确认 receiver action strip 可见,且按钮是 confirm/comment 行动,不是直接分享 CTA。 - - 确认普通分享面板没有在同一状态竞争展示。 - - 记录页面截图/录屏、post id、设备/模拟器、实际文案和可见区域。 - -- [ ] `receiver-confirm-conversion`:接收者确认后的二跳提示。 - - 从首跳分享详情页点击 receiver confirm action。 - - 确认确认动作真实成功;若同一用户已确认导致重复动作被拦截,应换账号/storage 或记录 blocked。 - - 确认 `receiverConversionPrompt` 出现。 - - 确认 `actionRelayPrompt` 没有抢占主 CTA。 - - 检查二跳分享 payload,路径必须包含 `from=share&source=receiver&receiverAction=confirm`;无法检查时写明具体原因。 - -- [ ] `receiver-comment-conversion`:接收者评论后的二跳提示。 - - 从首跳分享详情页打开评论弹窗并提交有效评论。 - - 确认评论真实提交成功,记录本地 storage 或 CloudBase 路径。 - - 确认 `receiverConversionPrompt` 出现。 - - 确认 `commentRelayPrompt` 没有抢占主 CTA。 - - 检查二跳分享 payload,路径必须包含 `from=share&source=receiver&receiverAction=comment`;无法检查时写明具体原因。 - -- [ ] `second-hop-receiver-source`:二跳接收者看到接力语境。 - - 优先通过真实系统分享卡片进入;无法操作时可直接打开 `/pages/detail/detail?id=&from=share&source=receiver&receiverAction=confirm` 和 `...&receiverAction=comment`,但必须标注这是 direct route 辅助。 - - 确认 receiver guide 标题或摘要表达“有人接力了任务”的语境。 - - 确认 `receiverAction=confirm` 文案强调上一位刚确认,`receiverAction=comment` 文案强调上一位刚补线索或最新评论。 - - 确认普通分享面板没有在同一状态展示。 - - 记录入口来源、route/payload、实际文案和面板状态。 - -- [ ] `ordinary-and-risk-entries`:普通入口和风险态不鼓励接收侧扩散。 - - 打开同一 active 低风险任务的普通详情入口,不带 `from=share`。 - - 打开存在 stale signal、report signal 的分享入口。 - - 打开 stale、resolved、expired、hidden fixture;hidden 可能需要 controlled fixture 或 admin/setup,无法访问时记录 blocked。 - - 确认普通入口不展示 receiver guide 或 receiver action strip。 - - 确认有 stale/report 信号时隐藏 receiver action strip。 - - 确认 stale/resolved/expired/hidden 不暴露接收侧 public relay CTA。 - - 记录每个 fixture 的 post id、状态、`staleCount`、`reportCount`、闭合原因和 UI 观察。 - -- [ ] `timeline-share-channel`:低风险 active 详情页朋友圈系统渠道。 - - 打开同一 active、非 stale、非 reported、非 closed 的任务详情页。 - - 打开真实微信系统菜单,确认同时可见“发送给朋友”和“分享到朋友圈”。 - - 触发或 inspect `onShareTimeline`;若 query 可见,必须包含任务 `id`、`from=share`、`source=timeline`、`shareChannel=timeline`。 - - 记录 `title`、`query`、`imageUrl` 或具体无法 inspect 的字段和原因。 - - 从朋友圈卡片、DevTools 单页模式或等价入口进入,确认首屏标题、地点/距离、状态、正文、图片或占位和接收语境可读。 - - 记录菜单、payload 和单页首屏证据;这条不能替代前五条 receiver journeys。 - -- [ ] `timeline-risk-gating`:风险和闭合任务不开放鼓励性朋友圈。 - - 打开弱 stale/report、`stale`、`resolved`、`expired`、`hidden`、unknown 或远端刷新失败 fixture。 - - 打开真实微信系统菜单,确认没有鼓励性 `shareTimeline` / “分享到朋友圈”入口。 - - 若仍能 inspect 分享标题、query 或页面文案,确认它们使用谨慎语义,不鼓励继续扩散。 - - 如果菜单无法打开、payload 无法 inspect、fixture 无法准备或设备不支持当前检查,只能记录 blocked 和 follow-up,不能写 passed。 - - 记录每个 fixture 的 post id、状态、`staleCount`、`reportCount`、闭合原因、菜单缺失证据和谨慎文案观察。 - -## 4. Share payload 怎么记录 - -- [ ] payload 证据优先记录结构化字段,不粘贴无关隐私或完整系统日志。 - - `journeyId` - - `postId` - - `sourceAction`:`confirm`、`comment`、`receiver` 或实际入口。 - - `title`:脱敏后的分享标题摘要。 - - `path`:必须包含页面路径和 query;二跳 conversion 必须看到 `from=share&source=receiver`,confirm/comment conversion 还必须分别看到 `receiverAction=confirm/comment`。 - - `query`:拆出的 `id`、`from`、`source`。 - - `captureMethod`:DevTools share hook、真机分享卡片、控制台日志、截图/录屏或无法检查原因。 - -- [ ] 结果 JSON 中可以写: - - ```json - "sharePayload": { - "path": "/pages/detail/detail?id=post_001&from=share&source=receiver&receiverAction=confirm", - "title": "脱敏后的分享标题摘要" - } - ``` - -- [ ] 若系统环境无法暴露 payload,必须写 `sharePayloadInspection`,并包含具体原因,例如“真机系统分享面板未暴露 path,已用录屏记录点击路径;payload 待 DevTools hook 复核”。不要只写“无法检查”四个字。 -- [ ] 不提交二维码、token、cookie、openId、unionId、真实头像 URL、真实昵称、精确经纬度、完整 CloudBase fileID 或完整 console/network 日志。 - -## 5. 修改 local 结果后复跑哪些 guard - -- [ ] 每次修改 viral journey local 结果文件后复跑: - - ```bash - node --no-warnings scripts/check-viral-journey-manual-evidence.mjs \ - harness/manual-test-results.local-viral-journey.json - ``` - -- [ ] 若只想扫描所有 ignored local viral journey 结果: - - ```bash - node --no-warnings scripts/check-viral-journey-manual-evidence.mjs - ``` - -- [ ] 复跑 readiness,确认新 local 结果不会破坏启动前检查: - - ```bash - node --no-warnings scripts/check-devtools-readiness.mjs - ``` - -- [ ] 跑基础 closeout guard: - - ```bash - node scripts/check-json.mjs - node harness/check-harness.mjs - git diff --check - ``` - -- [ ] 最后确认 local evidence 未进入可提交改动: - - ```bash - git status --short --ignored - git check-ignore -v harness/manual-test-results.local-viral-journey.json - ``` - - 期望:local 结果和附件只显示为 ignored;可提交改动中不得出现真实截图、录屏、payload、日志或 local JSON。 - -## 6. 汇报口径 - -- [ ] 可以写:`DevTools 端口诊断 ready,已进入真实手测待记录 UI evidence`。 -- [ ] 可以写:`DevTools service port blocked,七条传播 journey 未执行,blocked draft/evidence gate 仅记录环境阻塞`。 -- [ ] 可以写:`ignored local viral journey evidence schema 通过,仍需人工复核真实截图/录屏/payload`。 -- [ ] 不得写:`readiness 通过,所以 UI passed`。 -- [ ] 不得写:`dry-run 通过,所以 DevTools recovered`。 -- [ ] 不得写:`blocked draft 通过,所以 journey passed`。 -- [ ] 不得写:`没有 local evidence 文件,所以默认通过`。 -- [ ] 不得把 direct route 辅助打开伪装成真实系统分享卡片进入;两者都可记录,但必须区分。 diff --git a/harness/viral-devtools-journey-run-product-brief.md b/harness/viral-devtools-journey-run-product-brief.md deleted file mode 100644 index 7b824f2..0000000 --- a/harness/viral-devtools-journey-run-product-brief.md +++ /dev/null @@ -1,153 +0,0 @@ -# 传播链真实手测 DevTools 启动诊断包 Product Brief - -日期:2026-06-16 - -分支:`codex/iter-viral-devtools-journey-launch` - -工作目录:`/tmp/street-tasks-iter-worktrees/viral-devtools-journey-launch` - -## 目标 - -为传播链真实手测提供一个可运行的启动诊断包,把执行顺序、证据准备、证据校验和 blocker 语义收拢到同一个入口。P 轮目标不是证明任何 UI journey 已经 passed,而是让执行者在进入真实 WeChat DevTools 或真机手测前,先得到一份稳定的 readiness 报告: - -1. 先运行只读 DevTools service port 诊断,确认当前能否进入真实手测。 -2. 再准备或校验 ignored local viral journey evidence draft,保证手测结果只落在本地忽略文件中。 -3. 最后输出七条必跑传播 journey、当前状态和下一步,不让 blocked、failed、passed 被混写。 - -推荐运行包应呈现为一个单一命令入口,例如 `npm run check:viral-devtools-journey` 或等价脚本包装。该入口可以调用既有脚本,但必须保持可审计、无默认破坏性副作用,并在输出中明确区分“环境 readiness”和“真实 UI passed evidence”。 - -## 非目标 - -- 不新增或修改传播链 UI、分享策略、评论、确认、风控或页面逻辑。 -- 不把 WeChat DevTools、真机、GUI 依赖加入默认 CI。 -- 不自动 quit/open WeChat DevTools,不杀进程,不清缓存,不修改 service port、AppID、项目配置或本地 storage。 -- 不提交真实截图、录屏、payload、日志或 ignored local 手测结果文件。 -- 不把 `scripts/inspect-devtools-port-state.mjs`、`scripts/check-devtools-smoke-access.mjs`、`scripts/recover-devtools-service-port.mjs --dry-run`、evidence draft 准备或 evidence checker 通过写成 DevTools/真机 UI passed。 - -## 目标用户 - -- 准备执行传播链真实手测的开发、QA、产品 agent。 -- 需要判断当前 blocker 是 DevTools 环境问题、证据文件问题,还是已进入产品 journey 后发现真实缺陷的评测者。 -- 后续接手 agent:他们应能只看运行包输出和 ignored local evidence 文件,就知道下一步是恢复 DevTools、补手测证据、修产品缺陷,还是进入验收复核。 - -## 必须守住的证据口径 - -- `readiness` 只表示运行包完成了诊断、草稿准备或 schema 校验;它不表示 UI passed。 -- `diagnostic` 只表示端口、CLI、进程、监听、连接等环境事实;它不表示 DevTools 已能渲染项目,也不表示任何小程序页面通过。 -- `dry-run` 只表示会做什么、跳过了什么、下一步是什么;它不表示恢复动作已执行,更不表示恢复成功。 -- `harness/viral-journey-manual-results.example.json` 永远只是模板,不能作为真实手测结果。 -- 真实结果只能写入 ignored local 文件,例如 `harness/manual-test-results.local-viral-journey.json`;未 ignored 的本地结果必须被拦截。 -- `passed` 必须来自真实 DevTools 或真机观察,并带有具体 `actual`、非空 evidence,以及相关 journey 所需的 share payload 或无法检查 payload 的明确说明。 -- 没有本地结果文件时,运行包可以 exit 0,但只能输出“没有检查任何真实 UI 结果”;不能生成 `passed` 结论。 -- blocked draft 是合理的准备产物,但它只能表达“尚未完成真实手测”或“被具体环境阻塞”,不能表达 UI 通过。 - -## 运行顺序 - -运行包应按固定顺序输出分段结果: - -1. `DevTools service port forensics`:调用 `scripts/inspect-devtools-port-state.mjs`,默认只读。输出 `status: ready | blocked | unknown`、关键 diagnosis、端口监听/连接/进程声明摘要和安全边界。 -2. `Smoke access probe`:调用 `scripts/check-devtools-smoke-access.mjs` 的无副作用模式,确认 service port 是否可作为手测入口。若 blocked,应把原因写成 DevTools 环境 blocker。 -3. `Recovery dry-run hint`:可调用或提示 `scripts/recover-devtools-service-port.mjs --dry-run`。输出必须强调 quit/open/cache/config 均未改变;若需要真实恢复,必须由人工另行确认。 -4. `Viral journey evidence draft`:调用 `scripts/prepare-viral-journey-manual-evidence.mjs --dry-run` 或在用户显式请求时创建 ignored blocked draft。默认不覆盖已有本地文件。 -5. `Viral journey evidence check`:调用 `scripts/check-viral-journey-manual-evidence.mjs`,无文件时通过但声明未检查 UI;有 ignored local 文件时校验 schema、分支、commit、环境、七条 journey、状态聚合和 evidence 字段。 -6. `Required journeys and next step`:总是列出七条必跑 journey,并根据前面结果给出下一步:恢复 DevTools、创建/补全 evidence draft、执行真实手测、修复真实产品失败,或复核 passed evidence。 - -## 七条 journey 在 run package 中的呈现 - -运行包不需要展开模板里的每个步骤全文,但必须以固定顺序展示七条 journey 的 ID、中文名称、入口、关键观察点、证据要求和当前本地结果状态。前五条是 P 轮定义的 receiver 传播基线;W 轮继续追加两条 timeline journey,用来验证朋友圈系统渠道和风险态 no-timeline 边界。 - -### `first-hop-share-entry` - -- 名称:首跳从分享进入低风险 active 任务。 -- 入口:`/pages/detail/detail?id=&from=share`。 -- 关键观察:分享接收者 guide 可见,receiver action strip 可见,普通分享面板不在同一状态竞争展示,strip 上是 confirm/comment 行动而不是直接扩散按钮。 -- 证据要求:真实页面截图/录屏或等价 UI 观察记录,包含 post id、设备/模拟器信息和实际可见区域。 - -### `receiver-confirm-conversion` - -- 名称:接收者确认后的二跳提示。 -- 入口:首跳分享详情页,点击 receiver confirm action。 -- 关键观察:确认成功后显示 receiver conversion prompt,`actionRelayPrompt` 不抢占主 CTA;二跳分享路径包含 `from=share&source=receiver&receiverAction=confirm`。 -- 证据要求:确认动作前后的 UI 证据,以及 share payload;若系统环境无法暴露 payload,必须写明无法检查的具体原因。 - -### `receiver-comment-conversion` - -- 名称:接收者评论后的二跳提示。 -- 入口:首跳分享详情页,提交有效评论。 -- 关键观察:评论成功后显示 receiver conversion prompt,`commentRelayPrompt` 不抢占主 CTA;二跳分享路径包含 `from=share&source=receiver&receiverAction=comment`。 -- 证据要求:评论提交路径、评论成功后的 UI 证据、storage/cloud 路径说明和 share payload 或无法检查 payload 的明确说明。 - -### `second-hop-receiver-source` - -- 名称:二跳接收者看到接力语境。 -- 入口:`/pages/detail/detail?id=&from=share&source=receiver&receiverAction=`,优先来自真实系统分享卡片,无法操作时可用直接 route 辅助,但必须标注差异。 -- 关键观察:接收者 guide 文案表达“有人接力了任务”,摘要和 rows 引导下一位接收者先看确认与评论,普通分享面板不在同一状态展示。 -- 证据要求:二跳入口来源、route/payload、实际文案和面板状态证据。 - -### `ordinary-and-risk-entries` - -- 名称:普通入口和风险态不鼓励接收侧扩散。 -- 入口:同一 active 低风险任务的普通详情入口,以及 stale/report signal、stale/resolved/expired/hidden 等风险或闭合状态 fixture。 -- 关键观察:普通入口不展示 receiver guide/action strip;有 stale/report 信号时隐藏 receiver action strip;闭合状态不暴露接收侧 public relay CTA;风险态文案提醒先看评论、确认或最新状态。 -- 证据要求:每个 fixture 的 post id、状态、`staleCount`/`reportCount` 或闭合原因,以及对应 UI 观察证据。 - -### `timeline-share-channel` - -- 名称:低风险 active 详情页朋友圈系统渠道。 -- 入口:`/pages/detail/detail?id=`。 -- 关键观察:真实系统菜单同时有“发送给朋友”和“分享到朋友圈”;`onShareTimeline` 或等价可 inspect 信息包含 `id`、`from=share`、`source=timeline`、`shareChannel=timeline`;朋友圈或等价单页模式首屏可读。 -- 证据要求:菜单截图/录屏、timeline title/query/image 信息或明确 payload inspection 限制、单页模式首屏观察,且同一证据包仍保留前五条 receiver journey。 - -### `timeline-risk-gating` - -- 名称:风险和闭合任务不开放鼓励性朋友圈。 -- 入口:弱 stale/report signal、`stale`、`resolved`、`expired`、`hidden` 和 unknown fixture。 -- 关键观察:真实菜单中不出现鼓励性 `shareTimeline` / “分享到朋友圈”;任何可 inspect 标题或页面文案保持谨慎,不出现鼓励扩散 CTA。 -- 证据要求:每个风险 fixture 的 post id、状态、stale/report 计数或闭合原因、菜单缺失证据和谨慎语义观察;如果菜单无法打开或无法 inspect,只能记为 blocked,不能写 passed。 - -## 状态语义 - -### `success` - -运行包本身的 success 表示启动诊断和 evidence gate 已按顺序完成。它可以出现在以下情况: - -- DevTools 端口 ready,且 evidence checker 找到并校验通过 ignored local 结果文件。 -- DevTools 端口 blocked,但运行包成功产出 blocker 解释、blocked draft 下一步和七条必跑 journey。 -- 没有本地 evidence 文件,但 checker 明确输出“nothing checked”,并声明这不是 UI passed。 - -因此,run package success 不是产品 journey passed。只有 ignored local evidence 中七条 required journey 全部 `passed`、证据完整、`summary.overallStatus` 聚合为 `passed`,才可以进入“候选 UI passed evidence 待人工复核”。 - -### `blocked` - -`blocked` 表示当前无法继续真实手测或无法得出 UI 结论,但尚未证明产品行为失败。典型 blocker: - -- DevTools service port 被声明但无 listener,或 `127.0.0.1` / `::1` 连接失败。 -- DevTools CLI 可执行但服务端口不可用。 -- 需要人工启用 Service Port、换端口、换机器、重新打开项目或确认当前 UI 状态。 -- ignored local evidence 文件不存在、只有 blocked draft,或被具体设备/登录态/数据 fixture/CloudBase 状态阻塞。 -- 需要系统分享面板或真机能力才能检查 payload,但当前环境不可用。 - -blocked 必须带 `blocker` 和 `followUp`。不要把 DevTools 环境 blocker 写成产品 failed。 - -### `failed` - -`failed` 只用于已经进入真实 DevTools 或真机 journey,并观察到与 expected 相反的产品行为。典型失败: - -- 首跳分享入口未显示应有 receiver guide/action strip。 -- confirm/comment 成功后没有 receiver conversion prompt,或错误 CTA 抢占主入口。 -- 二跳 payload 缺少 `from=share&source=receiver`,或 confirm/comment conversion 缺少对应的 `receiverAction=confirm/comment`,且环境允许检查 payload。 -- 风险态或闭合态仍暴露接收侧扩散 CTA。 - -failed 必须带具体 `actual`、可复查 evidence 和下一步 `followUp`。没有真实 UI 观察时不能写 failed。 - -## 验收标准 - -- brief 文件存在于 `harness/viral-devtools-journey-run-product-brief.md`,且不要求修改业务代码。 -- 后续实现的运行包有单一入口,并按“只读端口诊断 -> smoke access probe -> recovery dry-run hint -> evidence draft 准备/预览 -> evidence checker -> 七条 journey 和下一步”的顺序输出。 -- 输出中清楚区分 run package success、DevTools blocked、evidence blocked、journey failed 和 journey passed。 -- 默认模式不执行 quit/open/kill/cache/config/storage 等副作用动作;任何带副作用恢复都必须由用户显式另行触发。 -- 无 ignored local evidence 文件时,命令 exit 0 但明确说明没有检查真实 UI 结果,且不生成 passed 结论。 -- 有 ignored local evidence 文件时,必须校验 schema、当前 branch/commit、环境字段、七条 required journey 唯一性、状态字段、evidence 要求、share/timeline payload 要求和 `summary.overallStatus` 聚合。 -- 七条 journey 必须以固定 ID 呈现:`first-hop-share-entry`、`receiver-confirm-conversion`、`receiver-comment-conversion`、`second-hop-receiver-source`、`ordinary-and-risk-entries`、`timeline-share-channel`、`timeline-risk-gating`。 -- 所有 readiness、diagnostic、dry-run、draft-created、checker-passed 文案都必须显式声明:它们不等于 WeChat DevTools 或真机 UI passed。 -- 如果端口诊断 blocked,运行包仍应输出可执行下一步:手动启用 DevTools Service Port、换端口或换机器后重跑只读诊断,再进入真实手测。 diff --git a/harness/viral-journey-evidence-design-checklist.md b/harness/viral-journey-evidence-design-checklist.md deleted file mode 100644 index dfc7e08..0000000 --- a/harness/viral-journey-evidence-design-checklist.md +++ /dev/null @@ -1,45 +0,0 @@ -# 传播链路证据设计 / QA Checklist - -日期:2026-06-16 - -## 自动场景 - -- [ ] `node --no-warnings scripts/check-viral-journey-evidence.mjs` 输出 `Viral journey evidence checks passed.` -- [ ] DevTools readiness 会运行传播链路证据脚本 -- [ ] Viral candidate 检查会运行传播链路证据脚本或同等关键断言 - -## 首跳接收 - -- [ ] `from=share` 且 active、无 stale/report 的任务展示接收侧说明 -- [ ] 同一状态展示接收侧第一步 action strip -- [ ] 接收侧 action strip 不包含 `open-type="share"` -- [ ] 普通分享面板在接收侧说明存在时不同时出现 - -## 转化后接力 - -- [ ] 从分享进入后点击确认,会生成 `receiverConversionPrompt` -- [ ] 从分享进入后提交评论,会生成 `receiverConversionPrompt` -- [ ] `receiverConversionPrompt` 出现时,`actionRelayPrompt` 和 `commentRelayPrompt` 不同时抢主 CTA -- [ ] 可接力状态的分享路径包含 `from=share&source=receiver` - -## 二跳接收 - -- [ ] `source=receiver` 的接收侧标题能表达“有人接力转给你” -- [ ] `source=receiver` 的说明强调先看确认和评论 -- [ ] 风险态二跳仍然先提示谨慎核对 - -## 风险和普通入口 - -- [ ] 普通入口不显示 `shareReceiverGuide` -- [ ] 普通入口不显示 `shareReceiverActionStrip` -- [ ] 普通入口不会生成 `receiverConversionPrompt` -- [ ] 任意 stale/report 信号不显示接收侧鼓励 action strip -- [ ] `stale` / `resolved` / `expired` / `hidden` 不显示接收者公开接力 CTA - -## 手测记录 - -- [ ] 手测前复制 `harness/viral-journey-manual-results.example.json`,并替换分支、commit、环境和实际 post id -- [ ] 没有实际执行时,保留 `overallStatus: "not_run"` 或记录为明确 blocker -- [ ] DevTools/真机结果必须写明是否观察到系统分享面板、真实二跳路径和窄屏层级 -- [ ] 自动脚本结果只能作为预检证据,不能当成 DevTools 或真机完成证据 - diff --git a/harness/viral-journey-evidence-product-brief.md b/harness/viral-journey-evidence-product-brief.md deleted file mode 100644 index a78337a..0000000 --- a/harness/viral-journey-evidence-product-brief.md +++ /dev/null @@ -1,32 +0,0 @@ -# 传播链路证据产品 Brief - -日期:2026-06-16 - -## 目标 - -为“分享接收者 -> 完成确认或评论 -> 二跳接力 -> 下一位接收者”的真实链路补一层可复跑证据框架。它把关键 helper 和详情页互斥条件组合成自动场景模型,帮助复核当前传播候选是否仍满足低风险、单主 CTA 和接力语境。 - -## 产品假设 - -当前 J/L/M 候选已经把分享接收、行动入口、评论接力、确认接力和接收者转化拆成多个 helper。最大短板不是单个 helper 缺少断言,而是缺少一条从首跳进入到二跳接收的连贯证据。把这条链路固化成脚本,可以降低后续改动破坏真实传播链路的风险。 - -## 自动证据范围 - -- `/pages/detail/detail?id=&from=share` 进入 active、无 stale/report 的任务时,接收侧说明与第一步 action strip 都存在 -- `shareReceiverGuide`、`shareReceiverActionStrip` 和普通 `shareMessage` 面板互斥 -- 接收者完成 confirm/comment 后,`receiverConversionPrompt` 优先于 `actionRelayPrompt` / `commentRelayPrompt` -- 二跳分享路径保留 `from=share&source=receiver` -- 下一位通过 `source=receiver` 进入时,接收侧文案是接力语境 -- 普通入口、任意 stale/report 信号、`stale` / `resolved` / `expired` / `hidden` 不出现接收侧鼓励 action strip 或接收者公开接力 CTA - -## 非目标 - -- 不替代 WeChat DevTools 或真机手测 -- 不验证系统分享面板、真实页面点击、窄屏换行、云端评论保存或分享卡片实际落地 -- 不改变地图、发布、详情、评论或管理主流程 -- 不新增埋点、归因、奖励或传播策略 - -## 证据边界 - -`scripts/check-viral-journey-evidence.mjs` 是自动场景证据,只证明 helper 输出和详情页静态互斥条件保持一致。真实链路仍需要在 WeChat DevTools 或真机中手动打开目标路径,点击确认/评论,触发系统分享,并记录实际 UI 与分享路径。 - diff --git a/harness/viral-journey-manual-evidence-checklist.md b/harness/viral-journey-manual-evidence-checklist.md deleted file mode 100644 index 718fdf3..0000000 --- a/harness/viral-journey-manual-evidence-checklist.md +++ /dev/null @@ -1,47 +0,0 @@ -# 传播链路真实手测证据 Checklist - -日期:2026-06-16 - -## 文档和入口 - -- [ ] `harness/viral-journey-manual-evidence-product-brief.md` 说明真实手测结果文件的价值、边界和状态规则。 -- [ ] `harness/viral-journey-manual-evidence-checklist.md` 明确开发、QA 和收尾验证项。 -- [ ] `scripts/check-viral-journey-manual-evidence.mjs` 默认只扫描 ignored/local 结果文件。 -- [ ] 即使显式传入结果文件,checker 也拒绝未被 git ignore 的路径。 -- [ ] `scripts/prepare-viral-journey-manual-evidence.mjs --dry-run` 不写文件,只打印将生成的 ignored local draft 和后续命令。 - -## Checker 正向规则 - -- [ ] 无结果文件时输出 `No viral journey manual evidence files found; nothing checked.`。 -- [ ] 无结果文件时额外说明这不代表 DevTools 或真机 UI passed。 -- [ ] ignored local 结果文件存在时,校验 `schemaVersion`、`branch`、`commit`、`testedAt`、`tester`、`environment`、`summary` 和 `journeys`。 -- [ ] `branch` 必须等于当前分支。 -- [ ] `commit` 必须等于当前 HEAD 的 full SHA 或 short SHA。 -- [ ] `environment` 必须记录 DevTools/base library、设备、真机/模拟器、网络、CloudBase 和数据准备。 -- [ ] 每个 required journey 必须存在且只能存在一次。 -- [ ] 不允许把 `harness/viral-journey-manual-results.example.json` 当作真实结果文件。 - -## Journey 状态规则 - -- [ ] 状态只允许 `passed`、`failed`、`blocked`。 -- [ ] `passed` 必须有非空 evidence,且 evidence 类型是 screenshot、recording、payload 或 log。 -- [ ] `passed.actual` 不能是 `Not run`、``、`placeholder`、`TODO`、`待填写` 等占位文本。 -- [ ] 关键二跳 journey 的 `passed` 必须包含 share payload,或用 `sharePayloadInspection` 明确说明无法检查。 -- [ ] `blocked` 必须有非空 `blocker` 和 `followUp`。 -- [ ] `failed` 必须有非空 `actual` 和 `followUp`。 -- [ ] `summary.overallStatus` 必须符合聚合规则:任何 failed -> failed;否则任何 blocked -> blocked;否则 required journey 全 passed -> passed。 - -## Readiness 接入 - -- [ ] `scripts/check-devtools-readiness.mjs` 运行 manual evidence checker。 -- [ ] readiness 输出明确说明 manual evidence checker 只是扫描 ignored/local 结果文件。 -- [ ] 没有本地结果文件时 readiness 不阻塞。 -- [ ] readiness 不得把无文件、blocked draft 或自动场景模型说成 DevTools/真机 UI passed。 - -## 手测者填报提醒 - -- [ ] 从 prepare helper 生成 ignored local draft,或手动复制模板后改成真实 schema。 -- [ ] 填入真实分支、commit、DevTools 版本、base library、设备、网络、CloudBase 和测试 post id。 -- [ ] 对 confirm/comment 转化 journey,尽量记录系统分享 payload;如果 DevTools 无法检查 payload,要写清楚原因。 -- [ ] 截图、录屏、payload 或日志只放 ignored/local 路径,不提交真实结果文件。 -- [ ] 修改结果文件后复跑 `node --no-warnings scripts/check-viral-journey-manual-evidence.mjs`。 diff --git a/harness/viral-journey-manual-evidence-product-brief.md b/harness/viral-journey-manual-evidence-product-brief.md deleted file mode 100644 index 1d50808..0000000 --- a/harness/viral-journey-manual-evidence-product-brief.md +++ /dev/null @@ -1,40 +0,0 @@ -# 传播链路真实手测证据 Product Brief - -日期:2026-06-16 - -## 目标 - -在 N 组 `harness/viral-journey-manual-results.example.json` 的 `not_run` 模板之后,补一个只校验真实本地手测结果文件的 gate。默认没有本地结果文件时,gate 应通过并说明“没有检查任何真实 UI 结果”;一旦存在本地结果文件,gate 必须验证它是否能支撑传播链路的真实 DevTools/真机结论。 - -## 产品判断 - -传播链路当前已经有自动场景模型,但自动模型只能证明 helper 输出、详情页互斥条件和分享路径拼接逻辑没有回归。真实链路仍会被 DevTools 渲染、系统分享面板、点击顺序、登录/本地 storage 状态、云端评论路径、窄屏布局和真实二跳入口影响。因此需要把“可选的本地手测结果文件”升级成可复跑校验入口,防止把 example、占位文本或无 evidence 的 `passed` 写进交接。 - -## 结果文件边界 - -- 推荐使用既有 ignored 命名:`harness/manual-test-results.local-viral-journey*.json`。 -- 如果使用 `harness/local-viral-journey-results*.json`,必须先通过本地 git ignore/exclude 让它成为 ignored local 文件;未忽略的真实结果不应进入 readiness 扫描。 -- `harness/viral-journey-manual-results.example.json` 永远只是模板,不是结果文件。 -- 真实结果文件应记录当前分支、当前 commit、测试环境、数据准备和每条 required journey 的状态。 - -## 状态规则 - -- `passed`:必须有非空 evidence,`actual` 不能是模板/占位文本;关键二跳 journey 必须记录 share payload,或明确写出为什么无法检查 payload。 -- `failed`:必须写明实际观察到的 `actual` 和下一步 `followUp`。 -- `blocked`:必须写明具体 `blocker` 和下一步 `followUp`。 -- `overallStatus` 只能由 journey 聚合得出:任一 `failed` 为 `failed`;否则任一 `blocked` 为 `blocked`;否则 required journey 全部 `passed` 才是 `passed`。 -- 没有本地结果文件时不能产生 `passed` 结论,只能说明没有真实结果被检查。 - -## 非目标 - -- 不改变小程序页面、文案、按钮、分享策略或评论/确认逻辑。 -- 不提交真实截图、录屏、payload 或日志。 -- 不把 DevTools readiness、自动场景模型或 dry-run 草稿当成 UI passed。 -- 不要求 CI 或默认检查拥有 DevTools/真机访问。 - -## 成功标准 - -- 默认无本地文件时,manual evidence checker exit 0,并输出没有找到文件且不代表 UI passed。 -- 本地 ignored 结果文件存在时,checker 校验 schema、分支/commit、环境、required journey 唯一性、状态字段和 `overallStatus` 聚合。 -- readiness 会运行这个 checker,但无文件时不阻塞,也不会暗示 DevTools/真机通过。 -- prepare helper 可以生成或预览 ignored local draft;draft 不得声称任何 journey `passed`。 diff --git a/harness/viral-journey-manual-results.example.json b/harness/viral-journey-manual-results.example.json deleted file mode 100644 index 8837af7..0000000 --- a/harness/viral-journey-manual-results.example.json +++ /dev/null @@ -1,226 +0,0 @@ -{ - "schemaVersion": "viral-journey-manual-results.example.v1", - "exampleNotice": "This is a fill-in example only. It is not real WeChat DevTools or device evidence.", - "branch": "codex/iter-viral-journey-evidence", - "commit": "", - "testedAt": "", - "tester": "", - "statusAllowedValues": [ - "not_run", - "blocked", - "failed" - ], - "environment": { - "wechatDevToolsVersion": "", - "baseLibraryVersion": "", - "device": "", - "isRealDevice": false, - "network": "", - "cloudBase": { - "enabled": false, - "environmentId": "", - "postsFunctionDeployed": false, - "notes": "Example values only; replace after checking the actual test environment." - }, - "dataSetup": { - "activeLowRiskPostId": "", - "staleSignalPostId": "", - "reportSignalPostId": "", - "closedPostIds": { - "stale": "", - "resolved": "", - "expired": "", - "hidden": "" - }, - "notes": "Record whether data came from local storage, mock data, or CloudBase." - } - }, - "summary": { - "overallStatus": "not_run", - "recommendation": "Do not treat this example as release evidence. Replace every placeholder after a real manual run.", - "notes": [ - "No manual journey has been executed for this example.", - "Use blocked only when a concrete DevTools, device, data, or environment issue prevents the run." - ] - }, - "journeys": [ - { - "id": "first-hop-share-entry", - "title": "首跳从分享进入低风险 active 任务", - "status": "not_run", - "route": "/pages/detail/detail?id=&from=share", - "steps": [ - "Open the route in WeChat DevTools or on a real device.", - "Wait for the detail page and comments to finish loading.", - "Inspect the share receiver guide, receiver action strip, and ordinary share panel area." - ], - "expected": [ - "shareReceiverGuide is visible.", - "shareReceiverActionStrip is visible.", - "The ordinary share panel is not visible in the same state.", - "The action strip buttons are confirm/comment actions, not direct share buttons." - ], - "actual": "Not run in this example. Replace with observed UI state.", - "evidence": [], - "risks": [ - "Static checks cannot prove native page rendering, loading order, or narrow-screen wrapping." - ], - "followUp": "Record the exact post id, device profile, and whether the guide/action strip/ordinary panel were visible." - }, - { - "id": "receiver-confirm-conversion", - "title": "接收者确认后的二跳提示", - "status": "not_run", - "route": "/pages/detail/detail?id=&from=share", - "steps": [ - "Open the first-hop route.", - "Tap the receiver confirm action.", - "Observe the prompt shown after the trust action is recorded.", - "Trigger the prompt share button if the environment allows inspecting the share payload." - ], - "expected": [ - "receiverConversionPrompt is visible after confirm.", - "2-3 relay channel suggestions are visible between targetRows and the share reason; they are scene suggestions, not share buttons or contact/group pickers.", - "The share reason is visible between targetRows and the share button, and the copy says the sender just confirmed or can help re-check.", - "actionRelayPrompt is not competing as the primary CTA.", - "The share path contains from=share&source=receiver&receiverAction=confirm.", - "If share payload is inspectable, the relay path also contains share_id, parent_share_id, and share_depth=2 or 2_plus." - ], - "actual": "Not run in this example. Replace with observed prompt and share payload.", - "evidence": [], - "risks": [ - "A duplicate prior confirm by the same user can block the confirm action before this journey reaches conversion." - ], - "followUp": "Use a user/storage state that has not already confirmed the target task." - }, - { - "id": "receiver-comment-conversion", - "title": "接收者评论后的二跳提示", - "status": "not_run", - "route": "/pages/detail/detail?id=&from=share", - "steps": [ - "Open the first-hop route.", - "Open the comment dialog and submit a valid comment.", - "Observe the prompt shown after the comment is recorded.", - "Inspect the share payload if the environment allows it." - ], - "expected": [ - "receiverConversionPrompt is visible after comment submit.", - "2-3 relay channel suggestions are visible between targetRows and the share reason; they are scene suggestions, not share buttons or contact/group pickers.", - "The share reason is visible between targetRows and the share button, and the copy says the sender just added a clue or the next receiver should read latest comments.", - "commentRelayPrompt is not competing as the primary CTA.", - "The share path contains from=share&source=receiver&receiverAction=comment.", - "If share payload is inspectable, the relay path also contains share_id, parent_share_id, and share_depth=2 or 2_plus." - ], - "actual": "Not run in this example. Replace with observed prompt and share payload.", - "evidence": [], - "risks": [ - "Guest state, CloudBase comment failure, or closed task state can block comment submission." - ], - "followUp": "Record login state, storage/cloud path, submitted comment text, and resulting prompt copy." - }, - { - "id": "second-hop-receiver-source", - "title": "二跳接收者看到接力语境", - "status": "not_run", - "route": "/pages/detail/detail?id=&from=share&source=receiver&receiverAction=", - "steps": [ - "Open the second-hop route directly or from the system share card.", - "Wait for the receiver guide to render.", - "Inspect the title, summary, rows, and note." - ], - "expected": [ - "The receiver guide title communicates that someone relayed the task.", - "If receiverAction=confirm is present, the copy distinguishes that the previous receiver just confirmed.", - "If receiverAction=comment is present, the copy distinguishes that the previous receiver just added a clue.", - "The page does not show an ordinary share panel at the same time." - ], - "actual": "Not run in this example. Replace with observed copy and panel state.", - "evidence": [], - "risks": [ - "Direct route opening can differ from a real system share entry if query parameters are lost." - ], - "followUp": "Compare direct-route behavior with a real system share-card entry when available." - }, - { - "id": "ordinary-and-risk-entries", - "title": "普通入口和风险态不鼓励接收侧扩散", - "status": "not_run", - "route": "multiple", - "steps": [ - "Open the same active low-risk post without from=share.", - "Open share routes for posts with any stale signal and any report signal.", - "Open share routes for stale, resolved, expired, and hidden posts when such fixtures exist.", - "Inspect receiver action strip, receiver conversion prompt, and any public relay CTA." - ], - "expected": [ - "The ordinary entry does not show receiver guide or receiver action strip.", - "Any stale/report signal hides the receiver action strip.", - "Any stale/report signal or closed state does not show relay channel suggestions.", - "stale, resolved, expired, and hidden states do not expose a receiver public relay CTA.", - "Risk-state copy asks users to inspect comments, confirmation, or latest status before taking action." - ], - "actual": "Not run in this example. Replace with observed state for each fixture.", - "evidence": [], - "risks": [ - "Hidden posts may be unavailable through normal list paths and need a controlled fixture or admin setup.", - "Expired status can depend on current time and fixture timestamps." - ], - "followUp": "Record every post id and status fixture used, including staleCount/reportCount values." - }, - { - "id": "timeline-share-channel", - "title": "低风险 active 详情页朋友圈系统渠道", - "status": "not_run", - "route": "/pages/detail/detail?id=", - "steps": [ - "Open a low-risk active detail page after the latest post state has loaded.", - "Open the real WeChat system menu from the detail page.", - "Inspect whether both friend share and timeline share are present.", - "Trigger or hook onShareTimeline if the environment allows inspecting the payload.", - "Open the timeline card or an equivalent single-page-mode entry and inspect the first screen." - ], - "expected": [ - "The real system menu shows both send-to-friend and share-to-timeline entries for the low-risk active task.", - "onShareTimeline evidence records title, query, and imageUrl or a concrete reason the field could not be inspected.", - "If query is inspectable, it contains id, from=share, source=timeline, and shareChannel=timeline.", - "The timeline or equivalent single-page-mode first screen keeps the task title, status, place, and source=timeline receiver context readable.", - "The source=timeline receiver context explains the user saw a nearby task from Moments and should inspect status/comments before confirming or adding a clue.", - "The timeline channel does not replace the existing receiver conversion journeys in this same result package." - ], - "actual": "Not run in this example. Replace with real menu, payload, and single-page first-screen observations.", - "evidence": [], - "risks": [ - "Some DevTools or device environments may not expose the raw timeline payload and require a concrete inspection limitation note.", - "Single-page mode can differ from direct detail routing, so record which entry method was used." - ], - "followUp": "Record system menu evidence, onShareTimeline title/query/imageUrl or the exact inspection blocker, and first-screen readability evidence." - }, - { - "id": "timeline-risk-gating", - "title": "风险和闭合任务不开放鼓励性朋友圈", - "status": "not_run", - "route": "multiple", - "steps": [ - "Open detail fixtures with weak stale and report signals.", - "Open stale, resolved, expired, hidden, and unknown-task fixtures when available.", - "Inspect the real system menu for shareTimeline availability.", - "If a timeline payload can still be inspected through a hook, record the actual title/query/image fields." - ], - "expected": [ - "Weak stale/report, stale, resolved, expired, hidden, and unknown tasks do not expose an encouraging timeline entry.", - "Passed evidence records that shareTimeline is absent; blocked evidence records the concrete reason the menu/payload could not be triggered.", - "Any inspectable title or actual copy uses cautious semantics such as stale, reported, closed, expired, hidden, or unavailable.", - "No risk or closed state shows an encouraging timeline CTA or task image meant for broad spread.", - "The existing ordinary-and-risk-entries receiver journey still verifies receiver guide/action strip risk gating." - ], - "actual": "Not run in this example. Replace with risk fixture menu and cautious-copy observations.", - "evidence": [], - "risks": [ - "Hidden and unknown tasks may need controlled fixtures or admin setup before the menu state can be observed.", - "A missing menu inspection must be recorded as blocked rather than passed when no equivalent evidence exists." - ], - "followUp": "Record every risk fixture id, status, stale/report counts, menu absence or inspection blocker, and any cautious title/payload observed." - } - ] -} diff --git a/harness/viral-loop-candidate-design-checklist.md b/harness/viral-loop-candidate-design-checklist.md deleted file mode 100644 index 739f8eb..0000000 --- a/harness/viral-loop-candidate-design-checklist.md +++ /dev/null @@ -1,29 +0,0 @@ -# 详情页传播闭环候选设计 / QA Checklist - -## 信息层级 - -- [ ] 发布成功页只显示发布后扩散计划,不显示普通分享面板 -- [ ] 分享接收页只显示接收侧引导,不显示普通分享面板 -- [ ] 评论成功后只显示评论接力提示,不再同时显示普通分享面板 -- [ ] 普通详情入口仍保留通用分享面板 -- [ ] 信任判断、评论列表、关闭任务按钮不被传播模块遮挡 - -## 风险状态 - -- [ ] 高举报、过时、已隐藏、已关闭、已过期任务不鼓励公开扩散 -- [ ] 风险态评论接力按钮不可触发公开分享 -- [ ] 接收侧提示仍提醒先看评论和现场状态 - -## 验证 - -- [ ] `node --check pages/detail/detail.js` -- [ ] `node --check scripts/check-viral-candidate.mjs` -- [ ] `node --check scripts/check-comment-relay.mjs` -- [ ] `node --no-warnings scripts/check-viral-candidate.mjs` -- [ ] `node --no-warnings scripts/check-comment-relay.mjs` -- [ ] `node --no-warnings scripts/check-share-receiver.mjs` -- [ ] `node --no-warnings scripts/check-share-message.mjs` -- [ ] `node --no-warnings scripts/check-publish-spread.mjs` -- [ ] `npm run check` -- [ ] `bash harness/init.sh` -- [ ] WeChat DevTools 中手动确认发布、分享接收、评论成功、普通详情四种入口互斥展示 diff --git a/harness/viral-loop-candidate-product-brief.md b/harness/viral-loop-candidate-product-brief.md deleted file mode 100644 index 35f23f6..0000000 --- a/harness/viral-loop-candidate-product-brief.md +++ /dev/null @@ -1,23 +0,0 @@ -# 详情页传播闭环候选产品 Brief - -## 目标 - -把发布后扩散、分享接收侧引导、评论后接力三段能力组合成一个更克制的详情页传播闭环。 - -## 产品假设 - -用户在不同入口的主要任务不同:发布者想扩散,接收者想判断为什么收到,评论者刚补完线索时才适合接力。如果同一屏同时露出多个传播 CTA,用户会更难判断下一步。因此候选版本应保证同一时刻只出现一个主要传播行动。 - -## 范围 - -- `from=publish` 显示发布成功扩散计划 -- `from=share` 显示接收侧引导 -- 评论提交成功后显示评论接力提示 -- 普通详情入口才显示通用分享面板 -- 不新增存储字段、不引入新依赖、不改变评论和信任动作逻辑 - -## 风险 - -- 隐藏通用分享面板可能降低部分接收侧的显性转发入口 -- 评论后接力仍需要真机确认 `open-type="share"` 行为 -- 自动检查只能证明互斥结构存在,不能证明用户会更愿意传播 diff --git a/harness/viral-manual-artifact-manifest-checklist.md b/harness/viral-manual-artifact-manifest-checklist.md deleted file mode 100644 index e03ce1b..0000000 --- a/harness/viral-manual-artifact-manifest-checklist.md +++ /dev/null @@ -1,173 +0,0 @@ -# AF 传播链人工证据附件 Manifest QA Checklist - -日期:2026-06-17 - -范围:本清单面向测试者和评测者,用于规范七条真实 viral journey 的人工证据附件如何收集、标注、脱敏和复核。它不定义用户界面文案,不执行 WeChat DevTools 或真机操作,不声明任何 journey passed。 - -## 0. 总原则 - -- [ ] Artifact manifest 只记录附件索引和脱敏观察,不提交截图、录屏、payload 原文、CloudBase 原始记录或本机文件路径。 -- [ ] 每条 journey 的状态只能由真实人工执行结果填写为 `passed`、`failed` 或 `blocked`。 -- [ ] `passed` 只能在所有必收和条件必收 artifact slot 已收集、已脱敏、已复核通过后填写。 -- [ ] 缺少必收附件、payload 无法检查、CloudBase 读回不可用或附件未完成脱敏时,不得填写 `passed`。 -- [ ] `failed` 用于已经完成观察且实际行为不符合预期;必须写清 `actual` 和 `followUp`。 -- [ ] `blocked` 用于环境、工具、权限、设备或附件能力阻塞验证;必须写清 `blocker` 和 `followUp`。 -- [ ] 可选附件缺失不阻塞 journey,但 manifest 必须标注 `optional_not_collected`,不能伪装成已收集。 -- [ ] 每份 manifest 都必须包含一句边界说明:`notClaimed: no DevTools UI journey passed; no real-device journey passed; no viral journey passed`;即使 manifest 来自真实手测结果,也只能说明附件清单已复核,不能替代 journey 本身的 evidence gate。 - -## 1. Artifact Slot 定义 - -每个 artifact slot 都只能记录脱敏后的索引和语义摘要。 - -| slot | 用途 | 允许记录 | 禁止记录 | -| --- | --- | --- | --- | -| `screenshot` | 静态页面或系统菜单状态 | 附件编号、journeyId、场景、可见/不可见组件摘要、设备类别 | 图片原件、本机路径、URL、二维码、真实用户资料 | -| `recording` | 连续交互过程 | 附件编号、起止动作、关键状态变化、是否覆盖完整操作 | 视频原件、文件路径、账号画面细节、联系人信息 | -| `payload-sample` | 分享 path/query/title/imageUrl/attribution 的结构化检查 | 字段是否存在、字段语义状态、敏感字段是否已脱敏 | 原始 path/query 值、完整 title、URL、cloud fileID、token、openid | -| `cloud-readback` | 云端或共享数据路径的结果读回 | 集合语义、记录是否存在、状态/计数/动作语义是否符合预期 | 完整 CloudBase 文档、`cloud://`、环境 id、真实 openid/unionid | -| `device-observation` | 设备和运行环境观察 | DevTools/真机类别、基础库版本、网络类别、入口方式、是否 CloudBase 启用 | 设备唯一标识、本机用户目录、日志路径、真实账号 | -| `risk-state-note` | 风险态和闭合态判定 | active/stale/resolved/expired/hidden 等语义、stale/report 风险桶、闭合原因摘要 | 真实地址、精确经纬度、完整 post 内容、真实姓名/手机号/邮箱 | - -Manifest 行建议字段: - -```text -journeyId: <七条 journey id 之一> -slot: screenshot | recording | payload-sample | cloud-readback | device-observation | risk-state-note -requirement: required | conditional-required | optional -slotStatus: collected | not_collected | blocked | not_applicable | rejected_for_redaction -artifactRef: <不含路径和 URL 的本地附件编号> -sanitizedObservation: <只写语义摘要> -redactionCheck: pending | passed | rework_required -reviewStatus: pending | accepted | rejected -blocker: -followUp: -``` - -## 2. 全局脱敏规则 - -- [ ] 不得记录 `cloud://`、CloudBase fileID、真实云环境 id、私有 AppID。 -- [ ] 不得记录本机路径、DevTools user-data 路径、截图/录屏/payload/raw log 文件路径。 -- [ ] 不得记录任何 URL、预览链接、二维码、图片链接或网络请求完整地址。 -- [ ] 不得记录 token、cookie、session、authorization、bearer、access token、refresh token、password、secret。 -- [ ] 不得记录 openid、unionid、微信号、真实昵称、头像 URL、真实姓名、真实手机号、真实邮箱。 -- [ ] 不得记录精确经纬度、详细住址、门牌号、真实评论正文全文、完整 post title/body。 -- [ ] `payload-sample` 只能记录字段是否存在和语义状态,例如“`source` 存在且语义为 receiver”,不能记录原始字段值。 -- [ ] `id`、`share_id`、`parent_share_id`、`imageUrl`、`title`、`path`、`query` 等字段只能写存在性、缺失性、语义类别或已脱敏占位。 -- [ ] 错误信息和复核意见只报告敏感类别和 slot,不回显敏感原文。 -- [ ] 如果附件中出现敏感内容,slot 必须标记为 `rejected_for_redaction`,待替换或二次脱敏后才能复核通过。 - -## 3. 七条 Journey Artifact Slots - -### 3.1 `first-hop-share-entry`:首跳从分享进入低风险 active 任务 - -| slot | 要求 | 收集与标注要求 | -| --- | --- | --- | -| `screenshot` | 必收 | 记录从分享入口进入后的首屏状态,必须覆盖 receiver guide、receiver action strip、任务主体是否可读。 | -| `device-observation` | 必收 | 记录入口来自真实分享卡片或可说明的 DevTools/真机入口,记录设备类别、基础库、网络和 CloudBase 是否启用。 | -| `risk-state-note` | 必收 | 记录 fixture 是低风险 active 任务,且无 stale/report/closed 风险信号;只写语义,不写原始 post 内容。 | -| `payload-sample` | 条件必收 | 当分享 path/query 可检查时,记录 `id`、`from=share` 等字段存在性和语义;无法检查时必须写 blocker/follow-up,不能直接 passed。 | -| `recording` | 可选 | 当需要证明入口到首屏连续性时收集短录屏;缺失不阻塞,但必须标注 optional。 | -| `cloud-readback` | 可选 | CloudBase 启用时可读回 post 仍为 active 的语义状态;不得记录原始云记录。 | - -### 3.2 `receiver-confirm-conversion`:接收者确认后的二跳提示 - -| slot | 要求 | 收集与标注要求 | -| --- | --- | --- | -| `recording` | 必收 | 记录从首跳分享详情页点击确认、确认成功、二跳提示出现的完整过程。 | -| `screenshot` | 必收 | 至少记录确认前状态和二跳提示出现后的状态;若用录屏覆盖,也要在 manifest 写明关键帧位置的语义摘要。 | -| `payload-sample` | 必收 | 记录二跳分享 payload 字段存在性和语义:`from=share`、`source=receiver`、`receiverAction=confirm`、attribution 字段是否存在;不记录原始值。 | -| `device-observation` | 必收 | 记录确认动作来自接收者路径,且不是普通详情入口;记录真机/DevTools、网络和 CloudBase 状态。 | -| `risk-state-note` | 必收 | 记录任务在确认时仍可接受信任动作,且不是 stale/report/closed 阻断态。 | -| `cloud-readback` | 条件必收 | CloudBase 启用或测试声明共享数据路径时,必须读回确认动作语义或计数变化;本地 fallback 时写明非云端证据。 | - -### 3.3 `receiver-comment-conversion`:接收者评论后的二跳提示 - -| slot | 要求 | 收集与标注要求 | -| --- | --- | --- | -| `recording` | 必收 | 记录从首跳分享详情页打开评论、提交评论、评论成功、二跳提示出现的完整过程。 | -| `screenshot` | 必收 | 记录评论成功状态和二跳提示;不得暴露评论正文全文、真实昵称或头像。 | -| `payload-sample` | 必收 | 记录二跳分享 payload 字段存在性和语义:`from=share`、`source=receiver`、`receiverAction=comment`、attribution 字段是否存在;不记录原始值。 | -| `cloud-readback` | 条件必收 | CloudBase 启用或声明共享评论路径时,必须读回“评论记录存在/计数变化”的语义状态;不得记录原始评论内容。 | -| `device-observation` | 必收 | 记录评论提交环境、入口来源和是否真机/DevTools;不记录账号标识。 | -| `risk-state-note` | 必收 | 记录任务允许评论且不是 closed 状态;若评论被 closed gating 拦截,应改填 failed 或 blocked。 | - -### 3.4 `second-hop-receiver-source`:二跳接收者看到接力语境 - -| slot | 要求 | 收集与标注要求 | -| --- | --- | --- | -| `screenshot` | 必收 | 分别记录 confirm 来源和 comment 来源的二跳首屏语境,证明文案能区分“刚确认”和“刚补线索”。 | -| `payload-sample` | 必收 | confirm/comment 两种二跳都要记录字段存在性和语义:`source=receiver`、`receiverAction=confirm/comment`;不得记录原始 query。 | -| `device-observation` | 必收 | 标注入口是真实二跳分享卡片还是 direct route 辅助;direct route 只能作为辅助 evidence,不能伪装成系统卡片 evidence。 | -| `risk-state-note` | 必收 | 记录二跳任务仍处于可接收扩散的低风险状态;如状态变化,应记录变化原因和影响。 | -| `recording` | 可选 | 当需要证明从二跳卡片到首屏的连续性时收集。 | -| `cloud-readback` | 可选 | 可记录 attribution 链路存在的语义读回;不得记录 share id 原值或云端原始文档。 | - -### 3.5 `ordinary-and-risk-entries`:普通入口和风险态不鼓励接收侧扩散 - -| slot | 要求 | 收集与标注要求 | -| --- | --- | --- | -| `screenshot` | 必收 | 普通入口、weak stale/report、`stale`、`resolved`、`expired`、`hidden` fixtures 都要有 UI 证据或明确 blocker。 | -| `risk-state-note` | 必收 | 每个 fixture 都要记录状态语义、风险桶、闭合原因,以及是否应隐藏鼓励性 public relay CTA。 | -| `device-observation` | 必收 | 记录每个 fixture 的入口类型、设备类别和 CloudBase/本地 fixture 来源语义。 | -| `cloud-readback` | 条件必收 | 当 fixture 状态来自 CloudBase 或声明为共享状态时,必须读回状态语义;本地 fixture 要写明不是云端证据。 | -| `recording` | 可选 | 如需要证明切换多个 fixture 的连续性,可收集一段录屏。 | -| `payload-sample` | 可选 | 一般不要求;若系统分享菜单仍可打开或有 payload 可检查,必须记录谨慎语义或 blocker。 | - -### 3.6 `timeline-share-channel`:低风险 active 详情页朋友圈系统渠道 - -| slot | 要求 | 收集与标注要求 | -| --- | --- | --- | -| `recording` | 必收 | 记录低风险 active 详情页打开系统菜单、看到发送给朋友和朋友圈入口、进入朋友圈或单页模式首屏的过程。 | -| `screenshot` | 必收 | 记录系统菜单和朋友圈/单页模式首屏;截图只能以附件编号引用。 | -| `payload-sample` | 必收 | 记录 timeline payload 字段存在性和语义:`id`、`from=share`、`source=timeline`、`shareChannel=timeline`、`title`、`imageUrl`;不记录原始值或 URL。 | -| `device-observation` | 必收 | 记录真机/DevTools、基础库、网络和系统菜单能力;如果 DevTools 不暴露朋友圈能力,必须写 blocker/follow-up。 | -| `risk-state-note` | 必收 | 记录任务是低风险 active,且朋友圈扩散没有被 stale/report/closed gating 阻断。 | -| `cloud-readback` | 可选 | 可读回 post 状态或 share attribution 语义;不得记录云端原始记录。 | - -### 3.7 `timeline-risk-gating`:风险和闭合任务不开放鼓励性朋友圈 - -| slot | 要求 | 收集与标注要求 | -| --- | --- | --- | -| `screenshot` | 必收 | weak stale/report、`stale`、`resolved`、`expired`、`hidden`、unknown 或刷新失败 fixtures 都要记录菜单缺失、禁用或谨慎文案证据。 | -| `risk-state-note` | 必收 | 每个 fixture 都要记录风险/闭合语义和预期 gating;不得记录原始内容、地址或坐标。 | -| `device-observation` | 必收 | 记录系统菜单能力、设备类别、基础库和入口来源;无法打开菜单时必须写 blocker/follow-up。 | -| `payload-sample` | 条件必收 | 如果菜单或 payload 可检查,必须记录 title/query/copy 是否为谨慎语义;无法检查 payload 且该字段影响结论时只能 blocked,不能 passed。 | -| `cloud-readback` | 条件必收 | 当风险或闭合状态来自 CloudBase 时,必须读回状态语义;如果云端不可用,记录 blocker 或降级范围。 | -| `recording` | 可选 | 如需证明多个风险 fixtures 的菜单状态切换,可收集录屏。 | - -## 4. Passed / Failed / Blocked 填写规则 - -- [ ] `passed` 必须满足:所有必收 slot 为 `collected`,所有触发的条件必收 slot 为 `collected` 或有被规则允许的 `not_applicable`,全部 `redactionCheck=passed`,全部 `reviewStatus=accepted`。 -- [ ] `passed.actual` 必须描述真实观察结果,不能写 `Not run`、`TODO`、`待补充`、`见附件` 或只写“附件齐了”。 -- [ ] `passed` 不得建立在 blocked draft、readiness、静态 checker、准备脚本、summary 生成或无附件结果之上。 -- [ ] `failed` 必须写实际偏差,例如“风险态仍出现鼓励性朋友圈入口”或“receiverAction 语义缺失”,并写清复测或修复 follow-up。 -- [ ] `blocked` 必须写具体 blocker,例如 Service Port 不可用、系统菜单不可打开、payload 不可 inspect、CloudBase 无权限、真机缺失或附件脱敏失败。 -- [ ] `blocked.followUp` 必须是下一步可执行动作,例如“使用真机复测朋友圈入口”“重新导出脱敏录屏”“补 CloudBase 只读读回”。 -- [ ] 缺少必收附件时只能填写 `blocked`,不能填写 `passed` 或把缺失项写成“无需验证”。 -- [ ] 附件含敏感信息时,相关 slot 先标记 `rejected_for_redaction`;替换为合格附件前,journey 不能 passed。 -- [ ] 如果 UI 已可观察且行为不符合预期,应填 `failed`,不要用 `blocked` 掩盖产品缺陷。 - -## 5. 复核流程 - -- [ ] 测试者先填写 manifest,不提交 raw artifact,只提交脱敏后的 slot 索引和语义摘要。 -- [ ] 测试者自查每个 slot 是否包含禁止信息,尤其是 payload、cloud-readback、截图说明和 blocker/follow-up。 -- [ ] 评测者逐条核对七个 journey 的必收、条件必收、可选 slot 状态,确认缺附件没有被写成 passed。 -- [ ] 评测者抽查附件编号是否能在 ignored/local 证据包中定位,但不得把定位路径写回可提交 manifest。 -- [ ] 评测者复核 `payload-sample` 是否只记录字段存在性和语义状态,不含原始 query/path/title/imageUrl。 -- [ ] 评测者复核 `cloud-readback` 是否只记录语义状态,不含 `cloud://`、云环境 id、完整文档或用户标识。 -- [ ] 评测者复核 `risk-state-note` 是否覆盖每个风险/闭合 fixture,且没有真实地址、精确坐标或个人信息。 -- [ ] 任一必收 slot `reviewStatus=rejected` 时,journey 总状态必须是 `blocked` 或 `failed`,直到重新收集并复核通过。 - -## 6. 收尾检查 - -- [ ] 可提交改动只包含本 checklist 或后续明确允许的 manifest 模板,不包含 local evidence、截图、录屏、payload、raw log 或 CloudBase dump。 -- [ ] manifest 中每条 journey 都能看出哪些 slot 必收、哪些条件必收、哪些可选。 -- [ ] manifest 中每个 `blocked` 都有 blocker 和 follow-up。 -- [ ] manifest 中没有把 readiness、blocked draft、summary integrity、schema checker 或附件缺失写成真实 journey passed。 -- [ ] 修改文档后至少运行: - - ```bash - git diff --check - ``` - -- [ ] 如本轮还修改了 JSON、脚本或 harness 入口,追加运行对应 `node --check`、`node scripts/check-json.mjs`、`node harness/check-harness.mjs` 或 `bash harness/init.sh`。 diff --git a/harness/viral-manual-artifact-manifest-product-brief.md b/harness/viral-manual-artifact-manifest-product-brief.md deleted file mode 100644 index 49ccc65..0000000 --- a/harness/viral-manual-artifact-manifest-product-brief.md +++ /dev/null @@ -1,147 +0,0 @@ -# Viral Manual Artifact Manifest 产品 Brief - -- 日期:2026-06-17 -- 分支:`codex/iter-viral-manual-artifact-manifest` -- 角色:AF 组产品 agent -- 基线:AE 的 viral manual JSON/Markdown summary integrity 之后 -- 对应 feature:`map-feed-001` - -## 背景 - -Street Tasks 的用户侧自发裂变目标不是“强推分享按钮”,而是让发布者和首跳接收者在真实任务场景里自然判断“这条街区任务值得转给下一位能帮忙的人”。理想链路是:低风险 active 任务可以被首跳分享;接收者先确认或评论;完成动作后再出现二跳接力语境;朋友圈渠道只在低风险场景可被谨慎使用;风险态、闭合态和未知任务不能被鼓励扩散。 - -AD 已把七条 viral manual journey 拆成可执行 evidence packet。后续手测结果会写入 ignored local JSON,并生成脱敏 Markdown summary。AE 已补上 JSON/Markdown summary integrity:评审可以知道 summary 是否忠实复述了同一份 local JSON 的 branch、commit、overallStatus、七条 journey 状态、actual、evidenceCount、blocker 和 followUp。 - -AE 之后还缺一层:真实 DevTools 或真机手测通常会产生截图、录屏、payload 摘要、系统菜单观察、CloudBase 或本地 readback 等附件。JSON/summary 可以说“有几份 evidence”以及“状态是什么”,但它们不适合承载附件清单细节;如果评审另行收到一批附件,仍可能不知道每个附件对应哪条 journey、是否脱敏、是否有 payload/readback 缺口、是否和 local JSON/summary 对齐,或者是否被误当成 passed evidence。 - -Artifact manifest 是下一步,因为它只解决“附件能不能被安全引用”的问题:给每个真实附件建立脱敏、可复核、可对齐的清单记录,让评审知道附件类型、覆盖 journey、来源环境、隐私处理、与 JSON/summary 的关系和剩余缺口。manifest 存在或 manifest 检查通过,不代表真实 DevTools UI、真机、朋友圈、payload 或 CloudBase 行为已经通过。 - -## 用户与评审价值 - -用户价值:执行者可以把真实手测附件按七条 journey 归档,而不是把截图、录屏和 payload 摘要散落在聊天、临时目录或个人机器状态里。后续补测时,也能看出哪条 journey 缺截图、缺录屏、缺 payload 摘要或缺 readback。 - -评审价值:评审可以安全引用“某条 journey 有哪些脱敏附件可复核”,并能区分附件清单完整性、JSON/summary 同源完整性和真实产品通过三件事。manifest 降低误引附件、泄露个人信息、引用旧附件和把 blocked/ready 误读成 passed 的风险。 - -## 范围内 - -- 定义 viral manual artifact manifest 的产品边界、字段口径和状态口径。 -- 固定覆盖七条 viral manual journey,不能删除、合并、改名或只记录整体附件。 -- 为每条 journey 说明需要的附件类型,但不写真实路径、真实 URL、账号、昵称、头像、手机号、精确地址、经纬度、token、cookie、CloudBase 私密值或 raw log。 -- 要求 manifest 与 ignored local JSON/Markdown summary 可对齐:journey id、status、evidenceCount 或附件计数、blocker、followUp 不能互相矛盾。 -- 要求每个附件记录脱敏状态、可引用状态、缺口说明和风险说明。 -- 明确 manifest 通过只表示附件清单字段完整、脱敏、可对齐,不表示 UI 或真机 passed。 - -## 非目标 - -- 不执行 WeChat DevTools、真机、系统分享、朋友圈、payload inspect、CloudBase readback 或本地 storage readback。 -- 不新增、不搬运、不提交真实截图、录屏、payload、readback、日志或云端数据。 -- 不把附件清单写成真实 evidence 内容本身;manifest 只记录可引用元数据和脱敏摘要。 -- 不生成或修改 ignored local JSON/Markdown summary。 -- 不修改小程序 UI、分享策略、归因逻辑、评论/确认流程、风控策略或页面文案。 -- 不把 readiness、summary integrity、manifest integrity、附件数量非零或评审可引用升级为 viral journey passed。 - -## Manifest 字段口径 - -manifest 建议只保存最小可引用元数据: - -- `run`: 分支、提交、测试时间、执行环境类别、结果 JSON/summary 对齐说明。 -- `journeys`: 七条固定 journey,每条记录 `id`、`title`、`status`、`artifactCount`、`missingArtifactTypes`、`blocker`、`followUp`。 -- `artifacts`: 每个附件的脱敏别名、附件类型、对应 journey、采集环境类别、引用用途、脱敏说明、是否可被评审引用、是否需要人工二次复核。 -- `alignment`: 与 ignored local JSON/summary 的对应关系,只记录“可对齐/不可对齐/待补齐”和原因,不回显真实本机路径或附件地址。 -- `privacy`: 敏感信息扫描结论、人工脱敏说明、不能公开引用的原因。 -- `notClaimed`: 固定声明 manifest 不是 DevTools UI passed、real-device passed、viral journey passed 或 CloudBase passed evidence。 - -artifact slot 使用固定枚举,而不是自由文本路径: - -- `screenshot`: 脱敏 UI 截图。 -- `recording`: 脱敏操作录屏或短视频。 -- `payload-sample`: 只记录分享 payload 字段名存在性和语义状态,不记录原始值。 -- `cloud-readback`: 只记录 CloudBase 或本地共享数据回读字段名和语义状态,不记录原始记录。 -- `device-observation`: 记录 DevTools/真机类别、基础库、网络、入口方式等非身份环境信息。 -- `risk-state-note`: 记录 active/stale/resolved/expired/hidden、stale/report 风险桶或关闭原因等语义。 - -系统菜单、route、文字观察和限制说明应落在上述 slot 的 `description`、`reviewerNote`、`blocker` 或 `followUp` 中,不能新增自由命名 slot。 - -## 七条 Journey 的附件需求 - -| Journey | 需要的附件类型 | 附件目的 | -| --- | --- | --- | -| `first-hop-share-entry` | `screenshot`;`device-observation`;`risk-state-note`;条件 `payload-sample`;可选 `recording`、`cloud-readback` | 证明首跳分享入口下接收者 guide 和行动入口真实可见,并记录普通分享面板是否被隐藏或不竞争。 | -| `receiver-confirm-conversion` | `recording`;`screenshot`;`payload-sample`;`device-observation`;`risk-state-note`;条件 `cloud-readback` | 证明接收者确认成功后出现二跳接力提示,且确认来源能在分享 payload 或限制说明中被复核。 | -| `receiver-comment-conversion` | `recording`;`screenshot`;`payload-sample`;`device-observation`;`risk-state-note`;条件 `cloud-readback` | 证明接收者评论提交成功后出现二跳接力提示,且评论来源、评论存储路径和 payload 检查缺口有记录。 | -| `second-hop-receiver-source` | `screenshot`;`payload-sample`;`device-observation`;`risk-state-note`;可选 `recording`、`cloud-readback` | 证明二跳接收者能看到“有人接力”的语境,并区分真实系统分享进入与直接 route 辅助进入。 | -| `ordinary-and-risk-entries` | `screenshot`;`risk-state-note`;`device-observation`;条件 `cloud-readback`;可选 `recording`、`payload-sample` | 证明普通入口不展示接收侧扩散,风险或闭合状态不暴露 public relay CTA,并记录各 fixture 的状态依据。 | -| `timeline-share-channel` | `recording`;`screenshot`;`payload-sample`;`device-observation`;`risk-state-note`;可选 `cloud-readback` | 证明低风险 active 详情页真实系统菜单包含朋友圈渠道,或记录无法 inspect 菜单/payload 的系统限制。 | -| `timeline-risk-gating` | `screenshot`;`risk-state-note`;`device-observation`;条件 `payload-sample`、`cloud-readback`;可选 `recording` | 证明风险态、闭合态或未知任务没有鼓励性朋友圈扩散,并记录菜单缺失、谨慎文案或 payload 限制。 | - -## 状态边界 - -### `blocked` - -manifest 层面的 `blocked` 表示附件清单无法达到可引用最低条件,常见原因包括: - -- 缺少对应 ignored local JSON/summary,无法对齐 journey 状态。 -- 某条 journey 的必需附件类型缺失,且没有具体 `limitation-note`。 -- 附件脱敏状态未知,可能包含个人信息、真实路径、真实 URL、raw payload、raw log 或账号信息。 -- manifest 记录的 journey status、artifactCount、blocker 或 followUp 与 JSON/summary 冲突。 -- DevTools、真机、系统菜单、朋友圈、payload inspect、CloudBase/readback 或数据 fixture 仍阻塞真实采集。 - -`blocked` 不能被写成产品失败或产品通过;它只说明当前附件清单不足以安全引用。 - -### `ready` - -manifest 层面的 `ready` 表示可以进入评审引用准备,但还不能判定真实产品通过: - -- 七条 journey 都有清单记录。 -- 每条 journey 的必需附件类型已记录,或缺失项有明确限制说明。 -- 每个附件都有脱敏别名、类型、对应 journey、引用用途、隐私处理和可引用状态。 -- manifest 与 ignored local JSON/summary 的 branch、commit、journey id、status、evidenceCount 或附件数量关系可解释。 -- 输出不包含真实路径、真实 URL、个人信息、raw payload、raw log 或敏感凭证。 - -`ready` 只说明附件清单可供人工评审继续查看,不说明 DevTools UI、真机或 viral journey passed。 - -### `passed` - -manifest 检查的 `passed` 只能表示 manifest 自身通过,例如: - -- 字段完整。 -- 附件类型覆盖符合七条 journey 的最低要求。 -- 所有附件引用信息已脱敏。 -- 与 ignored local JSON/Markdown summary 可对齐。 -- 没有把 blocked、ready、summary integrity 或 manifest integrity 写成 UI/真机通过。 - -manifest `passed` 不代表: - -- 七条 journey 真实 passed。 -- WeChat DevTools UI passed。 -- 真机 passed。 -- 系统分享面板或朋友圈 passed。 -- payload inspect passed。 -- CloudBase 或本地 readback passed。 - -真实 journey `passed` 仍必须来自已执行的 DevTools/真机观察、脱敏 evidence、payload 或限制说明、readback 或限制说明,以及人工复核。 - -## 误读防线 - -manifest 输出和评审报告允许写: - -- “artifact manifest passed,说明附件清单字段完整、脱敏并可与 ignored local JSON/summary 对齐。” -- “manifest 中列出七条 journey 的附件类型和缺口,供评审继续人工查看。” -- “manifest 不是 UI passed evidence,也不是真机 passed evidence。” - -manifest 输出和评审报告禁止写: - -- “manifest passed,所以 viral journey passed。” -- “附件清单存在,所以七条 journey 已真实通过。” -- “summary integrity passed 加 manifest passed,所以 DevTools/真机通过。” -- “有截图别名,所以无需人工查看截图内容。” -- “payload 附件缺失但 manifest 完整,所以 payload 通过。” - -## 验收标准 - -- brief 用中文说明用户侧自发裂变目标、AE summary integrity 后的缺口,以及 artifact manifest 为什么是下一步。 -- brief 固定列出七条 journey,并说明每条需要的附件类型。 -- brief 不写真实附件路径、真实 URL 或个人信息。 -- brief 明确 `blocked`、`ready`、`passed` 的 manifest 层边界。 -- brief 明确 manifest 通过只说明附件清单字段完整、脱敏、与 ignored local JSON/summary 可对齐。 -- brief 明确 manifest 通过不代表 DevTools UI、真机、朋友圈、payload、readback 或 viral journey 真实通过。 diff --git a/harness/viral-manual-journey-evidence-packet-checklist.md b/harness/viral-manual-journey-evidence-packet-checklist.md deleted file mode 100644 index 5ce7714..0000000 --- a/harness/viral-manual-journey-evidence-packet-checklist.md +++ /dev/null @@ -1,267 +0,0 @@ -# AD 传播链手测 Evidence Packet QA Checklist - -日期:2026-06-17 - -范围:本清单用于 AD 组在 AC 只读 UI confirmation recheck 之后,生成“viral journey evidence packet”的手测执行说明。它只定义评测者如何准备、执行和填写真实 evidence;不执行 WeChat DevTools、不写 evidence 文件、不生成截图/录屏/payload、不声明任何 journey passed。 - -## 0. 验收原则 - -- [ ] AD packet 的输入必须来自 AC 只读复核摘要,尤其是 `status`、`nextStep`、`notClaimed`、listener / smoke / viral preparation 状态。 -- [ ] 当 AC 状态不是 `ready_for_manual_journey` 时,AD 只能输出 `status: not_ready_for_manual_journey`、`packetStatus: not_ready`、阻塞原因和 `nextStep`,不得输出可执行的七条 journey packet。 -- [ ] 只有 AC 达到 `ready_for_manual_journey` 时,AD 才能输出七条 viral journey 的执行 packet;该 packet 的状态只能是 `status: ready_to_execute_manual_journey`、`packetStatus: ready_to_execute`,不是 `journey_passed`。 -- [ ] Ready packet 只表示评测者可以开始真实 DevTools 或真机手测;它不表示 DevTools UI journey passed、real-device journey passed 或 viral journey passed。 -- [ ] 所有输出都必须包含 exact 文案:`notClaimed: no DevTools UI journey passed; no real-device journey passed; no viral journey passed`。 -- [ ] AD 不得运行 DevTools 控制命令,不得打开/预览/上传小程序,不得自动点击系统分享菜单,不得写 ignored local evidence,不得把外部人工结果改写成自己的 passed 结论。 - -## 1. 记录字段 - -每次生成 packet 或 not-ready 摘要都必须记录以下字段: - -```text -status: not_ready_for_manual_journey | ready_to_execute_manual_journey -packetStatus: not_ready | ready_to_execute -acStatus: -acNextStep: -generatedAt: <日期时间和时区> -branch: <当前分支> -commit: <当前 HEAD> -worktree: <当前 worktree> -actor: AD_design_QA_packet_only -targetPort: 9420 -uiServicePortState: enabled | disabled | not_found | unavailable | not_confirmed | unknown -uiPortState: matches_9420 | mismatch | unconfirmed | unknown -listenerState: listening | no_listener | refused | timeout | unknown | not_run -smokeState: ready | blocked | unknown | not_run -viralJourneyPreparation: ready | blocked | unknown | not_run -manualJourneyStatus: unverified -evidenceWriteState: not_written_by_AD -commandsRunByAD: <只列只读验证命令;不得包含 DevTools 操作命令> -nextStep: <评测者下一步> -notClaimed: no DevTools UI journey passed; no real-device journey passed; no viral journey passed -``` - -字段约束: - -- [ ] `packetStatus=not_ready` 时必须有 `nextStep`,并解释 AC 当前为什么不能进入手测 packet。 -- [ ] `packetStatus=ready_to_execute` 时必须同时满足 `status=ready_to_execute_manual_journey`、`acStatus=ready_for_manual_journey`、`smokeState=ready`、`viralJourneyPreparation=ready` 或 AC 等价脱敏摘要。 -- [ ] `manualJourneyStatus` 在 AD packet 中固定为 `unverified`;AD 不读取或生成真实 passed evidence。 -- [ ] `evidenceWriteState` 必须明确 AD 没有写 evidence;真实结果只能由评测者在 ignored/local 结果文件中填写。 -- [ ] `commandsRunByAD` 只能记录文档/静态检查命令;不得记录任何 DevTools UI 或真机执行动作。 - -## 2. AC Blocked 时的 Not-Ready 输出 - -当 AC 仍处于 `pre_manual_confirmation`、`blocked_config_disabled`、`blocked_port_mismatch`、`blocked_no_listener`、`blocked_smoke_access`、`unknown` 或任何非 `ready_for_manual_journey` 状态时,AD 输出必须停在: - -```text -status: not_ready_for_manual_journey -packetStatus: not_ready -reason: AC is not ready_for_manual_journey -nextStep: <引用 AC 的下一步,例如用户人工开启 Service Port、修正端口、复跑只读 smoke、换环境> -manualJourneyStatus: unverified -notClaimed: no DevTools UI journey passed; no real-device journey passed; no viral journey passed -``` - -Not-ready checklist: - -- [ ] 不列出七条 journey 的逐步执行 packet。 -- [ ] 不生成 evidence JSON、blocked draft、截图、录屏、payload 或日志。 -- [ ] 不把 AC blocked、AC readiness、端口诊断或 smoke blocker 写成产品 failed。 -- [ ] 不把用户“已开启 Service Port”写成 listener ready、smoke ready 或 journey passed。 -- [ ] `nextStep` 必须是可执行的人工作业或只读复核动作,不能写成“已通过”。 - -## 3. Ready Packet 的七条 Journey 必填字段 - -只有 `acStatus=ready_for_manual_journey` 时,AD 才能输出以下七条 journey packet。每条 journey 都必须包含同一组字段: - -```text -journeyId: <必填,见下方七条 id> -packetState: ready_to_execute_manual_journey -entry: <真实 DevTools/真机入口或 route> -preconditions: -steps: <评测者要做的真实 UI 步骤> -expected: <应观察到的 UI / payload / gating 结果> -requiredEvidence: <截图/录屏/payload/log/readback 的脱敏引用要求> -payloadRequired: yes | no | if_inspectable -payloadFields: <必须检查的 path/query/title/imageUrl/attribution 字段> -ifCannotInspect: <必须填写具体限制、替代证据、未验证字段和 followUp> -actual: <由评测者真实填写;AD packet 中保持 empty> -statusToFill: passed | failed | blocked -followUpRequiredWhenBlockedOrFailed: yes -privacyNotes: <不得记录的隐私字段> -notClaimed: no DevTools UI journey passed; no real-device journey passed; no viral journey passed -``` - -七条 required journey: - -- [ ] `first-hop-share-entry`:首跳从分享进入低风险 active 任务。 - - `entry`:`/pages/detail/detail?id=&from=share` 或真实分享卡片入口。 - - `expected`:receiver guide 可见、receiver action strip 可见、按钮是 confirm/comment 行动、普通分享面板不竞争展示。 - - `requiredEvidence`:页面截图或录屏、post id、入口 query、设备/DevTools 信息、可见 UI 摘要。 - - `payloadRequired`:`if_inspectable`;若从系统分享卡片进入,应记录分享入口来源和 query 是否保留。 - -- [ ] `receiver-confirm-conversion`:接收者确认后的二跳提示。 - - `entry`:首跳分享详情页。 - - `expected`:确认动作真实成功、`receiverConversionPrompt` 出现、`actionRelayPrompt` 不抢主 CTA。 - - `requiredEvidence`:确认前后录屏或截图、确认计数/状态摘要、二跳提示截图、payload 摘要。 - - `payloadFields`:`path` 必须包含 `from=share`、`source=receiver`、`receiverAction=confirm`;可 inspect 时还要记录脱敏 `share_id`、`parent_share_id`、`share_depth`。 - - `ifCannotInspect`:说明哪个 DevTools/真机能力不暴露 path,用什么录屏或日志替代,哪些 payload 字段仍未验证。 - -- [ ] `receiver-comment-conversion`:接收者评论后的二跳提示。 - - `entry`:首跳分享详情页评论入口。 - - `expected`:评论真实提交成功、`receiverConversionPrompt` 出现、`commentRelayPrompt` 不抢主 CTA。 - - `requiredEvidence`:提交过程录屏或截图、评论成功摘要、本地/CloudBase 路径摘要、二跳提示截图、payload 摘要。 - - `payloadFields`:`path` 必须包含 `from=share`、`source=receiver`、`receiverAction=comment`;可 inspect 时还要记录脱敏 `share_id`、`parent_share_id`、`share_depth`。 - - `privacyNotes`:不得记录评论正文全文、真实昵称、头像、openid 或联系方式。 - -- [ ] `second-hop-receiver-source`:二跳接收者看到接力语境。 - - `entry`:真实二跳分享卡片;无法操作时可用 direct route 辅助,但必须标注不是系统卡片 evidence。 - - `expected`:`source=receiver` 语境可读,`receiverAction=confirm/comment` 文案区分上一位刚确认或刚补线索,普通分享面板不竞争展示。 - - `requiredEvidence`:入口来源、route/payload 摘要、首屏截图或录屏、confirm/comment 两种 action 的文案摘要。 - - `payloadFields`:`source=receiver`、`receiverAction=confirm|comment`。 - -- [ ] `ordinary-and-risk-entries`:普通入口和风险态不鼓励接收侧扩散。 - - `entry`:同一低风险 active 任务普通入口,以及 stale signal、report signal、`stale`、`resolved`、`expired`、`hidden` fixtures。 - - `expected`:普通入口不显示 receiver guide/action strip;任何 stale/report 或 closed 状态不显示鼓励性 public relay CTA。 - - `requiredEvidence`:每个 fixture 的 post id、状态、`staleCount`、`reportCount`、闭合原因、UI 截图或录屏摘要。 - - `payloadRequired`:`no`;如系统菜单仍可打开,要记录谨慎文案或无法检查说明。 - -- [ ] `timeline-share-channel`:低风险 active 详情页朋友圈系统渠道。 - - `entry`:低风险 active 详情页真实系统菜单。 - - `expected`:真实系统菜单可见“发送给朋友”和“分享到朋友圈”;`onShareTimeline` 或等价 payload 可记录时包含正确 query;朋友圈/单页模式首屏可读。 - - `requiredEvidence`:系统菜单截图或录屏、timeline payload 摘要、单页模式或等价入口首屏证据。 - - `payloadFields`:`query` 必须包含 `id`、`from=share`、`source=timeline`、`shareChannel=timeline`;记录 `title` 和 `imageUrl` 或具体无法 inspect 原因。 - - `ifCannotInspect`:不得写 passed;必须写 blocked 或保留未验证字段和 follow-up。 - -- [ ] `timeline-risk-gating`:风险和闭合任务不开放鼓励性朋友圈。 - - `entry`:weak stale/report、`stale`、`resolved`、`expired`、`hidden`、unknown 或刷新失败 fixtures。 - - `expected`:不出现鼓励性 `shareTimeline` / “分享到朋友圈”;如仍有 inspectable title/query/copy,必须是谨慎语义。 - - `requiredEvidence`:每个 fixture 的菜单缺失证据、状态字段、谨慎文案摘要或具体检查 blocker。 - - `payloadRequired`:`if_inspectable`;无法打开菜单或无法 inspect payload 时只能 blocked,不能 passed。 - -## 4. Allowed Evidence - -- [ ] 脱敏截图/录屏引用:只写 ignored/local 附件编号、场景、时间、设备;不要把原图或完整路径提交到可提交文件。 -- [ ] 结构化 payload 摘要:`journeyId`、`postId`、`entry`、`path` / `query`、`title` 摘要、`imageUrl` 是否存在、`source`、`receiverAction`、`shareChannel`、脱敏 attribution id。 -- [ ] UI 观察摘要:哪些组件可见/不可见、按钮优先级、风险态 gating、单页首屏是否可读。 -- [ ] 环境摘要:DevTools 版本、基础库、设备/真机、网络、CloudBase 是否启用、fixture 来源。 -- [ ] 命令摘要:只记录命令名、退出码、归一化状态和关键 blocker / ready 语义。 -- [ ] 无法检查说明:必须写明具体工具限制、替代 evidence、未验证字段和 follow-up。 - -## 5. Forbidden Evidence - -- [ ] raw config、完整 JSON dump、完整日志、完整 stdout / stderr、完整网络包、完整 CloudBase 记录。 -- [ ] 截图原图、录屏原件、二维码、系统分享面板原图或真实 payload 文件进入可提交路径。 -- [ ] 完整本地路径、文件名、DevTools user-data 路径、日志路径、evidence 附件路径。 -- [ ] 账号、登录态、token、cookie、openid、unionid、微信号、昵称、头像、手机号、邮箱、设备唯一标识。 -- [ ] 精确经纬度、详细住址、真实评论正文全文、图片 URL、CloudBase fileID、真实云环境 id 或私有 AppID。 -- [ ] 把 readiness、blocked draft、schema checker、AC ready 或 packet ready 当成 DevTools UI passed、real-device passed 或 viral journey passed 的文字。 - -## 6. 命令边界 - -AD 允许运行的只读命令: - -```bash -pwd -git branch --show-current -git rev-parse --short HEAD -git status --short -node harness/check-harness.mjs -git diff --check -``` - -AD 可以在文档中建议评测者手动运行,但不得代替评测者写结果: - -```bash -node --no-warnings scripts/check-viral-journey-manual-evidence.mjs -node --no-warnings scripts/check-devtools-readiness.mjs -``` - -AD 始终禁止: - -- [ ] `open`、`preview`、`upload`、`login`、`logout`、`quit` 或任何 WeChat DevTools 控制命令。 -- [ ] `kill`、`pkill`、`killall`、`launchctl`、重启 IDE、清缓存、清 storage。 -- [ ] 修改 Service Port、settings、端口号、配置文件、user data、local evidence 或 CloudBase 数据。 -- [ ] 自动化点击 DevTools 设置 UI、系统分享菜单、朋友圈菜单或小程序页面。 -- [ ] 创建、覆盖、移动、提交截图、录屏、payload、raw log、raw config 或 ignored/local evidence JSON。 - -## 7. Status Mapping - -- [ ] AC `pre_manual_confirmation` -> AD `status: not_ready_for_manual_journey` / `packetStatus: not_ready`。 - - `nextStep`:用户先在 WeChat DevTools UI 中人工确认 Service Port。 - - 不输出七条 journey packet。 - -- [ ] AC `blocked_config_disabled` -> AD `status: not_ready_for_manual_journey` / `packetStatus: not_ready`。 - - `nextStep`:用户决定是否手动开启 Service Port,然后复跑 AC 只读复核。 - - 不把“配置 disabled”写成产品失败。 - -- [ ] AC `blocked_port_mismatch` -> AD `status: not_ready_for_manual_journey` / `packetStatus: not_ready`。 - - `nextStep`:确认 UI 显示端口,按 AC 指引使用目标端口或保持 blocked。 - - 不自动改端口。 - -- [ ] AC `blocked_no_listener` -> AD `status: not_ready_for_manual_journey` / `packetStatus: not_ready`。 - - `nextStep`:用户检查 DevTools 实例、Service Port 状态、IDE 项目和网络,再复跑只读 listener / smoke。 - - 不进入手测 packet。 - -- [ ] AC `blocked_smoke_access` -> AD `status: not_ready_for_manual_journey` / `packetStatus: not_ready`。 - - `nextStep`:先恢复 smoke access;listener ready 不能替代页面或 journey ready。 - - 不输出 `ready_to_execute_manual_journey`。 - -- [ ] AC `ready_for_manual_journey` -> AD `status: ready_to_execute_manual_journey` / `packetStatus: ready_to_execute`。 - - `nextStep`:评测者执行七条真实 DevTools 或真机 journey,并在 ignored/local result 中填写 evidence、actual、payload 或无法检查说明。 - - 必须写:`notClaimed: no DevTools UI journey passed; no real-device journey passed; no viral journey passed`。 - - 不得写:`journey_passed`、`manual_journey_passed`、`DevTools UI passed` 或 `real-device passed`。 - -- [ ] 外部人工结果声称 passed -> AD `external_manual_evidence_needs_review`。 - - `nextStep`:由独立 checker 和人工 reviewer 复核;AD packet 只能引用“外部证据待复核”,不能自己 claim passed。 - - 必须保留 `notClaimed` exact 文案。 - -## 8. Static Guard 期望 - -后续若新增 AD packet 生成脚本或 checker,应覆盖以下规则: - -- [ ] AC gate:源码必须拒绝非 `ready_for_manual_journey` 输入生成七条 journey packet,并输出 `not_ready_for_manual_journey`、`packetStatus: not_ready` 与非空 `nextStep`。 -- [ ] Ready wording guard:`ready_for_manual_journey` 只能映射到 `ready_to_execute_manual_journey`,不得出现 `journey_passed`、`ui_passed`、`real_device_passed`、`viral_passed`。 -- [ ] Exact notClaimed guard:所有输出必须包含 `notClaimed: no DevTools UI journey passed; no real-device journey passed; no viral journey passed`。 -- [ ] Seven journey guard:ready packet 必须且只能列出七条 required journey id,且每条包含 `entry`、`preconditions`、`steps`、`expected`、`requiredEvidence`、`payloadRequired`、`ifCannotInspect`、`statusToFill`。 -- [ ] No evidence write guard:AD 脚本不得写 JSON、截图、录屏、payload、日志、CloudBase 数据或 DevTools 配置。 -- [ ] Side-effect guard:源码不得包含 DevTools open/preview/upload/quit/login/logout、kill、settings 修改、cache/storage 清理、UI 自动点击。 -- [ ] Privacy guard:输出必须抑制 raw path、文件名、账号、token、cookie、openid、真实云环境 id、精确位置和 raw stdout/stderr。 -- [ ] Blocked nextStep guard:任何 `not_ready_for_manual_journey` / `not_ready` 或 blocked 映射必须带可执行 `nextStep`。 - -## 9. 负向用例 - -- [ ] AC 为 `blocked_no_listener` 时输出七条 journey packet:应失败。 -- [ ] AC 为 `blocked_smoke_access` 时输出 `ready_to_execute_manual_journey`:应失败。 -- [ ] Ready packet 中出现 `journey_passed`、`passed_by_AD`、`DevTools UI passed` 或 `real-device passed`:应失败。 -- [ ] 缺少 exact `notClaimed` 文案:应失败。 -- [ ] 七条 journey 少一条、多一条、id 拼错或缺必填字段:应失败。 -- [ ] `timeline-share-channel` 没有要求 `source=timeline` 和 `shareChannel=timeline`:应失败。 -- [ ] confirm/comment 二跳 payload 没有要求 `receiverAction=confirm/comment`:应失败。 -- [ ] 无法 inspect payload 时允许写 passed 且没有限制说明:应失败。 -- [ ] AD 命令写入 local evidence、生成截图、覆盖 JSON 或提交 raw payload:应失败。 -- [ ] 输出中包含完整路径、文件名、raw config、token、cookie、openid、真实评论正文或精确经纬度:应失败。 - -## 10. 收尾验证命令 - -本 checklist 修改后,AD closeout 只运行不产生 evidence 的验证: - -```bash -git diff --check -git status --short -``` - -如果本轮允许跑完整 harness,可再运行: - -```bash -bash harness/init.sh -node scripts/check-json.mjs -node harness/check-harness.mjs -node --no-warnings scripts/check-devtools-readiness.mjs -``` - -期望: - -- [ ] 可提交改动只包含 `harness/viral-manual-journey-evidence-packet-checklist.md`。 -- [ ] 没有生成或修改 ignored/local evidence、截图、录屏、payload、raw log 或 DevTools 用户数据。 -- [ ] 没有运行任何 DevTools open/preview/upload/quit、kill、cache/settings/storage 修改命令。 -- [ ] 最终汇报只说 checklist 已准备好;不说任何 viral journey passed。 diff --git a/harness/viral-manual-journey-evidence-packet-product-brief.md b/harness/viral-manual-journey-evidence-packet-product-brief.md deleted file mode 100644 index a90b2f4..0000000 --- a/harness/viral-manual-journey-evidence-packet-product-brief.md +++ /dev/null @@ -1,216 +0,0 @@ -# AD 组 Viral Journey Evidence Packet 产品 Brief - -日期:2026-06-17 - -角色:AD 组产品 agent - -## 目标 - -新增一个“viral journey evidence packet”手测执行包,用于在 AC 输出 `ready_for_manual_journey` 之后,把七条传播 journey 的真实手测字段、payload 检查、隐私边界和回填命令整理成一个可执行 checklist。 - -这个包的目标是降低用户或评测者执行真实 evidence 时的漏项风险。它只做手测组织、字段约束、证据边界和回填提示,不提升现有自动分数,不伪造通过,不替代 WeChat DevTools 或真机观察。 - -## 非目标 - -- 不 claim DevTools passed、real-device passed、viral journey passed 或 CloudBase evidence passed。 -- 不自动执行 WeChat DevTools,不打开、不退出、不 preview、不 upload、不 kill、不清缓存、不修改 settings、不切项目、不改端口、不写本地存储。 -- 不把端口 ready、smoke ready、prepare 命令通过、模板完整或 checklist 生成写成 journey passed。 -- 不新增或修改小程序业务代码、分享策略、归因逻辑、评论/确认流程、风控策略或 UI 文案。 -- 不输出本机路径、token、cookie、账号、登录态、raw stdout、raw stderr、完整日志、完整配置或系统隐私信息。 -- 不写 ignored local evidence 文件,除非未来有明确 separate write helper,并且该 helper 具备独立的 ignored 校验、脱敏校验和 no-passed-draft guard。 - -## 用户与评测价值 - -用户价值:真实手测通常跨越 DevTools、系统分享面板、朋友圈、分享 payload、接收者动作、CloudBase 或本地数据状态。evidence packet 把这些步骤变成稳定 checklist,让执行者知道每条 journey 需要看什么、不能写什么、缺了什么就保持 blocked。 - -评测价值:把“环境入口 ready”和“传播链通过”拆开。评测者可以用 packet 检查证据字段是否齐备、隐私边界是否守住、七条 journey 是否逐条覆盖,而不会把 AC 的 `ready_for_manual_journey` 误判为真实产品通过。 - -## 输入与输出状态 - -输入状态只接受 AC 或后续只读复核的脱敏结果: - -- `blocked_config_disabled`:用户尚未在 UI 中确认或开启 Service Port。 -- `blocked_no_listener`:用户可能已确认 UI,但目标端口仍无 listener 或连接失败。 -- `ready_for_manual_journey`:Service Port、只读 smoke access 和手测准备入口已经满足继续人工手测的最低条件。 -- `unknown`:缺少足够脱敏状态,不能生成可执行手测 packet。 - -输出状态必须保持保守: - -- `status: not_ready_for_manual_journey` + `packetStatus: not_ready`:AC 尚未输出 `ready_for_manual_journey`,只展示阻塞原因和下一步,不展开可回填 passed 字段。 -- `status: ready_to_execute_manual_journey` + `packetStatus: ready_to_execute`:AC 已输出 `ready_for_manual_journey`,可以展示七条 journey checklist、payload 检查项、证据字段和回填命令。 -- `blocked`:仍缺 DevTools、真机、系统分享、朋友圈、payload、CloudBase 或数据 fixture 之一,不能继续或不能写 passed。 -- `manual_results_pending`:packet 已准备好,但尚未执行真实手测。 - -任何输出状态都不能表示 `journey_passed`。`journey_passed` 只能来自后续真实手测 evidence 文件或人工复核结论。 - -## Ready 前行为 - -在 AC 状态达到 `ready_for_manual_journey` 前,evidence packet 必须保持 blocked / not-ready: - -1. 读取或接收 AC 的脱敏状态摘要。 -2. 如果状态不是 `ready_for_manual_journey`,只输出阻塞说明、下一步和不可 claim 清单。 -3. 不展示可填写为 `passed` 的 journey 结果字段。 -4. 不创建 ignored local evidence。 -5. 不运行 DevTools 控制动作或系统分享动作。 -6. 不输出 raw stdout/stderr,不转述本机完整路径或日志。 - -ready 前不得展示七条 journey 的逐项执行 packet,也不得列出可回填为 passed 的字段;只能说明 required journey 会在 AC ready 后展开,并明确当前不具备真实 evidence 采集条件。 - -## Ready 后行为 - -在 AC 输出 `ready_for_manual_journey` 后,packet 才能进入执行 checklist 模式: - -1. 展示七条 required journey 的固定 ID、入口、操作步骤、预期观察、payload 检查、隐私检查、证据字段和 blocked/failed/passed 判定条件。 -2. 标出每条 journey 是否需要 DevTools、真机、系统分享面板、朋友圈、CloudBase、登录态、特定数据 fixture 或二跳接收者环境。 -3. 提供回填命令建议,但命令只能指向未来的校验或回填流程,不自动写 evidence。 -4. 对无法 inspect payload、无法打开系统菜单、无法触达朋友圈或 CloudBase 未部署的情况,要求写 `blocked` 和具体 `blocker/followUp`。 -5. 对真实观察到的产品不符合预期,要求写 `failed`、实际现象、脱敏 evidence 和下一步修复建议。 -6. 只有真实 UI 或真机观察完整、payload 或限制说明完整、证据脱敏完整时,后续 evidence 才能把对应 journey 写为 `passed`。 - -## 七条 Journey 覆盖 - -packet 必须固定覆盖以下七条 journey,不能删除、合并或重命名。 - -### `first-hop-share-entry` - -- 目标:首跳接收者从分享进入低风险 active 任务。 -- 入口:分享详情页,带 `from=share`。 -- 关键检查:receiver guide 可见;receiver action strip 可见;普通分享面板不与接收者行动入口竞争;主行动是 confirm/comment,而不是直接鼓励扩散。 -- 必填证据字段:post id 脱敏标识、入口来源、设备或模拟器类型、实际可见 UI、是否有截图/录屏或文字观察、隐私处理说明。 - -### `receiver-confirm-conversion` - -- 目标:接收者确认后出现二跳接力提示。 -- 入口:首跳分享详情页,点击确认有效。 -- 关键检查:确认成功;receiver conversion prompt 可见;其他 relay prompt 不抢主 CTA;分享 payload 包含接收者确认来源。 -- 必填证据字段:动作前后 UI、确认结果、payload 检查结果、无法 inspect payload 时的具体限制、CloudBase 或本地状态说明。 - -### `receiver-comment-conversion` - -- 目标:接收者评论后出现二跳接力提示。 -- 入口:首跳分享详情页,提交有效评论。 -- 关键检查:评论成功;receiver conversion prompt 可见;评论 relay 不抢主 CTA;分享 payload 包含接收者评论来源。 -- 必填证据字段:评论提交路径、评论成功 UI、评论存储路径说明、payload 检查结果、无法 inspect payload 时的具体限制。 - -### `second-hop-receiver-source` - -- 目标:二跳接收者看到接力语境。 -- 入口:优先来自真实系统分享卡片;无法操作时可用直接 route 辅助,但必须标注差异。 -- 关键检查:二跳文案表达“有人接力”;摘要和 rows 引导下一位先看确认与评论;普通分享面板不在同一状态竞争展示。 -- 必填证据字段:二跳入口来源、route/query 或 payload 摘要、实际文案、面板状态、是否真实系统分享进入。 - -### `ordinary-and-risk-entries` - -- 目标:普通入口和风险态不鼓励接收侧扩散。 -- 入口:普通详情入口、弱 stale/report signal、`stale`、`resolved`、`expired`、`hidden` 等 fixture。 -- 关键检查:普通入口不展示 receiver guide/action strip;风险或闭合状态不暴露接收侧 public relay CTA;文案提醒谨慎看评论、确认或最新状态。 -- 必填证据字段:fixture 状态、stale/report 计数或闭合原因、实际 UI、是否存在扩散 CTA、风险文案观察。 - -### `timeline-share-channel` - -- 目标:低风险 active 详情页具备朋友圈系统渠道。 -- 入口:低风险 active 详情页。 -- 关键检查:真实系统菜单可见“发送给朋友”和“分享到朋友圈”;可 inspect 的 timeline query/title/image 保留任务 id、分享来源和 timeline 渠道;朋友圈或等价单页模式首屏可读。 -- 必填证据字段:系统菜单观察、timeline payload 摘要、单页模式首屏观察、无法 inspect payload 时的具体系统限制。 - -### `timeline-risk-gating` - -- 目标:风险和闭合任务不开放鼓励性朋友圈。 -- 入口:弱 stale/report signal、`stale`、`resolved`、`expired`、`hidden`、unknown fixture。 -- 关键检查:系统菜单或可 inspect 分享信息不出现鼓励性朋友圈扩散;标题和页面文案保持谨慎;无法打开菜单时只能 blocked。 -- 必填证据字段:fixture 状态、菜单观察、payload 或限制说明、谨慎文案观察、blocked/followUp。 - -## 允许与禁止证据 - -允许证据: - -- 脱敏 UI 截图、录屏或文字观察摘要。 -- 脱敏 route/query/payload 摘要,只保留评测所需键值,不保留 token、账号、完整路径或 raw logs。 -- 用户手动说明的系统菜单状态、真机型号类别、DevTools/真机环境类别。 -- CloudBase 或本地存储路径的结论性说明,例如“评论写入云端成功”或“当前 CloudBase 未部署,评论 evidence blocked”。 -- 每条 journey 的 `actual`、`evidence`、`payloadCheck`、`privacyCheck`、`blocker`、`followUp` 等结构化字段。 - -禁止证据: - -- 本机绝对路径、完整文件名清单、raw stdout、raw stderr、完整日志、完整配置、raw storage dump。 -- token、cookie、session、账号、手机号、open id、union id、AppID 私密值、CloudBase 密钥或任何可识别个人/环境的信息。 -- DevTools 自动 quit/open/preview/upload/kill/cache/settings 的输出。 -- 只有 prepare/checklist 结果、环境 ready、端口 ready、smoke ready、用户口头确认但无真实 UI 观察的 `passed`。 -- 未脱敏截图、包含个人信息的分享面板、包含完整 payload 或完整日志的附件。 - -## 与用户侧自发裂变目标的关系 - -用户侧自发裂变目标是:发布者或接收者认为任务值得转发,首跳接收者完成确认/评论后自然触发二跳接力,低风险任务可以进入朋友或朋友圈渠道,风险和闭合任务被谨慎限制。 - -evidence packet 不创造裂变,也不优化转化。它只帮助评测者回答三个问题: - -- 用户是否真实看到了可自发接力的上下文和主行动。 -- 接收者完成确认/评论后,二跳 payload 和 UI 是否仍指向接力来源。 -- 风险态、闭合态和隐私边界是否阻止了不该发生的扩散。 - -因此 packet 不能被用来宣称“裂变目标已经达成”。它只能降低真实 evidence 收集时的漏项,并为后续产品判断提供干净材料。 - -## 评测口径 - -评测必须分层: - -- `environment_ready`:AC 输出 `ready_for_manual_journey`,只代表可以开始真实手测。 -- `ready_to_execute_manual_journey`:七条 journey checklist、字段和回填命令已准备,只代表执行材料完整。 -- `journey_blocked`:真实手测受 DevTools、真机、系统菜单、朋友圈、payload、CloudBase、登录态或 fixture 阻塞。 -- `journey_failed`:真实手测已执行,并观察到产品行为不符合预期。 -- `journey_passed`:真实手测已执行,actual 非空,证据脱敏且非空,payload 检查或限制说明完整,隐私检查通过。 - -不得接受以下评测表述: - -- “AC ready,所以七条 journey 通过。” -- “packet 生成,所以 DevTools/真机通过。” -- “没有 listener blocker 了,所以朋友圈通过。” -- “payload 无法 inspect,但仍然 passed。” -- “blocked draft 或 checklist 可作为 passed evidence。” - -## 下一步分叉 - -- AC 尚未 ready:保持 `status: not_ready_for_manual_journey` / `packetStatus: not_ready`,提示用户先完成 UI Service Port 人工确认和只读复核。 -- AC ready 但没有真实 DevTools/真机:输出 `status: ready_to_execute_manual_journey` / `packetStatus: ready_to_execute` 与 `manual_results_pending`,引导用户安排真实手测。 -- 系统分享或朋友圈不可用:对应 journey 写 `blocked`,补充系统限制和下一步设备/环境要求。 -- payload 无法 inspect:对应 journey 不能直接 passed;若 UI 行为可观察,也只能写 payload blocked 或 passed-with-payload-limitation 待评审确认,默认建议 blocked。 -- CloudBase 未部署或数据不可共享:涉及评论、确认、归因或跨用户观察的 journey 写 `blocked`,说明需部署或换 fixture。 -- 真实 UI 不符合预期:写 `failed`,记录 actual、脱敏 evidence 和修复建议。 -- 七条 journey 均有完整真实证据:进入人工复核,复核通过后再由独立 evidence checker 聚合 overall status。 - -## 建议脚本与 Guard 需求 - -建议新增或扩展一个只读准备脚本,例如 `prepare:viral-journey-evidence-packet`。该脚本的职责是读取 AC 的脱敏状态、输出 checklist 和回填指引;默认不写 evidence 文件。 - -脚本/guard 需求: - -- AC 状态 guard:只有输入状态为 `ready_for_manual_journey` 时才展示完整可执行 checklist;其他状态只能输出 `not_ready_for_manual_journey` / `not_ready` / blocked。 -- No passed claim guard:禁止输出 DevTools passed、real-device passed、viral journey passed、CloudBase passed 等结论性通过文案。 -- No side-effect guard:禁止 quit/open/preview/upload/kill/cache/settings/storage/project-switch 等副作用命令出现在默认脚本路径。 -- Sensitive-output guard:禁止输出本机路径、token、cookie、账号、登录态、raw stdout、raw stderr、完整日志、完整配置和 raw payload。 -- Evidence-write guard:默认不创建 ignored local evidence;若未来新增 separate write helper,必须强制 ignored 路径校验、字段 schema 校验、脱敏校验、blocked draft 不可 passed、existing file 不覆盖。 -- Seven-journey guard:固定校验七条 journey ID 唯一存在,且每条包含 actual、evidence、payloadCheck、privacyCheck、status、blocker/followUp 适用字段。 -- Payload guard:confirm/comment/timeline journey 必须有 payload 摘要或无法 inspect 的具体原因;缺失时不能 passed。 -- Privacy guard:每条 evidence 必须有隐私处理说明;发现敏感字段时拒绝输出或要求重填。 -- Rubric guard:overall status 只能由真实 journey 状态聚合,不能由 checklist readiness、AC ready 或脚本 exit code 推导。 - -## 回填命令建议 - -ready 后 packet 可以展示命令建议,但命令语义必须保守: - -```text -npm run prepare:devtools-ui-confirmation -npm run prepare:viral-journey-evidence-packet -node --no-warnings scripts/check-viral-journey-manual-evidence.mjs -``` - -这些命令只代表复核、准备或校验入口。它们不得自动执行真实 DevTools 手测,不得写 passed evidence,不得把 ignored local draft 写入版本库,也不得输出 raw stdout/stderr 或本机隐私信息。 - -## 验收标准 - -- brief 明确写出目标、非目标、用户/评测价值、输入/输出状态、ready 前/ready 后行为、七条 journey 覆盖、允许/禁止证据、与用户侧自发裂变目标的关系、评测口径、下一步分叉和建议脚本/guard。 -- brief 明确声明不 claim DevTools/real-device/viral journey passed。 -- brief 明确声明不自动 quit/open/preview/upload/kill/cache/settings。 -- brief 明确声明不输出本机路径、token、raw stdout/stderr。 -- brief 明确声明不写 ignored local evidence,除非未来有明确 separate write helper。 -- brief 只定义手测执行包产品边界,不把当前 blocked 状态升级为通过。 diff --git a/harness/viral-manual-summary-integrity-checklist.md b/harness/viral-manual-summary-integrity-checklist.md deleted file mode 100644 index 009b3fa..0000000 --- a/harness/viral-manual-summary-integrity-checklist.md +++ /dev/null @@ -1,150 +0,0 @@ -# Viral manual journey summary integrity guard QA 清单 - -日期:2026-06-17 - -范围:用于后续开发新增静态 guard,检查 viral manual journey ignored local JSON 与 Markdown summary 是否同源、完整、脱敏。guard 通过只代表本地结果 JSON 和本地 summary 在字段与敏感内容边界上保持一致,不代表 WeChat DevTools UI journey、真机 journey 或 viral journey 已经 passed。 - -工作目录:`/tmp/street-tasks-iter-worktrees/viral-manual-summary-integrity` - -重要边界: - -- [ ] 只读取 ignored local pair:`harness/manual-test-results.local-viral-journey*.json` 与对应的 `harness/manual-test-summary.local*.md`。 -- [ ] pair 的 JSON 和 Markdown 都必须通过 `git check-ignore`;未 ignored 的 local result 或 summary 不得被 readiness 扫描成证据。 -- [ ] 不修改或引用 `harness/viral-journey-manual-results.example.json` 作为真实 evidence。 -- [ ] 不生成、不提交、不移动截图、录屏、payload、raw log、CloudBase 记录或 ignored local evidence。 -- [ ] summary integrity guard 只防止摘要被手工改坏、拼接或泄露敏感内容;真实 UI 仍需 DevTools 或真机人工验证。 - -## 1. JSON 是唯一结论源 - -- [ ] guard 必须先对 result JSON 运行 viral 专用检查: - - ```bash - node --no-warnings scripts/check-viral-journey-manual-evidence.mjs - ``` - -- [ ] 如果该命令失败,summary guard 必须失败;不能从 Markdown summary 反推或补造 `passed` 结论。 -- [ ] `status=passed` 的 journey 必须已经由 `scripts/check-viral-journey-manual-evidence.mjs` 校验过 required evidence、actual、payload 或无法检查说明。 -- [ ] summary guard 不能把 `check-devtools-readiness.mjs`、blocked draft、summary 生成成功、branch/commit 匹配或 evidenceCount 非零写成 UI passed。 -- [ ] 如果 JSON 中没有 passed journey,summary 中不得出现任何 passed 语义;如果 JSON 中有 passed journey,summary 只能复述 JSON 已验证过的状态,不能新增 passed 范围。 - -## 2. 必须校验的同源字段 - -guard 必须从同一份 ignored local result JSON 和同一份 Markdown summary 中提取并比对以下字段。字段缺失、重复、值不同或无法解析都应失败。 - -顶层字段: - -- [ ] `branch`:summary `Run` 表的 `branch` 必须等于 JSON 顶层 `branch`。 -- [ ] `commit`:summary `Run` 表的 `commit` 必须等于 JSON 顶层 `commit`。 -- [ ] `overallStatus`:summary `Summary` 表的 `overallStatus` 必须等于 JSON `summary.overallStatus`。 - -七条 journey 必须且只能各出现一次: - -| id | title | -| --- | --- | -| `first-hop-share-entry` | `首跳从分享进入低风险 active 任务` | -| `receiver-confirm-conversion` | `接收者确认后的二跳提示` | -| `receiver-comment-conversion` | `接收者评论后的二跳提示` | -| `second-hop-receiver-source` | `二跳接收者看到接力语境` | -| `ordinary-and-risk-entries` | `普通入口和风险态不鼓励接收侧扩散` | -| `timeline-share-channel` | `低风险 active 详情页朋友圈系统渠道` | -| `timeline-risk-gating` | `风险和闭合任务不开放鼓励性朋友圈` | - -每条 journey 行必须校验: - -- [ ] `id`:summary 行必须存在,且和 JSON `journey.id` 一致。 -- [ ] `title`:summary 行标题必须和 JSON `journey.title` 一致,不能用旧标题、缩写或其他 journey 的标题。 -- [ ] `status`:summary 行状态必须和 JSON `journey.status` 一致。 -- [ ] `actual`:summary 行 `actual` 必须来自 JSON `journey.actual`;至少要能拦截被替换为 `unrelated text`、空值或其他 journey actual。 -- [ ] `evidenceCount`:summary 只能显示 evidence 数量,且必须等于 JSON `journey.evidence` 计算出的数量。 -- [ ] `blocker`:summary 行 blocker 必须来自 JSON `journey.blocker`;若 JSON 没有 `blocker`,则必须来自 `journey.risks` 的脱敏摘要。 -- [ ] `followUp`:summary 行 `followUp` 必须来自 JSON `journey.followUp`,不能清空或替换成与 JSON 无关的建议。 - -## 3. Evidence 最小化和敏感内容边界 - -summary 只能承载 evidence 数量,例如 `evidenceCount=0`、`evidenceCount=2`。不得透传 raw evidence 内容。 - -必须禁止出现在 summary 中: - -- [ ] `evidence[].path`、`evidence[].url`、`evidence[].value`、`evidence[].details` 的原始内容。 -- [ ] raw screenshot / recording / payload / log 路径、文件名、URL、完整 stdout/stderr、完整 JSON dump。 -- [ ] `cloud://`、CloudBase fileID、真实云环境 id、私有 AppID。 -- [ ] `/Users/`、`/private/tmp/` 下的真实用户路径、DevTools user-data 路径、机器名。 -- [ ] token、cookie、session、authorization、bearer、password、private key、access_token、refresh_token、二维码或预览链接凭证。 -- [ ] 手机号、微信号、邮箱、openid、unionid、真实昵称、头像 URL、精确地址、精确经纬度、评论正文全文。 - -敏感扫描建议: - -- [ ] 对整个 Markdown summary 做写入前和 guard 复核扫描;命中敏感模式时失败,并报告行号和类别。 -- [ ] 对 Markdown 表格中的 `actual`、`blocker`、`followUp` 同样执行敏感扫描;这些字段虽来自 JSON,也不能绕过脱敏边界。 -- [ ] 错误信息只描述敏感类别和行号,不回显敏感原文。 - -## 4. 正向 guard 期望 - -- [ ] 生成 blocked draft 时,`scripts/prepare-viral-journey-manual-evidence.mjs` 只创建 ignored local JSON,且所有 journey 初始为 `blocked`、evidence 为空。 -- [ ] 从 viral JSON 生成 summary 时,`scripts/create-manual-summary.mjs` 或后续 wrapper 必须保留 `branch`、`commit`、`overallStatus` 和七条 journey 的同源字段。 -- [ ] 对同一 pair 运行 future summary integrity guard,例如: - - ```bash - node scripts/check-viral-manual-summary-integrity.mjs \ - --results harness/manual-test-results.local-viral-journey.json \ - --summary harness/manual-test-summary.local-viral-journey.md - ``` - -- [ ] 命令成功时只能输出类似 `Viral manual summary integrity checks passed.`。 -- [ ] 成功输出必须继续声明:该检查只验证 ignored local JSON/summary 同源完整性,不是 DevTools UI passed evidence,也不是真机 passed evidence。 - -## 5. 负向样例 - -以下样例都必须失败;每个样例只在 ignored local 临时文件上执行,结束后清理。 - -- [ ] summary 缺少任一 required journey,例如删掉 `timeline-risk-gating` 行:应失败,错误点名 missing journey id。 -- [ ] summary 多出重复 journey,例如复制一行 `receiver-confirm-conversion`:应失败,错误点名 duplicate journey id。 -- [ ] summary journey `status` 与 JSON 不同,例如 JSON 为 `blocked`、summary 改成 `passed`:应失败。 -- [ ] summary `evidenceCount` 与 JSON evidence 数量不同,例如 JSON evidence 数组长度为 `0`、summary 改成 `1`:应失败。 -- [ ] summary `branch` 与 JSON 顶层 `branch` 不同:应失败。 -- [ ] summary `commit` 与 JSON 顶层 `commit` 不同:应失败。 -- [ ] summary `overallStatus` 与 JSON `summary.overallStatus` 不同:应失败。 -- [ ] summary `actual`、`blocker` 或 `followUp` 被清空、替换为 `unrelated text` 或串到另一条 journey:应失败。 -- [ ] summary 包含 raw evidence 路径、URL、value 或 details,例如 `/Users/.../screenshot.png`、`cloud://...`、payload 完整 path:应失败。 -- [ ] summary 包含 token、cookie、Authorization、手机号或真实账号标识:应失败。 -- [ ] summary 出现 `passed by AE`、`AE passed`、`summary passed`、`readiness passed so UI passed`、`manual journey passed by checklist` 等误导措辞:应失败。 -- [ ] JSON 没有先通过 `scripts/check-viral-journey-manual-evidence.mjs`,但 summary 写成 `passed`:应失败。 - -## 6. Readiness 接入要求 - -`scripts/check-devtools-readiness.mjs` 只应调用 summary integrity preflight 或等价只读入口,不应生成 JSON、生成 summary、运行 DevTools、打开 UI、修改 Service Port 或写 evidence。 - -readiness 扫描规则: - -- [ ] 只扫描 `harness/` 下 ignored local viral summary/result pair;没有 matching pair 时不扫描单独 JSON 或单独 summary。 -- [ ] 如果发现 local summary 但没有 matching ignored local result,应失败并点名缺失 pair,而不是跳过后报告成功。 -- [ ] 如果发现 local result 但没有 matching summary,可以输出没有 summary pair 可检查;不得把 result 单独当作 summary integrity passed。 -- [ ] 如果没有任何 matching ignored local summary/result pair,输出必须包含 `nothing checked` 或等价文案。 -- [ ] 无 pair 输出还必须包含 `not UI passed evidence` 或等价文案,明确这不是 DevTools UI passed、真机 passed 或 viral journey passed。 -- [ ] 有 pair 时逐对运行 summary integrity guard;任一 pair 失败则 readiness 失败,不能被其他 pair 的通过掩盖。 -- [ ] readiness 最终成功文案只能说静态 gate / local pair integrity 通过,不能写 `viral manual journey passed`。 - -## 7. 收尾验证 - -本 QA checklist 落地后至少运行: - -```bash -bash harness/init.sh -node scripts/check-json.mjs -node harness/check-harness.mjs -git diff --check -``` - -如果后续实现了 summary integrity guard 或 preflight,还应追加: - -```bash -node --check scripts/check-viral-manual-summary-integrity.mjs -node --no-warnings scripts/check-viral-journey-manual-evidence.mjs -node --no-warnings scripts/check-devtools-readiness.mjs -``` - -期望: - -- [ ] 可提交改动不包含 `harness/manual-test-results.local*.json` 或 `harness/manual-test-summary.local*.md`。 -- [ ] 没有新增截图、录屏、payload、raw log 或 CloudBase evidence。 -- [ ] 最终报告只说明 checklist 或 guard 准备情况;不声明任何 viral journey、DevTools UI 或真机路径 passed。 diff --git a/harness/viral-manual-summary-integrity-product-brief.md b/harness/viral-manual-summary-integrity-product-brief.md deleted file mode 100644 index c34a90d..0000000 --- a/harness/viral-manual-summary-integrity-product-brief.md +++ /dev/null @@ -1,170 +0,0 @@ -# Viral Manual Journey Summary Integrity 产品 Brief - -- 日期:2026-06-17 -- 分支:`codex/iter-viral-manual-summary-integrity` -- 角色:AE 组产品 agent -- 对应 feature:`map-feed-001` - -## 背景 - -AD 组已经把七条 viral manual journey 整理成手测执行包;O/W 相关能力也已经要求真实手测结果写入 ignored local JSON,并由 `scripts/check-viral-journey-manual-evidence.mjs` 校验状态、环境、七条 journey、payload 约束和 `summary.overallStatus` 聚合。另一个通用能力 `scripts/create-manual-summary.mjs` 可以把本地 JSON 转成脱敏 Markdown summary,供评审阅读。 - -剩余风险在 JSON 与 Markdown summary 之间:手工跑完 journey 后,执行者可能先生成 summary,再继续修改 ignored JSON;也可能手工改 summary、复制旧 summary、引用别的分支 summary,或只看 Markdown 而不重新核对源 JSON。这样会让评审报告引用过期摘要,甚至把当前 JSON 中的 `blocked` / `failed` 误读成 Markdown 里的 `passed`。 - -本 brief 定义一个窄切口:只验证 ignored local viral journey JSON 与 ignored local Markdown summary 是否同源一致。JSON 是事实源,Markdown 只是脱敏摘要视图。 - -## 用户与评审场景 - -1. 手测执行者在 WeChat DevTools 或真机上完成七条 viral manual journey,把真实观察写入 `harness/manual-test-results.local-viral-journey*.json`。 -2. 执行者运行 viral manual evidence checker,确认 JSON 本身的 schema、branch/commit、journey 状态和 payload/evidence 字段合规。 -3. 执行者生成 `harness/manual-test-summary.local-viral-journey*.md`,给评审或报告引用。 -4. 在评审前,必须重新确认当前 summary 仍来自当前 JSON:`overallStatus`、每条 journey 的 `status`、每条 journey 的 `evidenceCount`、`branch`、`commit` 都一致。 -5. 如果 JSON 或 Markdown 任一方被后编辑,重新运行完整性检查后才能继续引用该 summary。 - -评审价值:评审可以引用 Markdown 的可读摘要,但不需要猜测它是否已经落后于 JSON;完整性通过只表示“这份 summary 正确转述了这份 local JSON”,不表示真实 journey 通过。 - -## 要防的误判 - -- JSON 仍是 `blocked`,summary 却保留旧的 `passed` 或“已通过”表述。 -- 某条 journey 在 JSON 中从 `passed` 改成 `failed`,summary 表格仍显示 `passed`。 -- JSON 新增或删除 evidence 后,summary 的 `evidenceCount` 没有更新,评审误以为证据数量完整。 -- summary 的 `branch` / `commit` 来自旧分支或旧提交,评审把旧结果引用到当前轮。 -- summary 缺失七条 required journey 中的一条,或 journey id 被改名、重复、合并,导致风险态检查被静默跳过。 -- 执行者把 example JSON、未 ignored 文件、普通 manual summary 或 map-list blocked summary 当成 viral journey summary 引用。 -- readiness、helper 成功、summary 生成成功被写成 DevTools UI passed、real-device passed 或 viral journey passed。 - -## 范围内 - -- 只读校验 ignored local viral journey result JSON 与 ignored local Markdown summary 的同源完整性。 -- 结果 JSON 必须先符合 viral manual evidence checker 的产品语义;完整性检查不重新定义 passed/failed/blocked 规则。 -- 校验 summary 的 Run 信息:`branch` 与 `commit` 必须等于源 JSON。 -- 校验 summary 的 Summary 信息:`overallStatus` 必须等于源 JSON 的 `summary.overallStatus`。 -- 校验 summary 的 Journeys 表:七条 required journey 必须逐条存在,`id`、`status`、`evidenceCount` 必须与源 JSON 同步。 -- 对每条 journey,`evidenceCount` 只比较数量,不要求 Markdown 泄露 raw evidence 内容。 -- 输出必须保持脱敏,不打印 raw evidence、raw payload、完整日志、本机绝对路径、token、cookie、账号或 CloudBase 私密信息。 - -## 非目标 - -- 不执行 WeChat DevTools UI、真机、系统分享面板、朋友圈、payload inspect 或 CloudBase readback。 -- 不声明 DevTools UI passed、real-device passed、viral journey passed、timeline passed 或 CloudBase passed。 -- 不把 JSON checker 通过、summary integrity 通过、readiness 通过或 preflight 通过升级为产品 journey 通过。 -- 不生成、不修改、不格式化、不覆盖 Markdown summary。 -- 不生成、不修改、不覆盖 ignored local JSON。 -- 不新增业务 UI、分享策略、归因逻辑、评论/确认流程、风控策略或页面文案。 -- 不替代人工复核;尤其是 `passed` journey 仍需要评审查看真实 UI evidence 是否可信、脱敏且完整。 - -## 同源判定口径 - -JSON 是唯一事实源。Markdown summary 合格的最低条件: - -- 路径语义:JSON 和 Markdown 都位于 `harness/` 下,并且都是 git ignored local 文件;示例文件和可提交文件不能作为评审 summary pair。 -- JSON 前置:源 JSON 已通过 `scripts/check-viral-journey-manual-evidence.mjs `。 -- Run 同步:summary 中 `branch`、`commit` 与 JSON 顶层字段完全一致。 -- Overall 同步:summary 中 `overallStatus` 与 JSON `summary.overallStatus` 完全一致。 -- Journey 同步:summary 中七条 required journey 均存在且不重复,不能删除、合并、重命名。 -- 状态同步:每条 journey 的 summary `status` 与 JSON `journey.status` 完全一致。 -- 证据数量同步:每条 journey 的 summary `evidenceCount` 等于从 JSON `journey.evidence` 计算出的数量。 -- 保守输出:即使七条 journey 都是 `passed`,完整性检查也只能说 summary 与 JSON 同源;是否可被评审接受为真实 passed evidence,仍由 manual evidence 与人工复核决定。 - -## 建议 CLI 形态 - -主命令建议为只读、显式 pair 校验: - -```text -node --no-warnings scripts/check-viral-journey-summary-integrity.mjs -``` - -示例: - -```text -node --no-warnings scripts/check-viral-journey-summary-integrity.mjs \ - harness/manual-test-results.local-viral-journey.json \ - harness/manual-test-summary.local-viral-journey.md -``` - -可选 preflight 建议: - -```text -node --no-warnings scripts/check-viral-journey-summary-integrity-preflight.mjs -``` - -preflight 只扫描已存在的 ignored local viral journey summary,并按同一 suffix 查找对应 ignored local result JSON。没有 local summary 时应输出“nothing checked”,并明确这不是 UI passed evidence。发现 summary 存在但缺少匹配 JSON、pair 未 ignored、JSON checker 失败或字段不同步时,应失败。 - -## Readiness 边界 - -`scripts/check-devtools-readiness.mjs` 未来可以接入 summary integrity 的 static/local preflight,但职责必须非常窄: - -- 只运行静态 guard 或 ignored local file preflight。 -- 只读取已存在的 ignored local JSON/summary pair。 -- 不运行 summary generator。 -- 不创建、不修改、不覆盖 summary。 -- 不创建、不修改、不覆盖 manual result JSON。 -- 不自动打开、退出、preview、upload、kill、清缓存或修改 WeChat DevTools settings。 -- readiness 输出必须继续写明:该检查只验证 local JSON/summary 同源,不证明 DevTools UI、真机或 viral journey passed。 - -如果当前没有 local summary,readiness 不应制造 summary 来“补齐检查”;应保持 skipped/no-files 语义。 - -## 状态词示例 - -底层 viral manual result 仍只使用现有 journey 状态语义: - -- `passed`:真实手测已执行,actual/evidence/payload 或限制说明满足 checker 与人工复核要求。 -- `failed`:真实手测已执行,产品行为不符合预期,并有 actual 与 followUp。 -- `blocked`:真实手测受 DevTools、真机、系统分享、朋友圈、payload、CloudBase、登录态或 fixture 阻塞。 - -summary integrity 自己的输出状态应避免与 journey passed 混淆,建议使用: - -- `integrity_passed`:JSON 与 Markdown summary 在 branch/commit、overallStatus、journey status 和 evidenceCount 上一致。 -- `integrity_skipped_no_local_summary`:未发现 ignored local viral journey summary;没有检查任何 pair,也没有 UI passed 结论。 -- `integrity_failed_status_mismatch`:JSON 与 summary 的 `overallStatus` 或某条 journey `status` 不一致。 -- `integrity_failed_evidence_count_mismatch`:summary `evidenceCount` 与 JSON evidence 数量不一致。 -- `integrity_failed_branch_commit_mismatch`:summary 的 `branch` 或 `commit` 与 JSON 不一致。 -- `integrity_failed_missing_or_duplicate_journey`:summary 缺少 required journey 或 journey 重复。 -- `integrity_failed_unignored_or_wrong_file`:输入不是 ignored local viral journey pair,或误用了 example/其他 summary。 -- `integrity_blocked_json_invalid`:源 JSON 未通过 viral manual evidence checker,不能继续信任 summary。 - -成功示例文案: - -```text -integrity_passed: checked ignored local viral journey summary pair. -jsonOverallStatus=blocked summaryOverallStatus=blocked checkedJourneys=7 -notClaimed: no DevTools UI journey passed; no real-device journey passed; no viral journey passed -``` - -失败示例文案: - -```text -integrity_failed_status_mismatch: summary row receiver-comment-conversion status is passed, JSON status is blocked. -``` - -```text -integrity_failed_evidence_count_mismatch: summary row timeline-share-channel evidenceCount is 1, JSON evidence count is 0. -``` - -```text -integrity_failed_branch_commit_mismatch: summary commit does not match source JSON commit. -``` - -## 评审报告引用口径 - -允许写: - -- “Viral manual summary integrity passed for the ignored local JSON/Markdown pair.” -- “The Markdown summary matches the source JSON on branch/commit, overall status, seven journey statuses, and evidence counts.” -- “This check is a source-integrity guard only; UI evidence still requires manual review.” - -禁止写: - -- “summary integrity passed, so viral journey passed。” -- “readiness passed, so DevTools UI passed。” -- “summary shows passed, so real-device passed。” -- “blocked/failed 可以因为 summary 生成成功而按 passed 计。” - -## 验收标准 - -- brief 明确用户/评审场景、要防的误判和非目标。 -- brief 明确不得声称 DevTools UI、real-device 或 viral journey passed。 -- brief 明确本轮只验证 ignored local JSON 与 Markdown summary 同源,不验证真实 UI 通过。 -- brief 明确未来 CLI 可采用 `scripts/check-viral-journey-summary-integrity.mjs [result-json] [summary-md]`,并可增加只读 preflight 扫描。 -- brief 明确 readiness 只能跑静态或本地 ignored 文件检查,不能生成或修改 summary。 -- brief 给出成功/失败状态词示例,且不写实现方案。 diff --git a/harness/viral-publish-design-checklist.md b/harness/viral-publish-design-checklist.md deleted file mode 100644 index d9395e7..0000000 --- a/harness/viral-publish-design-checklist.md +++ /dev/null @@ -1,39 +0,0 @@ -# 发布后扩散闭环设计 / QA Checklist - -日期:2026-06-16 -适用范围:详情页 `from=publish` 发布成功上下文 - -## 设计取舍 - -- 信息层级:保留详情 hero 作为主内容,扩散计划放在 hero 后、信任判断前,避免盖住任务本身。 -- 结构:使用“先转给 / 想得到 / 稍后回访”三步计划,而不是长段解释,让发布者能直接行动。 -- 语气:使用“请附近的人确认、补线索、回看评论”等谨慎表达,避免“保证有效”“一定找回”等绝对承诺。 -- 控件:active/stale 任务显示一个主转发按钮;resolved/expired/hidden 不显示转发主按钮,改为返回地图。 -- 普通详情页:只有 `from=publish` 时才渲染扩散计划,其他来源不增加额外模块。 - -## 自动检查 - -- [ ] `node --no-warnings scripts/check-publish-spread.mjs` -- [ ] `node --check utils/publish-spread.js` -- [ ] `node --check pages/detail/detail.js` -- [ ] `node --check scripts/check-devtools-readiness.mjs` -- [ ] `node --no-warnings scripts/check-devtools-readiness.mjs` -- [ ] `node scripts/check-json.mjs` -- [ ] `node harness/check-harness.mjs` -- [ ] `git diff --check` -- [ ] `bash harness/init.sh` - -## 手测清单 - -- [ ] 从发布页成功发布 active 任务后跳转详情,看到扩散计划。 -- [ ] 扩散计划三步文案分别回答“转给谁”“想得到什么”“稍后做什么”。 -- [ ] 带图任务显示图片相关提示,且不遮挡详情图片。 -- [ ] 已有评论的任务提示先回看评论。 -- [ ] `resolved` 或 `expired` 任务即使带 `from=publish`,也不鼓励继续转发。 -- [ ] 从地图、列表、我的发布、活动动态进入同一详情页,不显示扩散计划。 -- [ ] 点击“转发扩散”能打开微信分享面板;分享卡片路径包含任务 `id`,不携带 `from=publish`。 -- [ ] 窄屏下三步计划和按钮不重叠、不截断关键动词。 - -## 未验证不得声称通过 - -自动检查只覆盖文案模型、路径拼接和基础语法。open-type share、微信分享面板、真实发布跳转、窄屏布局和图片渲染仍需要 WeChat DevTools 或真机验证;未执行时必须记录为未验证风险。 diff --git a/harness/viral-publish-product-brief.md b/harness/viral-publish-product-brief.md deleted file mode 100644 index f69339f..0000000 --- a/harness/viral-publish-product-brief.md +++ /dev/null @@ -1,35 +0,0 @@ -# 发布后扩散闭环产品 Brief - -日期:2026-06-16 -分支:`codex/iter-viral-publish` -目标 worktree:`/tmp/street-tasks-iter-worktrees/viral-publish` - -## 产品假设 - -发布者刚创建任务时动机最强。如果详情页在 `from=publish` 场景下给出清晰的扩散对象、想获得的确认/线索和稍后回访动作,发布者更可能把任务转发到附近群、朋友或现场相关人,从而带来更多确认、评论或关闭动作。 - -## 最小迭代范围 - -- 只强化发布成功后的详情页上下文:`/pages/detail/detail?id=...&from=publish`。 -- 普通详情页不展示扩散计划,仍保持任务信息、信任判断、评论和动作。 -- 文案按 `category`、`intent`、图片、评论数和状态生成,强调“请附近的人确认或补线索”,不承诺一定找回或一定有效。 -- `resolved`、`expired`、`hidden` 状态不鼓励继续扩散,改为提示回看历史线索或重新发布更准确的新任务。 -- 分享 path 保留非发布上下文参数,但移除 `from=publish`,避免被转发者看到发布者专属扩散卡。 - -## 非目标 - -- 不做增长埋点、邀请奖励、群裂变海报或服务端归因。 -- 不改变发布表单、帖子存储、评论/信任动作规则。 -- 不把扩散计划展示给从地图、列表、我的发布、活动动态或分享普通进入的用户。 - -## 成功信号 - -- 自动检查能覆盖四类分类文案、失物招领 `lost/found` 差异、图片提示、已有评论提示、关闭/过期不扩散和分享 path 参数保留。 -- WeChat DevTools/真机中,发布成功后详情页出现扩散计划,普通详情页没有新增负担。 -- 手测时发布者能在 3 秒内看懂:转给谁、希望对方做什么、稍后回来做什么。 - -## 风险 - -- 尚未接入真实埋点,无法自动判断转发率是否提升。 -- open-type share 的系统面板和分享卡片仍需 WeChat DevTools 或真机观察。 -- 旧任务被手动带 `from=publish` 打开时会展示非扩散版关闭提示,避免误导继续转发,但仍需视觉确认。 diff --git a/harness/viral-real-evidence-recovery-checklist.md b/harness/viral-real-evidence-recovery-checklist.md deleted file mode 100644 index b2bfe7d..0000000 --- a/harness/viral-real-evidence-recovery-checklist.md +++ /dev/null @@ -1,216 +0,0 @@ -# Z 轮真实证据恢复 QA Checklist - -日期:2026-06-17 - -## 目标与状态口径 - -- [ ] 本清单用于把 viral 分享证据分成可判分层级,避免把自动 readiness、blocked draft、未运行手测或真实 UI 通过混在一起。 -- [ ] 本轮只定义设计 / QA 证据要求,不新增或修改产品代码、脚本、云函数或真实结果文件。 -- [ ] `passed` 只能用于真实执行过目标层级、actual 非占位、关键期望满足、证据可定位且无隐私泄漏的结果。 -- [ ] `blocked` 只能用于已经尝试进入该层级,但被环境、账号、数据、权限、CloudBase 或设备阻断;必须写 `blocker`、`followUp` 和环境信息。 -- [ ] `not_run` 用于没有尝试执行;不得伪装成 `blocked`,也不得写成“看起来可通过”。 -- [ ] 如果真实运行后观察到产品不符合期望,应记为 `failed`;不要把产品缺陷写成环境 blocked。 - -## 全局 Evidence Header - -每一层级记录 passed、blocked、failed 或 not_run 前,都先补齐同一组 header,方便评测 agent 判断证据是否属于当前代码和环境。 - -- [ ] `branch`:当前分支名。 -- [ ] `commit`:当前 HEAD 的 full SHA 或短 SHA。 -- [ ] `testedAt`:测试时间,含时区。 -- [ ] `tester`:执行人或 agent 名称。 -- [ ] `repoPath`:本地仓库路径。 -- [ ] `projectPath`:WeChat DevTools 打开的项目路径。 -- [ ] `devtoolsVersion` 和 `baseLibraryVersion`:无法读取时写清楚原因。 -- [ ] `device`:模拟器机型 / 真机型号 / 系统版本 / 微信版本。 -- [ ] `network`:在线、弱网、断网、代理、公司网络等。 -- [ ] `cloudbaseEnv`:环境 id 或脱敏别名、云函数版本、集合状态;不能记录密钥。 -- [ ] `dataFixtures`:测试 post id、状态、stale/report/closed 字段、是否低风险;不要记录精确经纬度或私人内容。 - -## QA 分层 - -### 1. 自动 Readiness - -用途:确认静态模型、JSON、harness、manual evidence schema 和 no-side-effect 准备包仍可运行。它是前置检查,不是 UI 通过证据。 - -Passed evidence 必须记录: - -- [ ] 命令和退出码:至少包含 `bash harness/init.sh`、`npm run check` 或 `npm run check:readiness` 的真实输出摘要。 -- [ ] `scripts/check-devtools-readiness.mjs` 是否仍报告 service port / smoke blocker;如果报告 blocker,readiness 仍可通过,但 UI 层不能 passed。 -- [ ] 本次 readiness 使用的 branch、commit、Node 版本和运行时间。 -- [ ] 如果扫描到 ignored local manual evidence,记录文件路径、overallStatus 和 checker 输出摘要。 - -Blocked 时必须记录: - -- [ ] `blocker`:命令不可运行、依赖不可安装、Node 不可用、harness 文件缺失或 checker 失败的具体错误。 -- [ ] `followUp`:修复哪条命令、哪个文件或哪个环境依赖。 -- [ ] 环境信息:Node/npm 版本、shell、repoPath、退出码、stderr 关键行。 -- [ ] 不得宣称:WeChat DevTools 已打开、系统分享菜单可见、真实 payload 正确、CloudBase attribution 已写入、真机或窄屏已通过。 - -### 2. DevTools Service Port - -用途:确认 WeChat DevTools 服务端口可被 CLI / 探针访问,当前默认关注 `9420`。 - -Passed evidence 必须记录: - -- [ ] `npm run inspect:devtools-port` 或等价只读端口取证输出,明确 `9420` 正在监听。 -- [ ] `lsof` / 探针摘要:监听进程名、PID、端口、是否像 WeChat DevTools。 -- [ ] DevTools 设置截图或日志摘要:服务端口已开启,项目路径是本仓库。 -- [ ] 如果端口不是 `9420`,记录实际端口、为什么改端口、后续命令如何传参。 - -Blocked 时必须记录: - -- [ ] `blocker`:未监听、端口被其他进程占用、DevTools 未安装 / 未启动、服务端口开关不可用、权限禁止探测等。 -- [ ] `followUp`:谁需要打开 DevTools、开启服务端口、重启工具、换端口或提供机器访问。 -- [ ] 环境信息:DevTools 安装路径、进程摘要、端口探针输出、macOS 用户上下文、项目路径。 -- [ ] 不得宣称:CLI open 成功、模拟器编译成功、系统分享面板可打开、朋友圈菜单可见、单页模式可读。 - -### 3. DevTools Smoke / Open - -用途:确认 DevTools 能打开项目或被 smoke 探针访问,并能到达小程序首屏。它仍不等于 viral journey passed。 - -Passed evidence 必须记录: - -- [ ] `npm run check:devtools-smoke` 或 `node scripts/check-devtools-smoke-access.mjs --strict` 的退出码和关键输出。 -- [ ] DevTools UI 截图或录屏:项目已打开、路径是本仓库、模拟器不白屏、控制台首个错误为空或已说明为非阻塞。 -- [ ] 编译 / 打开动作摘要:打开的页面路径、是否清缓存、是否重新编译、是否有 warnings/errors。 -- [ ] 如果使用 `npm run prepare:viral-journey-run`,记录其中 port/smoke 状态,但不要把 run package 当成 UI evidence。 - -Blocked 时必须记录: - -- [ ] `blocker`:service port 不通、CLI timeout、DevTools 打不开项目、模拟器白屏、编译卡死、账号未登录、AppID / project config 阻断。 -- [ ] `followUp`:重开 DevTools、开启端口、清缓存、重新导入项目、登录开发者账号、提供控制台第一条红色错误。 -- [ ] 环境信息:DevTools 版本、base library、appid 使用 `touristappid` 还是本地私有 AppID、打开页面、错误日志摘要。 -- [ ] 不得宣称:系统分享 payload 已 inspect、朋友圈菜单存在、receiver 转化链路已通过、CloudBase 事件已落库、真机表现已通过。 - -### 4. 真实 Viral Journeys - -用途:运行真实用户传播链路,包括 friend share、timeline share、receiver confirm/comment 转化、二跳接力、风险态不鼓励扩散和单页模式。 - -Passed evidence 必须记录: - -- [ ] 所有 required journeys 各自有 `status=passed`、真实 `actual`、独立 evidence 引用和对应 post fixture。 -- [ ] 系统分享菜单截图 / 录屏:低风险 active 任务能看到朋友分享;timeline journey 还要看到朋友圈入口。 -- [ ] payload evidence:可 inspect 时记录 `title`、`path` 或 `query`、`imageUrl` / 图片来源、分享渠道和关键参数;不可 inspect 时必须写明工具限制、替代证据和未验证字段。 -- [ ] 落地页截图 / 录屏:`from=share`、`source=timeline`、`source=receiver` 等入口的首屏说明正确,普通入口不被 viral 文案覆盖。 -- [ ] 转化截图 / 录屏:接收者完成 confirm/comment 后,数量或评论刷新正确,二跳 prompt 优先级正确,普通 share panel 不同时竞争。 -- [ ] 风险态 evidence:weak stale/report、stale、resolved、expired、hidden、unknown 任务不出现鼓励扩散菜单或文案。 - -Blocked 时必须记录: - -- [ ] `blocker`:无法打开系统菜单、无法触发真实分享、账号无朋友圈能力、缺 fixture、单页模式无法启动、payload inspector 不可用、接收端无法进入。 -- [ ] `followUp`:需要哪个账号 / 设备 / fixture / DevTools 设置 / CloudBase 数据,以及下一次从哪条 journey 继续。 -- [ ] 环境信息:设备、微信版本、DevTools 版本、页面路径、fixture post id、入口 query、截图或控制台摘要。 -- [ ] 不得宣称:分享 payload 正确、朋友圈渠道通过、单页模式首屏可读、二跳链路完成、风险态 gating 生效。 - -### 5. CloudBase Attribution - -用途:确认 viral attribution 事件在真实链路中 best-effort 触发,并在 CloudBase 或明确 fallback 中可审计。 - -Passed evidence 必须记录: - -- [ ] 真实链路动作:landing、loaded / blocked、confirm_success、comment_success、relay_intent、relay_success 中实际跑过哪些。 -- [ ] 事件 payload 摘要:event name、post id、entry source、share id、parent share id、share depth、share channel、receiver action、conversion action、status / risk 字段。 -- [ ] CloudBase readback:`viral_attribution_events` 集合中脱敏事件数量、时间范围、字段摘要、`user_id_hash` 存在且没有 raw openid。 -- [ ] 降级 evidence:若 CloudBase 不可用但用户链路继续,记录本地 fallback / 控制台摘要、失败原因和未落库风险;这只能证明不阻断体验,不能证明 CloudBase 写入 passed。 -- [ ] 云端环境:云函数版本、集合权限、测试账号角色、网络状态。 - -Blocked 时必须记录: - -- [ ] `blocker`:云函数未部署、集合不存在、权限不足、网络失败、无法查看云端数据、服务端日志不可用、真实分享链路未跑到归因点。 -- [ ] `followUp`:部署哪个云函数、创建哪个集合、用哪个账号复跑、如何读取脱敏事件。 -- [ ] 环境信息:CloudBase env 脱敏名、函数名、集合名、调用时间、错误码 / 错误摘要。 -- [ ] 不得宣称:归因已落库、链路可统计、CloudBase 通过、用户 id 已安全脱敏,除非有 readback 或明确字段证据。 - -### 6. 真机 / 窄屏 - -用途:确认真实设备和小屏宽度下,地图、详情、接收页、转化提示、timeline 单页模式和底部操作不遮挡、不溢出。 - -Passed evidence 必须记录: - -- [ ] 真机或窄屏模拟器截图 / 录屏:至少覆盖 320-375px 级别窄屏、常见 iPhone 宽度、一个 Android 或微信真机场景。 -- [ ] 首屏 evidence:标题、状态、地点 / 距离摘要、receiver guide、系统导航栏、tabBar / 单页模式无 tabBar 都可读。 -- [ ] 交互 evidence:打开评论、完成 confirm、完成 comment、触发二跳 prompt、打开系统菜单、滚动到底部。 -- [ ] 布局检查:不遮挡地图控件、评论入口、分享按钮、relayChannels、shareReason、targetRows、底部安全区。 -- [ ] 性能 / 稳定性摘要:首屏没有无限 loading、白屏、明显卡死或遮挡式授权循环。 - -Blocked 时必须记录: - -- [ ] `blocker`:没有真机、无法登录微信、无法扫码预览、设备不支持朋友圈、窄屏模拟器不可用、网络 / 定位权限阻断。 -- [ ] `followUp`:需要哪台设备、哪个微信账号、哪个二维码 / 预览包、哪组 fixture、谁来补测。 -- [ ] 环境信息:机型、系统、微信版本、屏幕宽度、像素比、网络、定位授权状态。 -- [ ] 不得宣称:移动端视觉通过、真机分享通过、单页模式可读、窄屏转化 CTA 可用。 - -## 真实分享 Payload 检查点 - -每个可 inspect 的真实分享 payload 都要记录原始字段摘要和规范化字段摘要;不可 inspect 时必须写清楚“哪个工具不支持、用什么替代、哪些字段仍未验证”。 - -- [ ] `from`:应区分普通入口和分享入口;真实接收链路应包含 `from=share`。 -- [ ] `source`:至少区分 `timeline`、`receiver`、`comment`、`confirm`;未知 source 不应触发鼓励扩散语境。 -- [ ] `share_id`:当前分享或接力分享的脱敏 id;不能是用户 openid、unionid 或手机号。 -- [ ] `parent_share_id`:二跳或多跳接力时指向上一级分享;首跳为空或显式说明。 -- [ ] `share_depth`:首跳 / 二跳 / 多跳深度,必须是合理数字,不得无限增长或缺失。 -- [ ] `share_channel`:规范化事件字段;timeline payload query 如使用 `shareChannel=timeline`,需要在 evidence 中说明映射到 `share_channel=timeline`。 -- [ ] `receiverAction`:二跳接收者上一次完成 `confirm` 还是 `comment`;仅在相关链路出现,风险态不应伪造。 -- [ ] `conversionAction`:归因事件里的转化动作;如实现字段为 `conversion_action`,evidence 应同时写出原字段名和值。 -- [ ] `path` / `query`:朋友分享通常检查 `path`,朋友圈通常检查 `query` 且不得声称有自定义 timeline path。 -- [ ] `title` / `imageUrl`:文案谨慎、不诱导、不泄露隐私;风险态标题不能鼓励扩散。 - -## 隐私检查点 - -证据、payload、日志、截图和 CloudBase readback 都要先脱敏。出现以下内容时,评测 agent 应把相关层级判为 failed 或要求重采。 - -- [ ] 不能出现评论正文、用户输入全文、图片原始 URL 或完整分享文案全文。 -- [ ] 不能出现联系人姓名、好友关系、群名、微信群 id 或私聊上下文。 -- [ ] 不能出现 raw openid、unionid、手机号、微信号、身份证号、邮箱、门牌号或详细住址。 -- [ ] 不能出现精确经纬度;如果需要位置,只能用 post id、地点摘要、距离段或脱敏区域。 -- [ ] 不能出现 cookie、token、Authorization、session、refresh token、access token、client secret、AppSecret 或私有 AppID。 -- [ ] 不能把用户头像、昵称、云环境 id、设备序列号等可识别信息直接提交;必要时打码或用脱敏别名。 - -## Blocked 记录模板 - -每个 blocked 层级至少包含以下字段;缺字段时只能算记录不完整,不能作为有效阻塞证据。 - -```json -{ - "layer": "devtools-service-port", - "status": "blocked", - "blocker": "Port 9420 is not listening after DevTools was opened.", - "followUp": "Enable service port in WeChat DevTools, reopen this project, then rerun npm run inspect:devtools-port and npm run check:devtools-smoke.", - "environment": { - "repoPath": "/private/tmp/street-tasks-iter-worktrees/viral-real-evidence-recovery", - "projectPath": "/private/tmp/street-tasks-iter-worktrees/viral-real-evidence-recovery", - "devtoolsVersion": "unknown", - "device": "not reached", - "network": "not reached", - "cloudbaseEnv": "not reached" - }, - "notClaimed": [ - "DevTools smoke/open passed", - "system share payload inspected", - "timeline menu verified", - "CloudBase attribution written", - "real-device or narrow-screen conversion passed" - ] -} -``` - -## 评测 Agent 判分规则 - -- [ ] 先检查全局 header 是否匹配当前 branch / commit;旧证据或不同分支证据不能直接给当前轮加分。 -- [ ] 自动 readiness 只能给“结构准备好”分,不给 DevTools、分享、CloudBase 或真机通过分。 -- [ ] Service port passed 只说明可以尝试 DevTools smoke;不能替代 smoke/open,也不能替代 viral journeys。 -- [ ] Smoke/open passed 只说明项目能被 DevTools 打开和首屏可见;不能替代系统分享菜单、payload、CloudBase 或真机证据。 -- [ ] Viral journeys 是用户侧裂变判分主项;所有 required journeys 没有真实 passed evidence 时,不能评为最终用户链路通过。 -- [ ] CloudBase attribution 必须有真实 readback 或明确降级证据;只有客户端静态检查或“应当写入”不得给 CloudBase passed。 -- [ ] 真机 / 窄屏必须有截图或录屏;桌面宽屏 DevTools 截图不能替代窄屏和真机结论。 -- [ ] 任一层级 `not_run` 时,只能写未运行;不得用前置层通过、模板存在或 blocked draft 填补。 -- [ ] 任一层级 `blocked` 时,只能写真实阻塞和下一步;不得写“功能通过但缺证据”。 -- [ ] 任一层级出现隐私泄漏、占位 actual、不可定位 evidence、无法复现的口头描述,应扣回该层级 passed。 -- [ ] 若 service port 9420 仍未监听,评测结论应停在“真实 DevTools / 真机证据 blocked”,不能给 100/100 的真实通过结论。 - -## 收尾验证 - -- [ ] 修改本清单后运行 `node harness/check-harness.mjs`。 -- [ ] 修改本清单后运行 `git diff --check`。 -- [ ] 不提交真实截图、录屏、payload 或 local manual evidence;它们应保持 ignored/local。 diff --git a/harness/viral-real-evidence-recovery-product-brief.md b/harness/viral-real-evidence-recovery-product-brief.md deleted file mode 100644 index b380a0d..0000000 --- a/harness/viral-real-evidence-recovery-product-brief.md +++ /dev/null @@ -1,154 +0,0 @@ -# Z 组真实 Evidence 恢复产品 Brief - -日期:2026-06-17 - -分支基线:`ef8f0a5 feat: add viral attribution events` - -工作目录:`/tmp/street-tasks-iter-worktrees/viral-real-evidence-recovery` - -角色:Z 组产品 agent - -## Z 轮产品目标 - -拿到一份可复核、可区分 `passed` 与 `blocked` 的真实 WeChat DevTools 或真机 evidence package,让评测 agent 相信当前 viral 大版本不只是静态检查通过,而是系统分享、朋友圈、单页落地、二跳归因、CloudBase 事件和隐私边界都在真实环境中被验证或被准确阻塞。 - -## 背景判断 - -Y 轮已经把产品链路补到接近完整:`viral_attribution_events` 能记录 `landing`、`load`、`block`、`confirm`、`comment`、`relay`,分享链路带 `share_id`、`parent_share_id`、`share_depth`,并且自动检查覆盖字段白名单、二跳 payload 和 CloudBase best-effort 上报。 - -当前 99 分天花板不在继续加文案、组件或 CTA,而在真实 evidence 缺失。Z 轮应把研发和 QA 的注意力从“再做一个传播增强点”切到“恢复 DevTools/真机入口并产出可评测证据”。 - -## 证据缺口优先级 - -1. P0:WeChat DevTools service port 9420 仍是 blocker。当前 readiness 报告 `connect_refused` / smoke `blocked`,所以没有可信的 DevTools open、compile、system share 或 payload evidence。 -2. P0:七条 viral journey 没有真实 `passed` 本地结果文件。`harness/viral-journey-manual-results.example.json` 只是 `not_run` 模板,不能引用为验收证据。 -3. P0:系统分享 payload 未真实观察。需要证明好友分享 path、朋友圈 query、二跳 path/query 在微信系统能力里保留 `id`、`from=share`、`source`、`shareChannel`、`share_id`、`parent_share_id`、`share_depth`。 -4. P0:朋友圈菜单和单页模式首屏未真实通过。需要真实菜单中朋友圈入口、timeline 卡片信息、从朋友圈打开的单页或等价首屏可读证据。 -5. P0:CloudBase `viral_attribution_events` 未真实写入。自动检查证明代码会清洗和 best-effort 上报,但评测需要看到真实或明确 blocked 的云端样本。 -6. P1:窄屏和真机转化未验证。`from=share`/`source=timeline` 引导、receiver action strip、confirm/comment 后二跳提示、风险态 no-share CTA 都需要至少一个窄屏 DevTools 或真机观察。 -7. P1:隐私字段检查缺少真实证据包级结论。需要确认本地 evidence、CloudBase 样本和可提交摘要里没有评论正文、联系人/群聊、精确经纬度、原始 openid、token、完整 `cloud://` 或 CloudBase env id。 - -## 本轮不做什么 - -- 不再增加用户侧传播文案、卡片、CTA、奖励、海报、联系人推荐或渠道选择,除非该改动直接服务于真实 evidence 采集。 -- 不新增归因字段,除非当前 evidence 采集无法判断必要的 passed/blocked 状态。 -- 不把 readiness、static gate、dry-run、blocked capture、schema checker passed 写成 UI passed。 -- 不提交截图、录屏、真实 CloudBase 标识、用户身份、原始日志或 ignored local result 文件。 -- 不用直接路由替代系统分享作为唯一 passed 证据。直接路由可以辅助定位问题,但系统分享 payload 仍需真实观察或写为 blocked。 - -## 可验收 Evidence Package - -Z 轮的交付不一定必须全 passed,但必须诚实。一个可被评测接受的 evidence package 至少包含以下六类材料。 - -### 1. Service-port 状态 - -记录 `npm run inspect:devtools-port` 或等价命令的摘要: - -- 检查时间、worktree、branch、commit。 -- 端口号,默认 `9420`。 -- 是否存在 `ide-http-port` 声明。 -- 是否有 listener。 -- IPv4/IPv6 连接结果。 -- 诊断结论:`ready`、`blocked` 或 `unknown`。 - -如果仍是 `connect_refused` 或 no listener,状态只能写 `blocked`,不能继续写 DevTools smoke passed。 - -### 2. DevTools smoke/open 结果 - -记录 `npm run check:devtools-smoke`、`npm run prepare:viral-journey-run` 或真实 UI 操作结果: - -- DevTools 是否打开了当前 worktree,而不是其他项目。 -- 小程序是否编译成功。 -- 目标详情页能否打开。 -- 当前环境是 DevTools 模拟器、真机 iOS、真机 Android,或 blocked。 -- 如果 smoke 脚本失败,保留最小错误摘要和下一步,不贴完整敏感日志。 - -open 或 smoke 只是入口 evidence。它不自动证明七条 viral journey passed。 - -### 3. 真实或阻塞的 viral journey 文件 - -使用 ignored local 文件,例如 `harness/manual-test-results.local-viral-journey.json`,并通过: - -```bash -node --no-warnings scripts/check-viral-journey-manual-evidence.mjs harness/manual-test-results.local-viral-journey.json -``` - -七条 journey 必须完整且唯一: - -- `first-hop-share-entry` -- `receiver-confirm-conversion` -- `receiver-comment-conversion` -- `second-hop-receiver-source` -- `ordinary-and-risk-entries` -- `timeline-share-channel` -- `timeline-risk-gating` - -`passed` 必须有真实 UI actual、evidence、设备/环境、post id 或脱敏 fixture、必要 payload。`blocked` 必须有 blocker 和 followUp。`failed` 必须来自已进入真实 UI 后观察到的产品偏差。 - -### 4. Share payload 字段 - -对好友分享、朋友圈分享、接收者 confirm/comment 后二跳,分别记录可 inspect 的 payload 或无法 inspect 的具体原因。 - -低风险 passed evidence 至少要覆盖: - -- 好友分享 path:`id`、`from=share`、`source`、`share_id`。 -- 接收者二跳 path:`source=receiver`、`receiverAction=confirm|comment`、`share_id`、`parent_share_id`、`share_depth=2|2_plus`。 -- 朋友圈 query:`id`、`from=share`、`source=timeline`、`shareChannel=timeline`、`share_id`。 -- 朋友圈二跳 query:`parent_share_id` 和递增后的 `share_depth`。 - -如果微信系统环境不暴露 payload,只能把对应 payload 检查写成 `blocked` 或在 journey 内写明无法检查的具体系统限制,不能写 payload passed。 - -### 5. CloudBase attribution event 样本 - -真实 CloudBase 样本需要来自 `viral_attribution_events` 集合或云函数日志的脱敏摘要,至少覆盖: - -- `share_detail_landing` -- `share_detail_loaded` 或 `share_detail_blocked` -- `share_confirm_success` 或 `share_comment_success` -- `share_relay_success` - -每条样本只记录白名单字段摘要,例如 `event_type`、`attribution_session_id`、`post_id`、`post_category`、`post_status`、`from`、`entry_source`、`share_id`、`parent_share_id`、`share_depth`、`action_result`、`blocked_reason`、`is_publisher`、`user_id_hash`、`distance_bucket`、`app_version`。 - -如果 CloudBase 未部署、集合不存在、权限不通或当前只能本地 fallback,写 `blocked` 或 `partial`,并说明本地 storage 是否记录到 `viral_attribution_events`。不要把本地 fallback 等同于云端 shared attribution passed。 - -### 6. 隐私字段检查 - -证据包必须包含一条隐私检查结论: - -- CloudBase 样本不含原始 openid、unionid、手机号、微信号、联系人、群聊、评论正文、图片内容、精确经纬度、详细地址、token、cookie、AppSecret。 -- 本地 result JSON 和可提交摘要不含真实 CloudBase env id、完整 `cloud://` file id、request id、原始截图路径或完整本机私有路径。 -- 若截图或录屏用于复核,只在本地或受控位置留存,仓库只写脱敏观察摘要。 - -## 端口仍阻塞时的写法 - -如果 9420 仍然阻塞,Z 轮仍可产出合格 blocked evidence,但写法必须克制: - -- `summary.overallStatus` 写 `blocked`。 -- 七条 viral journey 全部写 `status: "blocked"`,不能混入未执行的 `passed`。 -- `actual` 写真实未发生的事,例如“DevTools service port 9420 connection refused,未打开目标页面,未触发系统分享面板,未检查 payload,未写入 CloudBase 样本”。 -- `blocker` 写可复核事实,例如“存在 `--ide-http-port 9420` 声明但无 listener”或“smoke access failed with connect_refused”。 -- `evidence` 放脱敏命令摘要,不放 example 文案或虚构截图。 -- `followUp` 写恢复条件,例如“启用 DevTools Service Port 或换机后重跑 `npm run inspect:devtools-port`、`npm run check:devtools-smoke`、`npm run prepare:viral-journey-run`,再执行七条真实 journey”。 -- 明确声明:blocked evidence 只证明当前环境无法验收,不证明产品 passed,也不证明产品 failed。 - -推荐 blocked 摘要句式: - -> 当前 Z 轮 evidence 状态为 blocked:`9420` service port 无 listener,DevTools smoke 未进入目标项目,所以七条 viral journey、系统分享 payload、朋友圈菜单、单页模式首屏和 CloudBase attribution event 样本均未产生真实 passed evidence。下一步是恢复 DevTools service port 或改用真机后重跑完整 journey。 - -## 给开发 Agent 的最小实现建议 - -1. 先不要改产品代码。运行 `npm run inspect:devtools-port`、`npm run check:devtools-smoke`、`npm run prepare:viral-journey-run`,判断当前是 `ready` 还是 `blocked`。 -2. 如果端口 blocked,运行或指导使用 `npm run inspect:devtools-recovery` 查看 dry-run,再在用户明确允许的环境下恢复 DevTools Service Port。恢复前后都保留脱敏摘要。 -3. 如果短期无法恢复,使用 `npm run capture:viral-blocked-evidence` 生成 ignored local blocked JSON,并复跑 manual evidence checker。不要提交该 local JSON。 -4. 如果 DevTools 或真机 ready,按七条 journey 一次性跑通,重点采集系统菜单、share path/query、timeline 单页首屏、confirm/comment 二跳、风险态 no-shareTimeline 和窄屏布局。 -5. 为 CloudBase 准备最小测试数据:一个低风险 active post,一个弱 stale/report post,一个 closed/risk fixture,并确认 `posts` 云函数和 `viral_attribution_events` 集合可用。若不可用,写 blocked,不临时绕过。 -6. 对每条 passed journey 同步记录归因事件样本或无法读取样本的原因。事件读取只做脱敏摘要,不导出完整数据库截图或原始日志。 -7. 收尾时运行 `node --no-warnings scripts/check-viral-journey-manual-evidence.mjs `、`node --no-warnings scripts/check-devtools-readiness.mjs`、`node harness/check-harness.mjs` 和 `git diff --check`。检查通过只说明证据结构和基础状态可复核,最终 UI passed 仍以真实 journey 状态为准。 - -## Z 轮成功标准 - -- 本 brief 存在于 `harness/viral-real-evidence-recovery-product-brief.md`。 -- 后续 agent 能按本 brief 判断当前应恢复 DevTools/真机、采集 passed evidence,还是诚实写 blocked evidence。 -- evidence package 覆盖 service port、DevTools smoke/open、七条 viral journey、share payload、CloudBase attribution event 样本和隐私字段检查。 -- 没有任何静态检查、dry-run、blocked capture 或 schema checker 被表述为真实 UI passed。 -- 没有继续新增与真实 evidence 无关的传播文案或 CTA。 diff --git a/harness/viral-receiver-action-design-checklist.md b/harness/viral-receiver-action-design-checklist.md deleted file mode 100644 index 65661cc..0000000 --- a/harness/viral-receiver-action-design-checklist.md +++ /dev/null @@ -1,56 +0,0 @@ -# 分享接收者第一步行动设计 / QA Checklist - -日期:2026-06-16 - -## 触发条件 - -- [ ] 普通详情入口不显示 action strip -- [ ] `from=share` 且 active、无举报、无过时反馈任务显示 action strip -- [ ] hidden / resolved / expired / stale / 有举报或有过时反馈任务不显示鼓励性 action strip - -## 行动设计 - -- [ ] 确认按钮文案是“我在附近,确认一下” -- [ ] 线索按钮文案是“补一条线索” -- [ ] 确认按钮绑定现有 `react`,并带 `data-action="confirm"` -- [ ] 线索按钮绑定现有 `openCommentDialog` -- [ ] 两个按钮都不使用 `open-type="share"` - -## 互斥与后续链路 - -- [ ] 普通分享面板不与 receiver guide / action strip / actionRelay / commentRelay / receiverConversion 同屏竞争 -- [ ] from=share 完成 confirm 后优先显示 receiverConversionPrompt,而不是 actionRelayPrompt -- [ ] from=share 完成 comment 后优先显示 receiverConversionPrompt,而不是 commentRelayPrompt -- [ ] 风险态只显示谨慎文案,不出现鼓励扩散或鼓励行动的按钮 - -## 视觉 QA - -- [ ] action strip 放在接收侧提示内部或紧邻其后,层级低于主任务内容但高于通用信任动作 -- [ ] 窄屏下两个按钮可换行,不挤压文案 -- [ ] disabled 或风险态不占用过多垂直空间 -- [ ] 视觉风格沿用详情页现有 panel、按钮和谨慎色系 - -## 验证 - -- [ ] `node --check utils/share-receiver-actions.js` -- [ ] `node --check pages/detail/detail.js` -- [ ] `node --check scripts/check-share-receiver-action.mjs` -- [ ] `node --check scripts/check-share-receiver.mjs` -- [ ] `node --check scripts/check-receiver-conversion.mjs` -- [ ] `node --check scripts/check-action-relay.mjs` -- [ ] `node --check scripts/check-comment-relay.mjs` -- [ ] `node --check scripts/check-viral-candidate.mjs` -- [ ] `node --check scripts/check-devtools-readiness.mjs` -- [ ] `node --no-warnings scripts/check-share-receiver-action.mjs` -- [ ] `node --no-warnings scripts/check-share-receiver.mjs` -- [ ] `node --no-warnings scripts/check-receiver-conversion.mjs` -- [ ] `node --no-warnings scripts/check-action-relay.mjs` -- [ ] `node --no-warnings scripts/check-comment-relay.mjs` -- [ ] `node --no-warnings scripts/check-viral-candidate.mjs` -- [ ] `node --no-warnings scripts/check-devtools-readiness.mjs` -- [ ] `node scripts/check-json.mjs` -- [ ] `node harness/check-harness.mjs` -- [ ] `git diff --check` -- [ ] `npm run check` -- [ ] `bash harness/init.sh` -- [ ] WeChat DevTools / 真机确认按钮位置、弹窗、confirm 后二跳和窄屏换行 diff --git a/harness/viral-receiver-action-product-brief.md b/harness/viral-receiver-action-product-brief.md deleted file mode 100644 index 16b8a0a..0000000 --- a/harness/viral-receiver-action-product-brief.md +++ /dev/null @@ -1,32 +0,0 @@ -# 分享接收者第一步行动产品 Brief - -日期:2026-06-16 - -## 目标 - -当用户从 `from=share` 打开任务详情时,在接收侧说明旁给一个轻量行动入口,让他不用翻到信任动作或评论区也能完成第一次有用动作。 - -## 产品假设 - -分享接收者的第一步通常不是“继续转发”,而是先确认自己是否在附近、是否知道线索。把“确认一下”和“补一条线索”放在接收侧提示里,能降低他完成 confirm/comment 的摩擦,并自然接上已有的 receiverConversionPrompt 二跳接力。 - -## 范围 - -- 只在 `entryQuery.from === 'share'` 时评估 -- 只在 active 且没有过时/举报信号的低风险任务上展示鼓励性 action strip -- action strip 只提供两个动作:确认有效、打开评论弹窗 -- confirm 复用现有 `react` 流程,comment 复用现有 `openCommentDialog` -- 风险态、关闭态、过期态不展示鼓励按钮,只保留谨慎文案 - -## 非目标 - -- 不新增页面、奖励、埋点、复杂归因或额外本地状态 -- 不改普通详情入口的分享提示 -- 不改变 commentRelay、actionRelay 或 receiverConversion 的既有优先级 -- 不把 stale、高举报、hidden、resolved、expired 包装成传播机会 - -## 风险 - -- 如果入口文案太强,会让高风险或过时任务看起来仍然值得扩散 -- 如果按钮和 `open-type="share"` 混在一起,可能触发错误分享行为 -- 如果 action strip 与普通分享面板同时出现,会让接收者不知道先确认还是先转发 diff --git a/harness/viral-receiver-action-source-design-checklist.md b/harness/viral-receiver-action-source-design-checklist.md deleted file mode 100644 index 199c940..0000000 --- a/harness/viral-receiver-action-source-design-checklist.md +++ /dev/null @@ -1,99 +0,0 @@ -# 接收者二跳 action 来源语义设计 / QA Checklist - -## 验收目标 - -- [ ] 接收者完成 confirm/comment 后的二跳分享路径仍保留 `from=share&source=receiver`,并额外携带 `receiverAction=confirm` 或 `receiverAction=comment` -- [ ] 下一位接收者打开时能区分“上一位刚确认过”和“上一位刚补了线索” -- [ ] 新语义只补充接力上下文,不把用户动作夸大成官方验证、平台背书或事实定论 -- [ ] 新参数不增加新的常驻分享入口,不改变 R 组 `receiverConversionPrompt.targetRows` 的三行目标化结构 - -## 触发条件 - -- [ ] 只有 `from=share` 进入的详情页,在 active、低风险任务上完成 confirm 后,二跳分享 payload 才带 `receiverAction=confirm` -- [ ] 只有 `from=share` 进入的详情页,在 active、低风险任务上完成 comment 后,二跳分享 payload 才带 `receiverAction=comment` -- [ ] 普通详情入口完成 confirm/comment 后不使用 `source=receiver&receiverAction=*`;它应继续走既有普通详情或对应 relay 规则 -- [ ] `from=publish` 发布后扩散不使用 `receiverAction`,也不展示接收者二跳 action 来源语义 -- [ ] `stale`、高 `staleCount`、高 `reportCount`、`resolved`、`expired`、`hidden` 或其他 closed/risk 状态不出现鼓励性二跳 share CTA -- [ ] risk/closed 状态即使有历史 confirm/comment,也只能提示先核对、看评论或当历史线索,不提示“继续接力” - -## 二跳分享 Payload - -- [ ] confirm 成功后的 receiver conversion share path 形如 `/pages/detail/detail?id=&from=share&source=receiver&receiverAction=confirm` -- [ ] comment 成功后的 receiver conversion share path 形如 `/pages/detail/detail?id=&from=share&source=receiver&receiverAction=comment` -- [ ] `id` 仍按现有规则编码;新增 query 不破坏已有 `from=share&source=receiver` 检查 -- [ ] `receiverAction` 只记录上一位接收者在本次 `from=share` 入口完成的动作,不记录普通详情页动作、发布者动作或系统自动状态 -- [ ] 分享标题可以承接动作差异,但必须短句低压,例如“有人刚确认:<任务>”或“有人刚补线索:<任务>” - -## 接收侧展示 - -- [ ] `source=receiver&receiverAction=confirm` 时,接收侧标题明确为接力语境下的确认信号,例如“上一位刚确认过” -- [ ] `source=receiver&receiverAction=comment` 时,接收侧标题明确为接力语境下的线索补充,例如“上一位刚补了线索” -- [ ] confirm 摘要强调“有人提供了现场确认信号,仍需看任务内容和评论”,不能写成“已证实”“官方确认”“已经可靠” -- [ ] comment 摘要强调“评论区刚有新补充,先看最新评论再判断”,不能暗示线索必然正确 -- [ ] 三行接收侧 rows 可分别体现“为什么到你这”“先做什么”“不在现场”,其中 confirm 版本优先提确认信号,comment 版本优先提最新评论 -- [ ] note 保持谨慎边界:建议先看确认、评论和现场状态,再决定确认、补充或继续接力 -- [ ] 风险态标题/摘要优先显示风险、关闭或过时状态;`receiverAction` 不得覆盖风险提示 - -## 互斥关系 - -- [ ] `source=receiver&receiverAction=confirm` 不等同于 `source=confirm`;前者表示接收者链路二跳,后者表示确认动作自己的直接 relay 来源 -- [ ] `source=receiver&receiverAction=comment` 不等同于 `source=comment`;前者表示接收者链路二跳,后者表示评论成功自己的直接 relay 来源 -- [ ] 普通 `from=share` 且无 `source=receiver` 时,不读取 `receiverAction` 作为接收者二跳语义 -- [ ] 普通 share 面板、发布后扩散计划、comment relay、action relay 和 receiver conversion 不同屏竞争主 CTA -- [ ] `receiverConversionPrompt` 出现时仍优先于 `actionRelayPrompt` 和 `commentRelayPrompt` -- [ ] 分享接收侧 action strip 只服务第一步确认/补线索;完成动作后由 receiver conversion 接管二跳,不同时显示两个主按钮组 -- [ ] 发布者从 `from=publish` 分享出的路径不能混入 `source=receiver` 或 `receiverAction` - -## 参数与回退 - -- [ ] 仅接受小写 `receiverAction=confirm` 和 `receiverAction=comment` -- [ ] `source=receiver` 但缺失 `receiverAction` 时,回退到现有泛化 `source=receiver` 文案:“有人接力转给你” -- [ ] `source=receiver` 但 `receiverAction` 未知、为空、大小写异常或重复冲突时,回退到现有泛化 `source=receiver` 文案 -- [ ] `source` 不是 `receiver` 时忽略 `receiverAction`,避免污染 `source=confirm`、`source=comment`、普通分享和发布后扩散 -- [ ] query 解析异常、缺失 id、额外未知参数或参数顺序变化都不能导致页面崩溃 -- [ ] 自动检查需要覆盖 malformed query,并确认 helper 返回谨慎默认值或 `null`,不抛未捕获异常 - -## 窄屏与文案密度 - -- [ ] 新增 action 来源文案不新增第四行 R 组 `targetRows`;仍保持“推荐转给 / 为什么可信 / 下一位先看”三行 -- [ ] 320px 宽度下,接收侧标题、摘要、三行 rows、note 和按钮都可自然换行 -- [ ] confirm/comment 差异文案应短于两行优先,不挤压任务正文、信任摘要和评论入口 -- [ ] `receiverConversionPrompt.targetRows` 与接收侧 guide rows 不应在同一屏形成重复长说明;同一状态只保留当前上下文最重要的一组 rows -- [ ] 评论入口仍可被快速找到,不能被新增来源说明推到过深位置或被按钮遮挡 -- [ ] 长标题、长地点、长评论摘要、长 category 文案不撑破 panel,不与主按钮或 note 重叠 - -## 自动检查项 - -- [ ] 更新或新增 receiver conversion 检查,断言 confirm/comment 二跳 path 分别包含 `from=share&source=receiver&receiverAction=confirm/comment` -- [ ] 更新或新增 share receiver 检查,断言 `source=receiver + receiverAction=confirm/comment` 的标题、摘要和 rows 文案不同 -- [ ] 检查缺失/未知 `receiverAction` 回退到泛化 `source=receiver` 文案 -- [ ] 检查 `source=confirm`、`source=comment`、普通 `from=share`、`from=publish` 不被 `receiverAction` 污染 -- [ ] 检查 risk/closed 状态不出现鼓励性接力 CTA,且 `receiverAction` 不覆盖风险文案 -- [ ] 检查普通分享面板互斥条件仍包含 publish spread、share receiver、receiver conversion、action relay 和 comment relay -- [ ] 建议命令:`node --check utils/receiver-conversion.js` -- [ ] 建议命令:`node --check utils/share-receiver.js` -- [ ] 建议命令:`node --check pages/detail/detail.js` -- [ ] 建议命令:`node --check scripts/check-receiver-conversion.mjs` -- [ ] 建议命令:`node --check scripts/check-share-receiver.mjs` -- [ ] 建议命令:`node --no-warnings scripts/check-receiver-conversion.mjs` -- [ ] 建议命令:`node --no-warnings scripts/check-share-receiver.mjs` -- [ ] 建议命令:`node --no-warnings scripts/check-viral-candidate.mjs` -- [ ] 建议命令:`node --no-warnings scripts/check-devtools-readiness.mjs` -- [ ] 基线命令:`node scripts/check-json.mjs` -- [ ] 基线命令:`node harness/check-harness.mjs` -- [ ] 基线命令:`git diff --check` -- [ ] 基线命令:`npm run check` -- [ ] 基线命令:`bash harness/init.sh` - -## DevTools / 真机待验证 - -- [ ] DevTools 从真实或辅助 route 打开 `/pages/detail/detail?id=&from=share`,点击确认后出现 receiver conversion,并记录分享 payload 含 `receiverAction=confirm` -- [ ] DevTools 从同类入口提交评论后出现 receiver conversion,并记录分享 payload 含 `receiverAction=comment` -- [ ] DevTools 用二跳路径打开 `source=receiver&receiverAction=confirm`,确认接收侧标题/摘要强调“刚确认”,且不说官方验证 -- [ ] DevTools 用二跳路径打开 `source=receiver&receiverAction=comment`,确认接收侧标题/摘要强调“刚补线索/最新评论” -- [ ] DevTools 验证 `source=receiver` 无 `receiverAction`、未知 `receiverAction`、乱序 query 都回退稳定且不崩溃 -- [ ] DevTools 验证普通详情、`from=publish`、`source=confirm`、`source=comment`、risk/closed 入口不混入接收者二跳 action 语义 -- [ ] 真机触发系统分享面板,确认实际分享卡片路径携带 `receiverAction`,而不是只在 helper 字符串里存在 -- [ ] 真机二跳打开后确认标题、摘要、rows、任务正文和评论入口在窄屏下不重叠、不溢出 -- [ ] 云端评论路径下验证 comment 成功后才生成 `receiverAction=comment`,评论失败或取消不生成 -- [ ] 未执行的 DevTools/真机项必须记录为待验证或 blocked,不得写成已通过 diff --git a/harness/viral-receiver-action-source-product-brief.md b/harness/viral-receiver-action-source-product-brief.md deleted file mode 100644 index 6f0a4c3..0000000 --- a/harness/viral-receiver-action-source-product-brief.md +++ /dev/null @@ -1,127 +0,0 @@ -# 接收者二跳 Action 来源语义产品 Brief - -日期:2026-06-16 - -## 背景 - -R 组已把 `from=share` 接收者完成 `confirm` 或 `comment` 后的 `receiverConversionPrompt` 升级为三行目标化接力提示:`推荐转给`、`为什么可信`、`下一位先看`。当前二跳分享 path 为 `/pages/detail/detail?id=&from=share&source=receiver`,能表达“这来自上一位接收者的接力”,但还不能表达上一位刚完成的是确认还是补线索。 - -本 brief 探索是否在二跳 path 上增加动作来源语义,例如 `receiverAction=confirm` 或 `receiverAction=comment`,让下一位打开时看到更具体的接力语境。 - -## 用户问题与产品假设 - -`source=receiver` 的语义偏泛,只能告诉下一位“这是一条接收者接力来的任务”。下一位仍然不知道上一位到底贡献了什么:是刚确认过任务仍有参考价值,还是刚补了一条新的位置、时间、认领、绕行或求助线索。 - -产品假设是:如果二跳 path 额外携带上一位接收者刚完成的动作,下一位会更容易理解这次接力为什么现在发生,也更容易判断下一步先做什么。`confirm` 可以强化“刚有人给了一个仍有参考价值的信号”,`comment` 可以强化“刚有人补了新线索,先看最新评论”。这应提升信任理解和下一步行动理解,但不能把用户动作包装成事实保证。 - -## 适用场景 - -- 只适用于 `from=share` 进入详情的接收者。 -- 只在接收者完成有效 `confirm` 或有效 `comment` 后,由 `receiverConversionPrompt` 生成二跳分享 path。 -- 只针对 `active` 且低风险任务:无 stale 状态、无过时信号、无举报信号、未进入 `resolved` / `expired` / `hidden` 等 closed 状态。 -- 只承担二跳接力语境说明,不承担普通详情页的分享、发布者扩散或风险态传播。 - -## 非适用场景 - -- 普通详情入口、地图/列表/个人页进入详情,或没有 `from=share` 的入口。 -- `from=publish` 发布者成功页扩散路径。 -- 接收者只是浏览,没有完成 `confirm` 或 `comment`。 -- 接收者刚做的是 `stale` 或 `report`;这类动作应抑制公开扩散,而不是成为传播语境。 -- 任务已 `stale`、有任意过时/举报风险,或已 `resolved`、`expired`、`hidden`。 -- 任何需要读取通讯录、联系人、群关系、用户身份或昵称来解释来源的场景。 - -## 参数语义建议 - -推荐 query 名:`receiverAction` - -允许值: - -- `confirm`:上一位接收者完成了确认动作。 -- `comment`:上一位接收者提交了有效评论或线索。 - -与现有参数的关系: - -- `from=share` 仍表示当前打开详情页是分享入口。 -- `source=receiver` 仍表示这条分享来自上一位接收者完成行动后的二跳接力。 -- `receiverAction` 是 `source=receiver` 的可选细分语义,只描述触发这次二跳的动作类型。 -- 建议二跳 path 形态为 `/pages/detail/detail?id=&from=share&source=receiver&receiverAction=`。 -- 当 `source !== receiver` 时,应忽略 `receiverAction`。 -- 当 `receiverAction` 缺失或不是允许值时,应回落到现有 `source=receiver` 泛化接力文案。 -- 不建议使用 `action`、`sourceAction` 等更宽泛名称,避免和详情页已有 trust action、comment action 或事件处理混淆。 - -隐私边界: - -- query 不携带用户 id、openId、昵称、头像、手机号、群 id 或联系人信息。 -- 文案只说“上一位接收者”或“有人刚刚”,不展示具体身份。 -- 不读取通讯录,不推断关系链,不暗示系统知道应该转给哪一个具体联系人。 - -## 下一位接收者文案建议 - -### `receiverAction=confirm` - -语气重点:上一位给了一个“仍有参考价值”的信号,但不是事实保证。 - -建议文案: - -- 标题:`上一位刚确认过这条任务` -- 说明:`这不是平台保证,但说明有人刚看完后认为仍值得参考。` -- 下一步:`先核对地点、时间、确认数和过时信号,再决定是否能帮上忙。` -- 行动提示:`如果你也在附近,可以再确认一次;如果发现不准,标记过时。` - -避免文案: - -- `已确认属实` -- `信息可靠` -- `已经被验证` - -### `receiverAction=comment` - -语气重点:上一位刚补了线索,下一位应优先看最新评论。 - -建议文案: - -- 标题:`上一位刚补了一条线索` -- 说明:`打开后先看最新评论,里面可能有位置、时间、认领、绕行或求助补充。` -- 下一步:`再结合任务状态和过时/举报信号判断是否继续行动。` -- 行动提示:`如果你知道更多,可以继续补线索;如果信息已变化,标记过时。` - -避免文案: - -- `线索已经确认` -- `评论证明这条是真的` -- `可以放心转发` - -### 缺失或未知 `receiverAction` - -保持现有泛化接力语境: - -- 标题:`有人把这条任务接力给你` -- 说明:`先看任务状态、确认数和评论,再决定是否能帮忙或是否需要谨慎。` - -## 风险边界 - -- 不能把“用户确认”写成事实保证,只能写成“有人刚给出确认信号”。 -- 不能因为 `receiverAction=confirm` 就隐藏 stale/report/closed 风险;风险信号仍应优先展示。 -- 不能让 `stale`、`report`、`resolved`、`expired`、`hidden` 继续扩散;这些状态下应不生成带接力鼓励的二跳 path,也不展示鼓励性 CTA。 -- 不能把 `comment` 包装成事实验证;评论只是补充线索,下一位仍需核对。 -- 不能泄露上一位接收者身份,也不能让用户误以为系统读取了通讯录或群关系。 -- 不能让普通入口、`from=publish` 或风险态承担接收者二跳转化目标。 - -## 验收标准 - -- 产品语义明确:`receiverAction` 只作为 `source=receiver` 的动作细分,不替代 `from=share` 或 `source=receiver`。 -- 允许值只有 `confirm` 和 `comment`;未知值必须安全回落到泛化 `source=receiver` 文案。 -- 当前实现中,低风险 active 的分享接收者完成 `confirm` 后,二跳 path 可携带 `receiverAction=confirm`。 -- 当前实现中,低风险 active 的分享接收者完成有效 `comment` 后,二跳 path 可携带 `receiverAction=comment`。 -- 普通详情、`from=publish`、无 `from=share`、无有效行动、stale/report/closed 状态不携带鼓励性 action 来源语义。 -- 下一位接收者文案能区分 confirm 与 comment:confirm 讲“刚有确认信号”,comment 讲“刚补线索,先看最新评论”。 -- 文案不出现事实保证、官方认证、放心转发、具体用户身份或通讯录关系。 -- 参数不引入新持久化数据,不需要读取通讯录,不需要复杂归因模型。 - -## 未验证风险 - -- 尚未验证真实 WeChat 系统分享面板是否完整保留 `receiverAction` query。 -- 尚未验证下一位通过真实二跳 path 打开详情时,`receiverAction` 与 `source=receiver` 的解析和文案优先级是否符合预期。 -- 尚未验证 confirm/comment 后若远端状态瞬间变为 stale、reported、resolved、expired 或 hidden,分享 path 是否会及时抑制 action 来源语义。 -- 尚未验证窄屏下 confirm/comment 差异文案是否换行自然、不挤压任务主体和风险提示。 -- 尚未验证携带 action 语义是否真的提升信任理解、行动理解或二跳转化率;当前只是产品假设。 diff --git a/harness/viral-receiver-conversion-design-checklist.md b/harness/viral-receiver-conversion-design-checklist.md deleted file mode 100644 index 335e9e1..0000000 --- a/harness/viral-receiver-conversion-design-checklist.md +++ /dev/null @@ -1,41 +0,0 @@ -# 接力转化设计 / QA Checklist - -日期:2026-06-16 - -## 触发条件 - -- [ ] 只有 `from=share` 的详情页会在 confirm/comment 成功后生成提示 -- [ ] 页面首次加载和重新进入时默认不显示提示 -- [ ] 普通详情入口不会主动显示接力提示 - -## 状态边界 - -- [ ] active 且低风险时才出现可分享的接力 CTA -- [ ] `stale` / `report` / `resolved` / `expired` / `hidden` 都只给谨慎提示 -- [ ] 高风险状态不出现鼓励扩散的按钮文案 - -## 接收侧 - -- [ ] 接力后的分享路径使用 `from=share&source=receiver` -- [ ] 接收侧文案能看出这条是“接力确认/补充后转给你”的 -- [ ] 风险态接收侧仍然强调先看确认和评论 - -## 互斥关系 - -- [ ] 接力提示出现时,普通分享面板不会同时显示 -- [ ] 接力提示和已有的分享接收侧说明不会互相遮挡 - -## 验证 - -- [ ] `node --check utils/receiver-conversion.js` -- [ ] `node --check utils/share-receiver.js` -- [ ] `node --check pages/detail/detail.js` -- [ ] `node --check scripts/check-receiver-conversion.mjs` -- [ ] `node --no-warnings scripts/check-receiver-conversion.mjs` -- [ ] `node scripts/check-share-receiver.mjs` -- [ ] `node scripts/check-viral-candidate.mjs` -- [ ] `node scripts/check-json.mjs` -- [ ] `node harness/check-harness.mjs` -- [ ] `git diff --check` -- [ ] `bash harness/init.sh` -- [ ] `npm run check` diff --git a/harness/viral-receiver-conversion-product-brief.md b/harness/viral-receiver-conversion-product-brief.md deleted file mode 100644 index d449f2c..0000000 --- a/harness/viral-receiver-conversion-product-brief.md +++ /dev/null @@ -1,31 +0,0 @@ -# 接力转化产品 Brief - -日期:2026-06-16 - -## 目标 - -当用户是从分享进入详情页,并且他已经完成一次有效行动后,给一次轻量提示,鼓励他把这条任务继续转给下一位更可能路过的人。 - -## 产品假设 - -如果接收者已经愿意确认或评论,说明这条内容对他来说已经完成了第一次转化。此时不需要再强调任务本身,而是可以把“接力给下一位更可能路过的人”作为下一步,形成接收后再传播的链路。 - -## 范围 - -- 只在 `from=share` 且用户完成 `confirm` 或 `comment` 后提示 -- 只针对 active 且低风险的任务鼓励继续接力 -- `stale` / `report` / `resolved` / `expired` / `hidden` 只做谨慎提示,不鼓励公开扩散 -- 分享路径继续使用详情页,不引入新页面或新数据结构 - -## 非目标 - -- 不改地图、发布、评论、管理或个人中心的主流程 -- 不增加埋点、奖励、弹窗链路或复杂归因 -- 不把风险态包装成新的传播机会 - -## 风险 - -- 接力提示如果过于像营销按钮,会削弱“先看确认和评论”的谨慎感 -- 普通详情入口不应看到这条提示,否则会和原有分享入口互相抢注意力 -- 窄屏下按钮和正文需要保持可读,不要把卡片挤成一行 - diff --git a/harness/viral-receiver-design-checklist.md b/harness/viral-receiver-design-checklist.md deleted file mode 100644 index 74c408f..0000000 --- a/harness/viral-receiver-design-checklist.md +++ /dev/null @@ -1,41 +0,0 @@ -# 详情页分享接收侧设计 / QA Checklist - -## 信息层级 - -- [ ] 只有 `from=share` 的详情页显示接收侧提示 -- [ ] `from=publish` 仍然只显示发布成功扩散计划 -- [ ] 提示模块足够轻,不会压住评论、信任判断和主内容 -- [ ] 窄屏下标题、摘要、三行说明都能正常换行 - -## 文案规则 - -- [ ] active 任务能说清楚为什么转给我、现在先做什么 -- [ ] `comments`、`confirmations`、`staleCount`、`reportCount` 都会影响文案 -- [ ] `stale` / 高举报场景会提醒先核对,不会鼓励盲转 -- [ ] `resolved` / `expired` / `hidden` 只当历史线索,不继续放大传播 -- [ ] 不在现场时,文案会明确给出补评论或转给更可能路过的人这两条路 - -## 技术与路径 - -- [ ] 接收侧提示由 `utils/share-receiver.js` 统一生成 -- [ ] 详情页只在 `entryQuery.from === 'share'` 时读取该 helper -- [ ] 现有分享标题与发布成功扩散逻辑不受影响 -- [ ] 代码不引入新依赖 - -## 验证 - -- [ ] `node --check pages/detail/detail.js` -- [ ] `node --check utils/share-receiver.js` -- [ ] `node --check scripts/check-share-receiver.mjs` -- [ ] `node scripts/check-share-receiver.mjs` -- [ ] `node --check utils/share-message.js` -- [ ] `node --check utils/publish-spread.js` -- [ ] `node scripts/check-share-message.mjs` -- [ ] `node scripts/check-publish-spread.mjs` -- [ ] `node scripts/check-viral-candidate.mjs` -- [ ] `node scripts/check-json.mjs` -- [ ] `node harness/check-harness.mjs` -- [ ] `git diff --check` -- [ ] `bash harness/init.sh` -- [ ] `npm run check` -- [ ] WeChat DevTools 中确认 from=share 的提示模块、换行和按钮层级 diff --git a/harness/viral-receiver-product-brief.md b/harness/viral-receiver-product-brief.md deleted file mode 100644 index 5c41161..0000000 --- a/harness/viral-receiver-product-brief.md +++ /dev/null @@ -1,33 +0,0 @@ -# 详情页分享接收侧产品 Brief - -## 目标 - -当用户从 `from=share` 打开任务详情时,页面先明确回答三个问题: - -1. 你为什么会收到这条任务 -2. 现在最合适做什么 -3. 如果你不在现场,还可以怎么帮 - -## 产品假设 - -如果接收侧页面把“为什么转给你”“先做哪一步”“不在现场怎么帮”说清楚,用户更容易继续做确认、评论或二次转发,而不是只看一眼就离开。 - -## 范围 - -- 仅在 `entryQuery.from === 'share'` 且任务存在时展示接收侧提示 -- 不影响 `from=publish` 的发布成功扩散计划 -- 不新增复杂交互,动作仍然沿用现有确认、评论、转发按钮 -- 不引入新依赖 - -## 设计原则 - -- 文案短,先说结论,再说原因 -- 状态越不稳定,口径越保守 -- 高举报、已隐藏、已关闭、已过期、过时场景不鼓励盲目扩散 -- 让用户知道自己不是被叫来“围观”,而是被叫来“补确认、补线索、补判断” - -## 预期结果 - -- from=share 的详情页能清楚说明“为什么是我” -- 用户能快速看出下一步是确认、补评论还是继续转给更可能路过的人 -- 已关闭或风险较高的任务只会被当作历史线索看,不会被包装成仍然适合传播的内容 diff --git a/harness/viral-relay-channel-picker-design-checklist.md b/harness/viral-relay-channel-picker-design-checklist.md deleted file mode 100644 index ca343b5..0000000 --- a/harness/viral-relay-channel-picker-design-checklist.md +++ /dev/null @@ -1,79 +0,0 @@ -# 接收者二跳转发场景建议设计 / QA Checklist - -## 验收目标 - -- [ ] 在 `receiverConversionPrompt` 内增加 2-3 个轻量“适合转给”场景建议,帮助接收者判断更适合转给哪类群/人 -- [ ] 场景建议只补充接力对象判断,不读取联系人、不读取真实群、不承诺已经识别用户的微信群或好友 -- [ ] 现有 `receiverConversionPrompt.targetRows`、`shareReason` 和主分享按钮仍是核心结构;新增内容不替代 `shareReason` -- [ ] 新增内容保持 U 轮低密度,不制造大型 UI、不增加新的常驻分享入口、不让多个 CTA 同屏竞争 -- [ ] 风险态、关闭态或不应公开扩散状态下,不能因为出现“适合转给”建议而变成可扩散状态 -- [ ] 本清单是设计与验收要求,不代表 WeChat DevTools 或真机视觉验证已经通过 - -## 信息层级 - -- [ ] 不建议把 channel picker 放在 `shareReason` 与按钮之间;建议顺序为 `targetRows` -> 场景建议 -> `shareReason` -> 主分享按钮 / note -- [ ] 放在 `targetRows` 之后,是因为用户先看到“推荐转给 / 为什么可信 / 下一位先看”的完整判断框架,再看 2-3 个更具体的场景例子 -- [ ] 放在 `shareReason` 之前,是因为 `shareReason` 是点击分享前最贴近动作的一句转述话术,应该继续紧邻主按钮 -- [ ] 场景建议不应插入标题、body 或 `targetRows` 内部,避免破坏既有三行结构和扫描节奏 -- [ ] 场景建议不应放到按钮下方;按钮后的 note 只保留安全边界和低压提醒,不再承载新的选择信息 -- [ ] 面板阅读路径应是“为什么值得接力”到“适合给谁”到“可以怎么说”到“继续接力”,不要让用户先面对选择再理解风险边界 - -## UI 形态约束 - -- [ ] 首选 2-3 个轻量 chip 或紧凑微行,标题可为 `更适合转给` / `适合转给`,内容一眼扫完 -- [ ] chip 应是场景提示,不是新的分享按钮;不得使用 `open-type="share"`、主按钮颜色、加载态、倒计时或强行动词 -- [ ] chip 不应出现选中后才能继续的必填感;如有本地选中态,也只能改变轻微高亮,不改变分享路径、不读取真实群、不生成联系人数据 -- [ ] 若采用 rows,最多 3 行,每行只表达一个场景;不要再嵌套说明、头像、群图标、人数、在线状态或推荐理由长文 -- [ ] 若采用 segmented choices,必须弱化为“场景标签”而非模式切换;不能像三个并列 CTA,也不能把主分享按钮压低或挤窄 -- [ ] 场景建议区域高度应低于 `targetRows`,视觉重量低于标题/body,接近 `shareReason` 的信息密度 -- [ ] 不新增弹层、抽屉、联系人列表、群选择页、搜索框或“查看更多群”入口 - -## 选项文案 - -- [ ] 每个选项文案应短、可扫读,优先 4-8 个中文字符;最长不超过一行优先 -- [ ] 文案表达“人群/场景”,不表达真实识别结果;可写 `附近邻居`、`门卫前台`、`会路过的人`,不要写 `已识别的业主群`、`你的家长群`、`张三` -- [ ] 不使用“推荐联系人”“智能匹配”“已找到群聊”“一键转群”等会让用户以为系统读取了社交关系的词 -- [ ] 不使用压力型或扩散型文案,例如 `马上扩散`、`转发所有群`、`让大家都看到` -- [ ] `lost_found` 可偏向 `路过的人`、`门卫前台`、`楼栋邻居`;`intent=found` 时避免暗示高价值物品可私下交付 -- [ ] `help_needed` 可偏向 `能搭把手的人`、`附近店员`、`熟悉现场的人` -- [ ] `street_update` 可偏向 `同路线的人`、`即将经过的人`、`楼栋邻居` -- [ ] `check_in` 可偏向 `会到这里的人`、`附近朋友`、`社区邻居` -- [ ] 兜底 category 使用 `熟悉地点的人`、`附近邻居`、`可能路过的人` 这类通用但仍目标化的短句,不退化成泛泛“分享给好友” - -## 风险 / 关闭态互斥 - -- [ ] 只有 `receiverConversionPrompt.shouldRelay === true` 时才显示场景建议 -- [ ] `shareReason` 为 `null` 或 `targetRows` 为空时,不显示场景建议,避免在谨慎态制造转发暗示 -- [ ] `stale`、高 `staleCount`、高 `reportCount`、`resolved`、`expired`、`hidden` 都不能显示“适合转给”场景建议 -- [ ] 风险态即使来自 `from=share` 且用户完成过 confirm/comment,也只能保留先核对、看评论、看现场状态的谨慎语义 -- [ ] 关闭态只允许作为历史线索或处理结果查看,不出现 public relay CTA、场景建议或可转述扩散理由 -- [ ] 普通详情入口、`from=publish` 入口、非 `from=share` 入口不显示接收者二跳场景建议 -- [ ] `receiverConversionPrompt` 出现时仍优先压住普通分享面板、`actionRelayPrompt` 和 `commentRelayPrompt`,避免同屏多个传播入口 - -## 自动检查关注点 - -- [ ] 检查低风险 active + `from=share` + confirm/comment 时,`receiverConversionPrompt` 才返回 2-3 个场景建议 -- [ ] 检查场景建议数量不小于 2、不大于 3,且每个 label/value 都是短文案 -- [ ] 检查风险态、关闭态、普通入口、`from=publish`、`shouldRelay === false`、`shareReason === null` 时不返回或不渲染场景建议 -- [ ] 检查现有 `targetRows` 仍为三行,且顺序仍为 `推荐转给`、`为什么可信`、`下一位先看` -- [ ] 检查 WXML 顺序为 `targetRows` 后显示场景建议,再显示 `shareReason`,最后显示主分享按钮和 note -- [ ] 检查场景 chip/row 不带 `open-type="share"`,不绑定真实联系人或真实群选择行为 -- [ ] 检查新增实现不调用联系人、群聊、通讯录或外部社交关系读取 API,也不新增云端联系人匹配逻辑 -- [ ] 检查普通分享面板互斥条件没有回退:`receiverConversionPrompt` 可见时不同时显示普通 share、action relay 或 comment relay 主面板 -- [ ] 建议命令:`node --check utils/receiver-conversion.js` -- [ ] 建议命令:`node --check pages/detail/detail.js` -- [ ] 建议命令:`node --no-warnings scripts/check-receiver-conversion.mjs` -- [ ] 建议命令:`node --no-warnings scripts/check-viral-candidate.mjs` -- [ ] 基线建议:`node scripts/check-json.mjs`、`node harness/check-harness.mjs`、`git diff --check` - -## 手测关注点 - -- [ ] WeChat DevTools 从 `from=share` 打开 active 低风险任务,点击确认后观察场景建议是否出现在 `targetRows` 与 `shareReason` 之间 -- [ ] WeChat DevTools 从 `from=share` 打开 active 低风险任务,提交评论后观察场景建议是否仍为 2-3 个短选项,且不替代 comment 版本 `shareReason` -- [ ] WeChat DevTools 验证 `stale`、高举报、高过时、`resolved`、`expired`、`hidden` 不出现场景建议和公开 relay CTA -- [ ] WeChat DevTools 验证普通详情入口、`from=publish` 入口不出现接收者二跳场景建议 -- [ ] 窄屏 320px / iPhone SE 下验证 chip/row 可自然换行,不撑破 panel,不挤压 `shareReason`、主按钮或 note -- [ ] 窄屏下验证多个按钮和分享入口不会拥挤:场景建议不是按钮组,主分享按钮仍是唯一高权重 CTA,评论入口和信任动作不与其重叠 -- [ ] 长标题、长地点、长 category、长评论摘要场景下,场景建议不与 `targetRows`、`shareReason`、按钮文字或底部 note 重叠 -- [ ] 触发系统分享面板时,确认用户看到的是一个主分享按钮,而不是多个看起来都能“转发”的 chip/按钮 -- [ ] 手测记录必须写明设备/视口、入口、动作、任务状态和观察结果;未执行或因 DevTools service port blocker 无法执行时,记录为 not_run / blocked,不写成 passed diff --git a/harness/viral-relay-channel-picker-product-brief.md b/harness/viral-relay-channel-picker-product-brief.md deleted file mode 100644 index 3a664f5..0000000 --- a/harness/viral-relay-channel-picker-product-brief.md +++ /dev/null @@ -1,129 +0,0 @@ -# 接收者二跳转发场景选择产品 Brief - -日期:2026-06-17 - -分支:`codex/iter-viral-relay-channel-picker` - -基线:`194e3cc feat: add receiver share reason` - -## 背景 - -T 轮已经在 `from=share` 接收者完成 `confirm` 或 `comment` 后,为 `receiverConversionPrompt` 增加了 `shareReason`,让用户知道“转给下一位时可以怎么说”。当前 prompt 还保留三行 `targetRows`:`推荐转给`、`为什么可信`、`下一位先看`,并且二跳路径继续带 `from=share&source=receiver&receiverAction=confirm/comment`。 - -本轮产品目标不是继续润色那句理由,而是在用户已经完成一次有效行动后,给 2 到 3 个“更适合转到哪类场景”的短选项。例如楼栋群、门卫/前台、路过朋友、同路线邻居等。它必须是场景建议,不是联系人选择器:微信小程序不能读取联系人或微信群,本项目也不能知道真实关系链,因此文案不能暗示系统知道用户认识谁、在哪个群里或应该点名转给谁。 - -## 产品假设 - -“选转发场景”比继续润色分享理由更可能突破 99,因为 T 已经解决了“怎么说”的表达阻力,下一层瓶颈更可能是“发给谁/哪里”的判断阻力。 - -继续打磨 `shareReason` 的边际收益会递减:一句更顺的转述理由能降低表达成本,但用户仍然要离开小程序,在微信分享面板里自己选择聊天对象或群。如果他不知道这条任务更适合楼栋群、门卫/前台、路过朋友还是同路线邻居,再好的理由也可能停在页面内。 - -场景选择更贴近真实转发决策:它不要求系统读取通讯录,也不要求预填聊天文本,只是在分享前帮用户把任务类型、刚完成的动作和低风险状态翻译成“应该发到哪类场景”。这比继续增加一句同义分享理由更可能让用户形成明确下一步,从而提升自发二跳。 - -## 触发条件 - -Channel picker 只在以下条件全部满足时出现: - -- 当前详情入口来自分享:`from=share`。 -- 当前用户刚完成有效 `confirm` 或有效 `comment`。 -- 当前任务为低风险 `active`:`status === 'active'`,`staleCount === 0`,`reportCount === 0`。 -- 当前 `receiverConversionPrompt.shouldRelay === true`。 -- 二跳路径仍沿用已有语义:`from=share&source=receiver&receiverAction=confirm/comment`。 - -不触发场景: - -- 普通地图、列表、个人页、活动页、`from=publish` 等非分享入口。 -- 用户只是浏览、打开评论框、取消评论、重复确认失败,或动作没有成功落库。 -- 用户刚执行的是 `stale` 或 `report`。 -- 任务状态在动作后变为风险态、关闭态或无法确认。 - -## 场景建议规则 - -Channel picker 建议展示 2 到 3 个短选项,每个选项是一类可理解的转发场景,而不是具体联系人、群名或关系推断。 - -建议字段: - -- `label`:4 到 8 个字的场景名,例如 `楼栋群`、`门卫/前台`、`同路线邻居`。 -- `hint`:一句很短的适用理由,例如 `可能有人刚经过`、`方便现场核对`。 -- `priorityReason`:内部生成理由,用于自动检查或后续埋点说明,不直接展示为“系统知道”。 - -通用文案原则: - -- 用“建议发给”“适合转到”“可以问问”这类弱表达,不用“发给你的”“你认识的”“你所在的群”。 -- 不出现具体联系人、微信群名、昵称、头像、手机号、楼号房号或系统推断关系。 -- 不让用户误以为小程序读到了通讯录、微信群、位置同行关系或真实社交关系。 -- 选项保持短,不把长标题、长地点、评论正文拼进选项。 -- 如果无法判断分类,回落到 `能核对地点的人`、`附近可能路过的人` 这类保守场景。 - -### 分类与动作建议 - -| 条件 | 建议选项 | 规则说明 | -| --- | --- | --- | -| `lost_found` + `intent=lost` | `路过朋友`、`门卫/前台`、`楼栋群` | 丢失物品更适合发给可能经过丢失地点、能现场留意或代问的人。 | -| `lost_found` + `intent=found` | `楼栋群`、`门卫/前台`、`附近邻居` | 拾到物品更适合触达可能失主或现场代管点,避免公开扩散隐私细节。 | -| `help_needed` | `能搭把手的人`、`附近邻居`、`店员/前台` | 求助类优先找有能力行动或熟悉现场的人,不鼓励泛化围观。 | -| `street_update` | `同路线邻居`、`路过朋友`、`楼栋群` | 地点动态强调时效,适合发给即将经过或同路线的人。 | -| `check_in` | `附近朋友`、`同社区邻居`、`会到这里的人` | 打卡类更偏地点参考,适合发给可能到场判断的人。 | -| 未知分类 | `能核对地点的人`、`可能路过的人` | 保守兜底,只提示场景,不制造关系假设。 | - -`receiverAction` 可影响排序和提示语: - -- `receiverAction=confirm`:优先推荐能现场核对的场景,例如 `同路线邻居`、`路过朋友`、`门卫/前台`;hint 可强调 `刚有确认信号,适合再核对`。 -- `receiverAction=comment`:优先推荐熟悉地点或能理解线索的场景,例如 `楼栋群`、`附近邻居`、`店员/前台`;hint 可强调 `刚补了线索,先看最新评论`。 - -界面上不需要让用户“选择联系人”。更准确的定位是:在继续接力按钮上方,给一行 `适合转到`,下面展示 2 到 3 个轻量 chips。用户点选 chip 只改变页面内推荐文案或分享按钮旁的提示,不代表系统知道或锁定真实接收方。 - -## 与 T 的 shareReason 关系 - -Channel picker 和 `shareReason` 分工不同,不能互相替代: - -- Channel picker 补“发给谁/哪里”:帮助用户判断更适合转到楼栋群、门卫/前台、路过朋友、同路线邻居等场景。 -- `shareReason` 补“怎么说”:给用户一条可转述的短理由,例如 `我刚确认过,帮忙再核对一下` 或 `我刚补了线索,你先看最新评论`。 -- `targetRows` 继续承担结构化解释:`推荐转给`、`为什么可信`、`下一位先看` 三行不被 channel picker 拆掉。 -- `receiverAction=confirm/comment` 继续承担路径语义:下一位打开时知道上一位刚确认还是刚补线索。 - -推荐信息顺序: - -1. 现有 `targetRows`:先解释为什么这次可以考虑接力。 -2. Channel picker:再给 2 到 3 个“适合转到”的场景。 -3. `shareReason`:最后给“转给下一位时可以说”的一句话。 -4. `open-type="share"` 主按钮:保持一个主 CTA,避免多个传播入口互相抢。 - -## 风险边界 - -风险或关闭信号优先于增长。以下场景不出现 channel picker,也不出现“适合转到”“继续接力到这些场景”等鼓励公开扩散的选项: - -- 弱 stale:`staleCount > 0`,即使还没有达到 `stale` 阈值。 -- 弱 report:`reportCount > 0`,即使还没有达到 `hidden` 阈值。 -- `status === 'stale'`。 -- `status === 'resolved'`。 -- `status === 'expired'`。 -- `status === 'hidden'`。 -- 用户刚执行 `stale` 或 `report`。 -- 状态缺失、远端刷新失败、动作后无法确认最新状态。 - -这些场景可以保留谨慎说明,例如“先看确认和评论”“不建议继续公开扩散”,但不能给楼栋群、同路线邻居、路过朋友等公开转发场景建议,也不能生成鼓励性 channel 文案。 - -## 验收标准 - -- 低风险 `from=share` 接收者完成 `confirm` 后,出现 2 到 3 个 channel 选项,选项偏向现场核对或可能路过场景。 -- 低风险 `from=share` 接收者完成 `comment` 后,出现 2 到 3 个 channel 选项,选项偏向熟悉地点、能理解线索或能补充信息的场景。 -- 选项按 `category`、`intent`、`receiverAction` 有可观察差异;`lost_found` 的 lost/found 至少有不同优先级。 -- 所有选项都是泛化场景建议,不出现具体联系人、微信群名、真实关系或“系统知道你应该转给谁”的暗示。 -- Channel picker 与现有 `targetRows`、`shareReason` 同屏协作:`targetRows` 仍是三行,`shareReason` 仍解释怎么说,picker 只解释发给哪类场景。 -- 二跳分享路径不新增联系人或群信息,继续只使用 `from=share&source=receiver&receiverAction=confirm/comment`。 -- 普通入口、未完成行动、`stale/report` 动作、弱 stale/report、`stale/resolved/expired/hidden` 都不出现 channel picker。 -- 窄屏下 2 到 3 个短选项可换行,不挤压任务主体、风险提示、`shareReason` 和主分享按钮。 - -## 建议自动检查 - -- 新增或扩展 receiver conversion 检查:低风险 `confirm/comment` prompt 返回 `relayChannels` 或等价字段,数量为 2 到 3。 -- 检查 `lost_found` 的 `lost` 与 `found` channel 排序或内容不同;`help_needed`、`street_update`、`check_in` 至少各有一组分类化选项。 -- 检查 `confirm` 与 `comment` 的 channel 排序或 hint 有差异:confirm 偏核对现场,comment 偏看最新评论或补线索。 -- 检查 channel 文案不包含联系人/群关系暗示词,例如 `你的朋友`、`你所在的群`、`通讯录`、`微信群成员`、`系统识别`、`认识的人`。 -- 检查风险和关闭状态下 `relayChannels` 为空或不存在:弱 stale/report、`stale`、`resolved`、`expired`、`hidden`、`stale/report` 动作都覆盖。 -- 检查 `targetRows` 仍为三行,`shareReason` 仍存在且不被 channel picker 合并或替代。 -- 检查分享路径没有新增联系人、群、用户身份相关 query,只保留既有 `from=share&source=receiver&receiverAction=confirm/comment`。 -- 建议命令形态:`node --no-warnings scripts/check-receiver-conversion.mjs` 覆盖 helper 输出;`node --no-warnings scripts/check-viral-journey-evidence.mjs` 覆盖首跳完成行动到二跳路径的互斥链路;`git diff --check -- harness/viral-relay-channel-picker-product-brief.md` 覆盖本 brief 格式。 - -自动检查只能证明生成规则、互斥条件、禁用词和路径语义没有回退;真实 WeChat 分享面板、真机展示、用户是否真的按场景转发,以及是否提升二跳转化,仍需要 DevTools、真机或数据验证。 diff --git a/harness/viral-share-design-checklist.md b/harness/viral-share-design-checklist.md deleted file mode 100644 index 4272715..0000000 --- a/harness/viral-share-design-checklist.md +++ /dev/null @@ -1,34 +0,0 @@ -# 详情页任务转发设计/QA Checklist - -## 视觉与信息层级 - -- [ ] 详情页里的分享模块是轻量信息块,不是营销落地页 -- [ ] 模块清楚显示“转给谁 / 为什么转 / 能帮什么” -- [ ] 文案在窄屏下能完整换行,不挤压分享按钮 -- [ ] 分享按钮仍然是原生 `open-type="share"` 按钮 - -## 文案规则 - -- [ ] active 任务的文案以“附近会帮忙的人”为核心 -- [ ] lost_found 任务会结合 `intent` 说明更适合转给谁 -- [ ] `comments`、`confirmations`、`staleCount`、`reportCount` 都会影响文案 -- [ ] `resolved`、`expired`、`hidden`、高举报、过时等边界状态会收敛口径 -- [ ] 分享标题不会夸大“真实有效”这类结论 - -## 技术与路径 - -- [ ] `onShareAppMessage` 通过共享 helper 生成 title/path -- [ ] 详情页路径保留任务 `id` -- [ ] 分享路径带最小来源参数 `from=share` -- [ ] helper 对无 post 情况有保底路径 - -## 验证 - -- [ ] `node --check pages/detail/detail.js` -- [ ] `node --check utils/share-message.js` -- [ ] `node scripts/check-share-message.mjs` -- [ ] `node scripts/check-json.mjs` -- [ ] `node harness/check-harness.mjs` -- [ ] `git diff --check` -- [ ] WeChat DevTools 中确认详情页分享模块和分享按钮的实际显示 - diff --git a/harness/viral-share-product-brief.md b/harness/viral-share-product-brief.md deleted file mode 100644 index 2e692fc..0000000 --- a/harness/viral-share-product-brief.md +++ /dev/null @@ -1,34 +0,0 @@ -# 详情页任务转发产品 Brief - -## 目标 - -在详情页增加一版最小但可解释的转发提示,让用户在准备分享任务时,能立刻看见三件事: - -1. 转给谁更合适 -2. 为什么要转 -3. 转出去能帮任务什么 - -## 产品假设 - -当用户明确知道任务适合发给附近住户、物业、路过的人,且知道分享的目的不是“宣传”,而是“求确认、求线索、求修正”,转发意愿会更高。 - -## 范围 - -- 仅做详情页的转发体验优化 -- 不新增落地页,不做营销式引导 -- 不改现有评论、确认、过时、举报、关闭等业务状态 -- 不引入新依赖 - -## 设计原则 - -- 文案要谨慎,不能把任务说成确定事实 -- 详情页要贴近附近任务场景,不要像广告页 -- 转发说明要短,用户一眼能扫完 -- 状态越不确定,文案越要保守 - -## 预期结果 - -- 用户打开详情页后,能看懂这条任务适合转给谁 -- 用户知道分享的价值是补确认、补线索、纠正过时信息 -- resolved / expired / 举报等边界状态下,分享文案仍然不夸大真实性 - diff --git a/harness/viral-share-reason-design-checklist.md b/harness/viral-share-reason-design-checklist.md deleted file mode 100644 index 6c5d3c8..0000000 --- a/harness/viral-share-reason-design-checklist.md +++ /dev/null @@ -1,72 +0,0 @@ -# 接收者二跳转发理由设计 / QA Checklist - -## 验收目标 - -- [ ] 在 `receiverConversionPrompt` 面板内增加一条可转述短理由,让完成 confirm/comment 的接收者能说清“为什么把这条转给下一位” -- [ ] 短理由只服务二跳接力理解,不新增大面积 UI、不新增常驻分享入口、不替代现有三行 `targetRows` -- [ ] 现有 `receiverConversionPrompt.targetRows` 仍保持三行:`推荐转给`、`为什么可信`、`下一位先看` -- [ ] 文案保持低压、事实边界清楚,不写“请扩散”“已证实”“大家都在转”等误导性增长表达 -- [ ] 本清单是设计与验收要求,不代表 WeChat DevTools 或真机视觉验证已经通过 - -## 信息层级 - -- [ ] 短理由应放在 `targetRows` 与主按钮之间,作为一个低密度的过渡说明 -- [ ] 放在 `targetRows` 之后,是因为用户已经先看到“推荐转给谁 / 为什么可信 / 下一位先看什么”,再看到一句可直接复述的理由,理解链路更完整 -- [ ] 放在按钮之前,是因为它贴近“继续接力”动作,能帮助用户在点击前确认自己要转述的理由 -- [ ] 短理由不应放进 `targetRows` 作为第四行,避免破坏 R 组已有三行结构和扫描节奏 -- [ ] 短理由不应放在标题或 body 前面,避免抢走“你刚确认/刚补线索”的状态承接 -- [ ] 呈现形式建议为一行轻量说明或 `label + value` 微行,例如 `转给下一位时可以说:<短理由>` -- [ ] 如使用 label,label 要短于现有 target label,value 才是重点;不把它做成醒目的 badge、卡片、步骤条或第二个 CTA - -## 文案差异 - -- [ ] confirm 态短理由应强调“刚有人确认/现场信号更新”,但不能表达成官方验证或事实定论 -- [ ] confirm 态示例方向:`刚有人确认过这条,转给可能路过的人,能更快核对现场。` -- [ ] comment 态短理由应强调“刚补了线索/评论可先看”,但不能暗示评论一定正确 -- [ ] comment 态示例方向:`刚有人补了线索,转给熟悉这里的人,可以先看最新评论再判断。` -- [ ] confirm/comment 两种状态的理由必须一眼可区分,不能都退化成泛化的“帮忙转给下一位” -- [ ] 短理由应尽量控制在 24 个中文字符左右,最长不超过两行;长标题、长地点、长 category 文案不应被拼进这条理由 -- [ ] 短理由只描述接力原因,不承诺结果,不制造紧迫感,不使用“必须”“马上”“扩散起来”等压力型词 - -## 视觉约束 - -- [ ] 320px 窄屏下,短理由允许自然换行,但不应超过两行优先;换行后仍与 `targetRows` 和按钮有清楚间距 -- [ ] 短理由的字号、颜色和重量应低于标题/body,接近 note 或 target value 的密度,不能成为面板主视觉 -- [ ] 若采用 `label + value`,label 宽度不能挤压 value;value 需要 `min-width: 0`、自然换行和长词断行能力 -- [ ] 短理由与 `receiverConversion-actions` 之间要保留足够间距,避免按钮被说明文字贴住或视觉上归属不清 -- [ ] 主按钮宽度、高度和可点击区域不因短理由增加而被压缩;按钮文字不截断、不溢出、不与 note 重叠 -- [ ] 面板整体仍沿用现有 `receiver-conversion` 的 panel、间距、色系和低密度表达,不新增大块背景、插图、营销横幅或装饰元素 -- [ ] 短理由不应把评论入口、任务正文或信任信息推到明显更深的位置;若首屏空间紧张,优先收紧短理由而不是压缩按钮 - -## 风险 / 关闭态互斥 - -- [ ] 风险态、关闭态或不应公开扩散的状态下,短理由完全不出现 -- [ ] `stale`、高 `staleCount`、高 `reportCount`、`resolved`、`expired`、`hidden` 都不能显示可转述短理由 -- [ ] `receiverConversionPrompt.shouldRelay` 为 false 时,面板只能保留谨慎核对、历史线索或先看评论确认的语义 -- [ ] 风险态即使有 confirm/comment 历史,也不能显示“转给下一位时可以说”这类鼓励性说明 -- [ ] 普通详情入口、`from=publish`、非 `from=share` 入口不显示该短理由 -- [ ] `receiverConversionPrompt` 出现时仍应压住 `actionRelayPrompt` 和 `commentRelayPrompt`,避免同屏出现多个传播理由或多个主 CTA - -## 自动检查关注点 - -- [ ] 检查低风险 active + `from=share` + confirm 时,`receiverConversionPrompt` 包含 confirm 版本短理由 -- [ ] 检查低风险 active + `from=share` + comment 时,`receiverConversionPrompt` 包含 comment 版本短理由 -- [ ] 检查 confirm/comment 短理由不同,并分别包含“确认”或“线索/评论”等动作语义 -- [ ] 检查 `targetRows` 仍为三行,且 label 顺序仍为 `推荐转给`、`为什么可信`、`下一位先看` -- [ ] 检查 `shouldRelay === false`、risk/closed 状态、普通入口和 `from=publish` 不返回或不渲染短理由 -- [ ] 检查普通分享面板互斥条件没有回退:`receiverConversionPrompt` 可见时不同时显示普通 share、action relay 或 comment relay 主面板 -- [ ] 建议命令:`node --check utils/receiver-conversion.js` -- [ ] 建议命令:`node --check pages/detail/detail.js` -- [ ] 建议命令:`node --no-warnings scripts/check-receiver-conversion.mjs` -- [ ] 建议命令:`node --no-warnings scripts/check-viral-candidate.mjs` -- [ ] 基线建议:`node scripts/check-json.mjs`、`node harness/check-harness.mjs`、`git diff --check` - -## 手测关注点 - -- [ ] WeChat DevTools 从 `from=share` 打开 active 低风险任务,点击确认后观察短理由位置是否在三行 `targetRows` 与按钮之间 -- [ ] WeChat DevTools 从 `from=share` 打开 active 低风险任务,提交评论后观察短理由是否切换为 comment 版本 -- [ ] WeChat DevTools 验证 `stale`、高举报、高过时、`resolved`、`expired`、`hidden` 不出现短理由和公开 relay CTA -- [ ] WeChat DevTools 验证普通详情入口、`from=publish` 入口不出现接收者二跳短理由 -- [ ] 真机或窄屏模拟验证 320px / iPhone SE 宽度下,短理由、三行 `targetRows`、按钮和 note 不重叠、不截断、不挤压 -- [ ] 真机触发系统分享面板后,确认用户看到的按钮和短理由关系清楚;若无法检查实际分享 payload,必须记录为未验证或 blocked -- [ ] 真实视觉验证结果必须记录具体设备、入口、动作和观察结论;未执行项不能写成 passed diff --git a/harness/viral-share-reason-product-brief.md b/harness/viral-share-reason-product-brief.md deleted file mode 100644 index 05c3195..0000000 --- a/harness/viral-share-reason-product-brief.md +++ /dev/null @@ -1,104 +0,0 @@ -# 接收者二跳可转述理由产品 Brief - -日期:2026-06-16 - -## 背景 - -当前 Street Tasks 是原生微信小程序,详情页分享接收链路已经具备三层语义: - -- `from=share` 表示用户从分享打开任务详情。 -- 分享接收者完成 `confirm` 或 `comment` 后,会看到 `receiverConversionPrompt`,其中已有 `targetRows` 用来说明推荐转给谁、为什么可信、下一位先看什么。 -- 二跳路径已保留 `from=share&source=receiver&receiverAction=confirm/comment`,让下一位接收者能区分上一位刚确认还是刚补线索。 - -下一轮不要继续优先增加参数。真实 WeChat 分享面板不保证能预填聊天文本;即使路径和标题携带了更多语义,用户在转给下一位或发到群里时,仍然要自己组织一句“为什么转给你”。本轮目标是给完成行动的接收者一条可以直接转述的短理由,降低二次分享的表达成本。 - -## 产品假设 - -“可转述理由”比继续增加参数更可能提升二跳,因为它解决的是发送者当下的表达问题,而不是下一位页面解析问题。 - -继续加参数的价值主要发生在下一位打开页面之后:它能让系统知道来源、动作和语境,但发送者在点击微信分享、选择聊天对象、补一句说明时看不到这些参数。参数越多,也越容易把传播链路做成技术归因,而不是用户自然愿意说出口的理由。 - -短理由的价值发生在转发前和转发时:用户可以直接照着说、稍微改写后发到聊天里,或者只把它当作判断“该转给谁”的提示。它不依赖系统分享面板预填聊天文本,也不要求读取联系人、群关系或新增复杂归因。对低风险任务来说,一句克制、可读、能解释刚刚动作的理由,比更精细的 query 更接近真实分享场景。 - -## 触发条件 - -只在以下条件全部满足时出现鼓励性可转述理由: - -- 当前详情入口来自分享,即 `from=share`。 -- 用户刚完成一次有效 `confirm` 或提交一次有效 `comment`。 -- 当前任务仍是 `active`。 -- 当前任务没有任何 stale/report 信号:`staleCount` 为 0,`reportCount` 为 0,且状态不是 `stale`。 -- 当前 `receiverConversionPrompt.shouldRelay` 为真,并继续使用已有二跳路径语义:`from=share&source=receiver&receiverAction=confirm/comment`。 - -不触发场景: - -- 普通地图、列表、个人页、活动页等非分享入口。 -- 用户只是浏览、只打开评论区、或未成功完成 `confirm/comment`。 -- 用户刚完成的是 `stale` 或 `report`。 -- 任务在动作后变为风险态、关闭态或无法确认当前状态。 -- 为了生成理由而新增联系人读取、群关系推断、用户身份展示或更多分享 query 参数。 - -## 文案规则 - -可转述理由必须是一句短文本,目标是“用户能直接说给下一位听”,不是系统证明。 - -规则: - -- 短:建议 12 到 28 个汉字,最多一行主信息;长标题、长地点和评论正文不应拼进理由。 -- 可读:使用口语化表达,像用户会发在聊天里的句子,不写成后台标签或埋点说明。 -- 可转述:从发送者视角或自然第三人称出发,例如“我刚确认过,帮忙再核对一下”。 -- 不夸大:不能把确认写成事实保证,不能把评论写成验证结果,不能出现“属实”“已验证”“可靠”“放心转发”“官方确认”等表达。 -- 区分动作:`confirm` 讲“刚确认/刚有确认信号,适合再核对”;`comment` 讲“刚补线索/有新评论,下一位先看最新评论”。 -- 不泄露隐私:不展示上一位接收者身份,不摘录可能含隐私的评论正文,不暗示系统知道具体联系人。 -- 不替代 `targetRows`:短理由用于转述;`targetRows` 继续承担推荐对象、理由和下一位查看线索的结构化说明。 - -建议示例: - -- `confirm`:`我刚确认过,帮忙再核对一下` -- `confirm`:`这条刚有确认,看看你能不能帮` -- `comment`:`我刚补了线索,你先看最新评论` -- `comment`:`这条有新线索,帮忙看一下` - -避免示例: - -- `已确认属实,可以放心转发` -- `平台已验证,快帮忙扩散` -- `上一位证明这是真的` -- `评论已经确认信息可靠` - -## 风险边界 - -任何风险或关闭信号都优先于二跳增长目标。以下状态不能出现鼓励性可转述理由,也不能出现“继续接力”“帮忙转发”一类公开扩散 CTA: - -- `stale` 状态。 -- `staleCount > 0` 的弱 stale 信号,即使还没达到 stale 阈值。 -- `reportCount > 0` 的弱 report 信号,即使还没达到 hidden 阈值。 -- `resolved`。 -- `expired`。 -- `hidden`。 -- 用户刚执行 `stale` 或 `report`。 -- 状态缺失、远端状态刷新失败、或动作后状态无法确认。 - -这些场景可以显示谨慎说明,例如“先核对最新情况,不建议继续公开转发”,但它不能作为可复制的鼓励性分享理由,也不能被写入 share title、share reason 或 receiver conversion 主 CTA。 - -## 验收标准 - -- 低风险 `from=share` 接收者完成 `confirm` 后,`receiverConversionPrompt` 出现一条短理由,语义强调“刚确认/可再核对”,且不宣称事实可靠。 -- 低风险 `from=share` 接收者完成 `comment` 后,`receiverConversionPrompt` 出现一条短理由,语义强调“刚补线索/先看最新评论”,且不把评论包装成证明。 -- `confirm` 和 `comment` 的理由文本不同,用户能看出动作差异。 -- 理由不依赖 WeChat 分享面板预填聊天文本;即使系统面板只展示原生分享卡片,页面内也能让用户看到这句可转述理由。 -- 二跳路径继续沿用已有语义,不因本轮新增更多参数:`from=share&source=receiver&receiverAction=confirm/comment`。 -- `receiverConversionPrompt.targetRows` 继续保留,短理由不替代推荐对象、结构化理由和下一位查看线索。 -- 普通入口、非 `from=share`、未完成行动、`stale/report` 动作、弱 stale/report、`stale/resolved/expired/hidden` 都不出现鼓励性理由。 -- 文案不出现事实保证、官方认证、放心转发、具体用户身份、联系人关系或聊天群推断。 -- 窄屏下理由可以完整换行,不挤压任务主体、风险提示和分享按钮。 - -## 需要更新的自动检查 - -- 更新 `scripts/check-receiver-conversion.mjs`:断言低风险 `confirm/comment` conversion prompt 都包含短理由;两类理由不同;长度受控;不包含“属实、已验证、可靠、放心转发、官方确认”等禁用词;风险态和弱 stale/report 下理由为空或仅为非鼓励性谨慎文案。 -- 更新 `scripts/check-viral-journey-evidence.mjs`:在首跳 `from=share` 完成 `confirm/comment` 的链路模型中检查短理由存在,并继续检查 `targetRows`、互斥 CTA 和 `receiverAction` 路径语义不回退。 -- 更新 `scripts/check-share-receiver.mjs` 或接收侧相关检查:确认下一位 `source=receiver&receiverAction=confirm/comment` 文案仍区分动作,但不把上一位的短理由误读成事实证明。 -- 更新 `scripts/check-viral-journey-manual-evidence.mjs` 的证据 schema:允许手测记录页面内看到的 share reason 文案,同时继续标注真实 WeChat 分享面板是否无法预填聊天文本;不得把“面板未预填”判为功能失败。 -- 如 readiness 脚本扫描传播链路文档或必备检查项,需要把本 brief 和新增短理由检查纳入对应文件存在性与语义扫描。 - -自动检查只能证明 helper 输出、互斥条件、禁用词和路径语义没有回归;真实微信分享面板、真机展示、用户是否会手动转述以及二跳转化提升仍需要 DevTools、真机或数据验证。 diff --git a/harness/viral-targeted-relay-design-checklist.md b/harness/viral-targeted-relay-design-checklist.md deleted file mode 100644 index 0f3058d..0000000 --- a/harness/viral-targeted-relay-design-checklist.md +++ /dev/null @@ -1,109 +0,0 @@ -# 目标化二跳接力设计 / QA Checklist - -日期:2026-06-16 - -## 验收目标 - -- [ ] `receiverConversionPrompt` 不只是提示“再分享一次”,而是说明接力对象、接力理由、下一位接收者会看到什么 -- [ ] 面板能让完成 confirm/comment 的分享接收者理解“我为什么要转给这个人” -- [ ] 面板不把风险任务、关闭任务或普通详情页推向公开扩散 -- [ ] 新结构不与发布后扩散、普通分享、评论接力、确认接力和分享接收侧行动竞争 - -## 触发条件 - -- [ ] 只有 `from=share` 的详情页在低风险 active 任务完成 confirm 后显示目标化二跳接力 -- [ ] 只有 `from=share` 的详情页在低风险 active 任务完成 comment 后显示目标化二跳接力 -- [ ] 普通详情入口、`from=publish` 入口和非分享来源完成 confirm/comment 后不显示该面板 -- [ ] 页面首次加载、重新进入、刷新详情或切换任务时默认清空该面板 -- [ ] confirm/comment 成功后优先显示 `receiverConversionPrompt`,不同时显示 `actionRelayPrompt` 或 `commentRelayPrompt` - -## 状态与风险边界 - -- [ ] 只有 active、未关闭、未过期、未隐藏、低举报、低过时反馈任务出现公开 relay CTA -- [ ] `resolved` / `expired` / `hidden` / closed 状态不出现 public relay CTA -- [ ] `stale`、高 `staleCount` 或高 `reportCount` 只显示谨慎核对文案,不鼓励继续公开传播 -- [ ] 风险态可以提示“先看评论、确认和现场状态”,但按钮不能暗示“帮忙转发” -- [ ] 面板文案不说“已证实”“可靠无误”“请扩散”等确定性或压力型表达 - -## 信息层级 - -- [ ] 标题一句话交代二跳动作,例如“把线索转给更可能在附近的人” -- [ ] body 先承接用户刚完成的 confirm/comment,再说明为什么现在适合接力 -- [ ] 结构化行第一行说明“接力对象”,例如附近邻居、同楼栋、同校区、店员、宠物群、家长群 -- [ ] 结构化行第二行说明“接力理由”,且理由来自 category/intent、地点、刚确认或刚补充的线索 -- [ ] 结构化行第三行说明“下一位接收者看到什么”,包括任务摘要、你的确认/线索、先核对提示 -- [ ] note 保持低压和安全边界,强调只转给可能真的能帮忙的人 -- [ ] 主按钮只承担一次明确接力动作,次级信息不做成额外 CTA - -## 按钮与文案边界 - -- [ ] 主按钮使用低压文案,例如“转给可能帮得上的人”,避免“立即扩散”“全网转发” -- [ ] confirm 后文案强调“你刚提供了一个现场确认信号”,不夸大为事实证明 -- [ ] comment 后文案强调“你刚补了一条线索/问题”,鼓励转给能补下一步的人 -- [ ] 按钮附近不重复出现普通分享按钮,避免用户不知道该点哪个 -- [ ] 没有接力对象建议时,不显示空泛的目标化行;回退为谨慎 note 或不显示面板 -- [ ] 文案保持中文、短句、可扫读,避免营销化、催促式或道德绑架式语气 - -## Category / Intent 目标化文案 - -- [ ] `lost_found` + `intent=lost`:接力对象指向最后出现地点附近、物业/店员/同楼栋邻居;理由是“可能见过或能留意” -- [ ] `lost_found` + `intent=found`:接力对象指向失主可能所在人群;理由是“帮助物品回到主人手里”,不暗示私下交付高价值物品 -- [ ] `help_needed`:接力对象指向现场附近且能提供具体帮助或熟悉情况的人;理由强调“就近、短时、可确认” -- [ ] `street_update`:接力对象指向会经过该地点、同楼栋或同路线的人;理由强调“先核对现场再行动” -- [ ] `check_in`:接力对象指向可能会到这里、附近朋友或同社区邻居;理由强调“地点状态仍有参考价值” -- [ ] 其他 category 使用通用但仍目标化的“附近/熟悉该地点的人”,不退化成泛泛“分享给好友” - -## 互斥与竞争关系 - -- [ ] 目标化二跳面板出现时,普通 share 面板不出现 -- [ ] 目标化二跳面板出现时,分享接收侧 action strip 不再作为主 CTA 抢焦点 -- [ ] 目标化二跳面板出现时,comment relay 和 action relay 不同屏显示 -- [ ] 发布后扩散计划只服务 `from=publish`,不与接收者二跳面板同屏 -- [ ] 普通信任动作和评论入口仍可用,但视觉层级低于当前二跳主 CTA -- [ ] 面板不会遮挡任务正文、信任摘要、评论列表或关闭任务入口 - -## 窄屏与视觉 QA - -- [ ] 320px 宽度下标题、body、结构化行 label/value、note 和按钮都能自然换行 -- [ ] 长地点、长 category 文案、长评论摘要不会撑破卡片或挤压按钮 -- [ ] 结构化行 label 不依赖固定大宽度;value 换行后仍能看出对应关系 -- [ ] 主按钮在窄屏下高度可增长,文字不截断、不溢出、不与 note 重叠 -- [ ] 面板沿用详情页现有 panel、间距、字体和谨慎色系,不新增强营销视觉 -- [ ] 多行结构在 iPhone SE / 常见安卓窄屏中不把评论入口挤出首屏过远 - -## DevTools / 真机待验证 - -- [ ] WeChat DevTools 从 `from=share` 打开 active 低风险任务,点击确认后出现目标化二跳面板 -- [ ] WeChat DevTools 从 `from=share` 打开 active 低风险任务,发布评论后出现目标化二跳面板 -- [ ] WeChat DevTools 验证 stale / high-report / resolved / expired / hidden 不出现公开 relay CTA -- [ ] WeChat DevTools 验证普通详情入口 confirm/comment 后不出现该面板 -- [ ] WeChat DevTools 验证普通 share/action/comment relay 不与该面板同屏竞争 -- [ ] 真机触发主按钮后能打开系统分享面板,分享路径保留 `from=share&source=receiver` -- [ ] 真机二跳接收者打开后能看到“上一位接收者刚确认/补充”的上下文 -- [ ] 真机窄屏验证结构化行、按钮、note 换行和滚动位置 -- [ ] 记录任何未执行的 DevTools/真机项为待验证,不标记为通过 - -## 自动检查项 - -- [ ] `node --check utils/receiver-conversion.js` -- [ ] `node --check utils/share-receiver.js` -- [ ] `node --check pages/detail/detail.js` -- [ ] `node --check scripts/check-receiver-conversion.mjs` -- [ ] `node --check scripts/check-share-receiver.mjs` -- [ ] `node --check scripts/check-share-receiver-action.mjs` -- [ ] `node --check scripts/check-action-relay.mjs` -- [ ] `node --check scripts/check-comment-relay.mjs` -- [ ] `node --check scripts/check-viral-candidate.mjs` -- [ ] `node --check scripts/check-devtools-readiness.mjs` -- [ ] `node --no-warnings scripts/check-receiver-conversion.mjs` -- [ ] `node --no-warnings scripts/check-share-receiver.mjs` -- [ ] `node --no-warnings scripts/check-share-receiver-action.mjs` -- [ ] `node --no-warnings scripts/check-action-relay.mjs` -- [ ] `node --no-warnings scripts/check-comment-relay.mjs` -- [ ] `node --no-warnings scripts/check-viral-candidate.mjs` -- [ ] `node --no-warnings scripts/check-devtools-readiness.mjs` -- [ ] `node scripts/check-json.mjs` -- [ ] `node harness/check-harness.mjs` -- [ ] `git diff --check` -- [ ] `npm run check` -- [ ] `bash harness/init.sh` diff --git a/harness/viral-targeted-relay-product-brief.md b/harness/viral-targeted-relay-product-brief.md deleted file mode 100644 index cb92455..0000000 --- a/harness/viral-targeted-relay-product-brief.md +++ /dev/null @@ -1,126 +0,0 @@ -# 目标化二跳接力产品 Brief - -日期:2026-06-16 - -## 背景 - -M 组已让 `from=share` 接收者先完成确认或补线索;L 组已在接收者完成 `confirm` 或 `comment` 后显示 `receiverConversionPrompt`,并让继续分享路径带上 `from=share&source=receiver`。R 组的目标不是增加更多默认分享按钮,而是把这次高意图二跳提示改得更具体:告诉接收者该转给谁、为什么这次可信、对方先看什么。 - -## 产品假设 - -接收者完成确认或评论后,已经从“被动看到一条分享”变成“对这条任务有过一次判断或贡献”。如果此时提示只说“继续分享”,用户仍要自己想接收对象、转发理由和风险责任;如果提示明确给出推荐目标、接力理由和下一位接收者的查看线索,继续接力的心理成本会更低。 - -这次接力必须建立在刚发生的高意图动作上:确认代表“我看到它仍有参考价值”,评论代表“我补了一个新线索”。提示应该让接收者把这个新信号传给更可能核对或行动的人,而不是鼓励无差别公开扩散。 - -## 目标 - -- 在 `from=share` 接收者完成确认或评论后,把二跳提示从泛化分享改为目标化接力。 -- 让接收者立刻知道“转给谁”“为什么现在转可信”“下一位先看什么”。 -- 保持低风险、低打扰,只在 active 且信号健康的任务上鼓励公开接力。 -- 保留 `from=share&source=receiver` 的二跳来源语义,便于后续人工验证真实分享 payload。 - -## 非目标 - -- 不新增普通详情页的默认分享按钮。 -- 不改发布后扩散卡、分享接收者第一步行动条、评论/确认持久化或 CloudBase 数据模型。 -- 不引入奖励、排行榜、复杂归因、群发建议或通讯录读取。 -- 不把 `stale`、高举报、`resolved`、`expired`、`hidden` 等状态包装成传播机会。 -- 不让普通详情入口、风险态或 closed 状态承担接力转化目标。 - -## 适用场景 - -- 用户通过分享进入详情页,即入口参数包含 `from=share`。 -- 当前任务为 `active`,且没有明显风险信号:未达到 stale 阈值、举报数未接近隐藏阈值、不是 closed 状态。 -- 接收者刚完成一次有效 `confirm` 或提交一条有效 `comment`。 -- 页面能识别任务分类、状态、最近评论或刚完成动作,用来生成目标化提示。 - -不适用场景: - -- 普通地图、列表、个人页、活动页等非分享入口进入的详情。 -- 用户尚未确认或评论,只是浏览详情。 -- 当前任务已 `resolved`、`expired`、`hidden`,或已被多次标记过时/举报。 -- 接收者刚做的是 `stale` 或 `report`,此时应强调谨慎和停止扩散。 - -## 风险态边界 - -- `active` 且低风险:可以出现目标化二跳提示,文案以“转给更可能核对的人”为主。 -- `stale` 或 staleCount 接近阈值:不鼓励公开接力;只提示“可转给直接相关的人核对是否仍有效”。 -- reportCount 接近隐藏阈值:不鼓励继续扩散;提示先看评论和举报信号,必要时不要转发。 -- `resolved`、`expired`、`hidden`:不展示公开接力 CTA;可以展示轻量结果文案,例如“这条已结束,不建议继续转发”。 -- 普通详情入口:不展示接收者二跳提示,避免和常规浏览、发布者扩散、评论成功提示混淆。 - -## 建议 UI 信息结构 - -目标化二跳提示建议是一个轻量卡片或 action panel,替换现有泛化 `receiverConversionPrompt` 的核心文案,不增加新的常驻分享入口。 - -信息结构: - -1. `recommended target`:推荐转给谁。用一个短句说明目标人群,例如“转给刚在这附近的人”“转给楼下群里能核对的人”。 -2. `relay reason`:为什么这次可信。引用刚发生的动作和任务信号,例如“你刚确认仍有效”“你刚补了新线索”“已有 3 次确认且暂无举报”。 -3. `next receiver cue`:下一位先看什么。告诉下一位打开后优先查看的内容,例如“先看最新评论里的位置补充”“先核对照片和地点”“先看确认数与过时信号”。 -4. 主行动:一个 `open-type="share"` 的接力按钮,按钮文案避免泛化“分享一下”,改为“转给能核对的人”或“接力给附近的人”。 -5. 谨慎提示:一行弱提示,强调只转给可能有帮助的人;风险态下替换为不鼓励公开扩散的提示。 - -示例文案骨架: - -- 推荐对象:`建议转给:{target}` -- 接力理由:`因为:{reason}` -- 对方先看:`打开后先看:{cue}` -- 按钮:`接力给合适的人` - -## 分类目标化建议 - -### lost_found / 失物招领 - -- 推荐目标:物品可能经过地点附近的人、楼栋/店铺群、保安/前台、刚在附近活动的朋友。 -- 接力理由:接收者刚确认位置仍有效,或刚补充了外观、时间、领取点等线索。 -- 下一位先看:物品照片、丢失/拾到方向、最后出现地点、最新评论中的认领或补充线索。 -- 文案倾向:克制、具体,避免让不相关的人围观隐私物品。 - -### help_needed / 求助问答 - -- 推荐目标:能直接提供帮助的人、附近邻居、物业/店员、懂相关问题的朋友。 -- 接力理由:接收者刚确认需求仍在,或刚补充了更具体的困难/联系方式替代线索。 -- 下一位先看:需求紧急程度、地点、评论里的补充条件和是否已有回应。 -- 文案倾向:强调“能帮上忙的人”,避免扩大到无能力行动的人群。 - -### street_update / 地点动态 - -- 推荐目标:即将经过该地点的人、同楼栋/同路线群、附近店员或常去那里的人。 -- 接力理由:地点动态时效短,接收者刚确认后能提升“仍然有效”的可信度。 -- 下一位先看:更新时间、确认数、过时信号、具体地点或绕行/到场提示。 -- 文案倾向:突出时效,过时或 stale 接近阈值时立即转为“不建议继续公开转”。 - -### check_in / 打卡 - -- 推荐目标:可能会到这里的人、附近朋友、同社区邻居或熟悉该地点的人。 -- 接力理由:接收者刚确认这个地点状态仍有参考价值,或补充了到场体验/时间线索。 -- 下一位先看:地点状态、最新评论、适合到场时间和是否已有过时反馈。 -- 文案倾向:不制造热闹感,优先帮助合适的人判断是否值得到场。 - -## 分享路径与状态建议 - -- 二跳分享路径继续使用详情页,并保留 `from=share&source=receiver`。 -- 如果当前动作是 confirm,分享标题可强调“刚有人确认仍有效”,但不要宣称官方认证。 -- 如果当前动作是 comment,分享标题或提示可强调“刚补了一条新线索”,接收者打开后优先看最新评论。 -- 如果状态在动作后变为风险态或 closed,立刻抑制公开接力 CTA。 -- 如果缺少分类或无法生成目标化内容,使用保守 fallback:推荐转给“能核对这条信息的人”,并提示先看确认、评论和状态。 - -## 验收标准 - -- 仅 `from=share` 接收者在完成 confirm/comment 后看到目标化二跳提示;普通详情入口不显示。 -- 提示至少包含 recommended target、relay reason、next receiver cue 三段信息。 -- 分享按钮仍产出包含 `from=share&source=receiver` 的详情页路径。 -- lost_found、help_needed、street_update、check_in 至少各有一组分类化 recommended target / relay reason / next receiver cue。 -- active 低风险任务可鼓励接力;stale、高举报、resolved、expired、hidden 不鼓励公开接力。 -- closed 状态不出现“接力给附近的人”一类公开扩散 CTA。 -- 文案不宣称任务已被平台认证,不把用户确认等同于事实保证。 -- 窄屏下三段信息可换行阅读,按钮不挤压主要内容。 - -## 未验证风险 - -- 真实 WeChat 分享面板、二跳 payload 和 `source=receiver` 参数仍需 DevTools 或真机验证。 -- 分类目标化文案可能在真实长标题、长地点、长评论下显得拥挤,需要窄屏视觉检查。 -- 云端状态延迟可能导致刚完成动作后本地看似低风险,但远端已有举报或关闭状态;实现时需要以最新详情状态为准。 -- 过强的“推荐转给谁”可能让用户误以为系统读取了通讯录;文案应保持人群建议,不出现具体联系人。 -- lost_found 和 help_needed 分类涉及隐私或求助压力,需避免鼓励公开群发。 diff --git a/harness/viral-timeline-evidence-checklist.md b/harness/viral-timeline-evidence-checklist.md deleted file mode 100644 index 0580bbf..0000000 --- a/harness/viral-timeline-evidence-checklist.md +++ /dev/null @@ -1,96 +0,0 @@ -# 朋友圈 Timeline 证据与 QA Checklist - -日期:2026-06-17 - -## 范围与成功标准 - -- [ ] 本清单只补充朋友圈 timeline 的真实证据结构、手测步骤和 checker / 模板更新要求,不新增产品 UI。 -- [ ] 开发 agent 更新 checker 时,应把 timeline journeys 纳入真实手测 schema,但不得删减或弱化现有五条 `from=share` receiver journeys。 -- [ ] 没有 WeChat DevTools、真机或等价可复现证据时,timeline 能力只能记为 `blocked` / `unverified`,不能写成 `passed`。 -- [ ] 真实结果文件仍应保持 ignored/local;示例文件、草稿或静态推断不能作为发布证据。 - -## 新增 Timeline Journeys - -下列四个 QA 检查点应归入两条 required journey:`timeline-share-channel` 覆盖 active 菜单、payload 和单页打开;`timeline-risk-gating` 覆盖风险 / closed guard。 - -- [ ] `timeline-active-entry-menu`:低风险 active 详情页打开右上角系统菜单。 - - 步骤:准备 active、非 stale、非 reported、非 closed 的任务;进入详情页;打开右上角菜单;记录菜单项。 - - 期望:同时可见“发送给朋友”和“分享到朋友圈”;页面没有自制诱导分享按钮、弹层或奖励式扩散文案。 - - 证据:菜单截图或录屏,必须能看出任务状态、入口页面和菜单项。 -- [ ] `timeline-active-payload`:触发 active 低风险任务的朋友圈分享 payload。 - - 步骤:从同一详情页触发“分享到朋友圈”;记录 `onShareTimeline` 实际返回或 DevTools 可 inspect 的分享信息。 - - 期望:payload 包含标题、必要 `query`、分享图信息;`query` 至少带任务 `id`、`from=share`、`source=timeline`、`shareChannel=timeline`;不得声称返回了自定义 `path`。 - - 证据:payload 日志、DevTools 面板截图、录屏中的分享预览,或无法 inspect 时的明确说明。 -- [ ] `timeline-single-page-open`:从朋友圈或等价单页模式打开任务详情。 - - 步骤:使用真实朋友圈入口、DevTools 单页模式、场景值 1154 或项目约定的等价方式进入详情页;等待首屏稳定。 - - 期望:首屏可读到标题、地点 / 距离摘要、正文、图片或占位、状态、谨慎提示;无登录 / 无定位时不是空白页。 - - 证据:首屏截图或录屏,必须覆盖微信固定顶部 / 底部区域和核心内容。 -- [ ] `timeline-risk-and-closed-guard`:风险、关闭和弱风险信号不开放或不鼓励 timeline。 - - 步骤:分别准备 `resolved`、`expired`、`hidden`、`stale` / `staleCount >= 3`、`reportCount > 0`、未达阈值的弱 stale / report 任务;打开详情页和菜单,必要时检查分享标题 / query。 - - 期望:closed / hidden 不鼓励公开扩散;stale、高 stale、高 report 明确降级为谨慎查看;弱 stale / report 也不得出现“请大家转发”“帮忙扩散”等动作召唤。 - - 证据:每个 fixture 的 post id、状态字段、菜单 / 标题 / 落地页截图或录屏;缺少某类 fixture 时写 `blocked` 和补数方案。 - -## Evidence 字段质量 - -- [ ] 每条 timeline journey 必须有 `id`、`title`、`status`、`route`(或真实结果中的等价入口字段)、`steps`、`expected`、`actual`、`evidence`、`risks`、`followUp`。 -- [ ] `status` 允许值建议与现有 checker 对齐:真实结果中只允许 `passed`、`failed`、`blocked`;示例草稿可保留 `not_run`,但不能被当作真实结果。 -- [ ] `passed.actual` 必须写真实观察,不得是 `Not run`、`TODO`、`待填写`、`placeholder` 或“应该可以”。 -- [ ] `passed.evidence` 至少包含一种可核验材料:`screenshot`、`recording`、`payload`、`log`。 -- [ ] 截图证据必须写清楚:本地 ignored 路径、拍摄时间、设备 / DevTools 环境、覆盖了哪个 UI 状态。 -- [ ] 录屏证据必须写清楚:本地 ignored 路径、开始和结束动作、可见的关键判断点。 -- [ ] 日志证据必须写清楚:日志来源、截取时间、关键字段和值;不要粘贴联系人、群、openid、unionid、手机号或精确住址。 -- [ ] payload 证据必须写清楚:`title`、`query`、`imageUrl` / 默认图来源、是否缺失 `path`、来源参数、任务 id;不得只写“payload 正常”。 -- [ ] `actual` 必须包含真实差异:看到什么、没看到什么、是否和 expected 一致;风险 journey 要逐项列出 fixture 的状态和观察结果。 -- [ ] `passed` 的最低标准:真实运行过、证据可定位、actual 非占位、关键期望全部满足、没有无法解释的检查缺口。 -- [ ] `blocked` 只用于环境或数据阻断:无法打开 DevTools service port、无可用真机、账号无朋友圈能力、缺少 fixture、CloudBase / 网络导致页面无法加载等。 -- [ ] `blocked` 必须写 `blocker` 和 `followUp`:谁需要做什么、需要哪个设备 / 账号 / 数据、下一次如何复跑。 -- [ ] 如果 DevTools 无法 inspect `onShareTimeline` payload,可记录为 passed 的前提是:有真实分享预览 / 单页入口证据,并在 `sharePayloadInspection` 写明工具限制、替代证据、仍未验证的字段;否则应记 `blocked`。 -- [ ] 不允许用代码阅读、模拟函数返回、静态截图或旧版本证据替代本轮真实 DevTools / 真机 evidence。 - -## 单页模式 QA - -- [ ] 单页模式进入时确认 tabBar 不渲染,页面仍能表达当前位置、任务状态和下一步可做什么。 -- [ ] 未登录、游客态或本地 storage 隔离时,详情首屏仍展示任务核心内容,不要求先登录才可读。 -- [ ] 拒绝定位、定位 API 不可用或场景限制下,页面不自动弹出阻断式授权链路;距离可降级为地点摘要或未知距离。 -- [ ] `navigateTo`、`switchTab`、`reLaunch` 等在单页模式失败时,不影响继续阅读详情。 -- [ ] 不在首屏自动调用联系人、群、电话、剪贴板、媒体选择、支付等受限或敏感 API。 -- [ ] 微信固定顶部导航栏不遮挡标题、状态提示或返回 / 更多入口;底部固定栏不遮挡评论输入、主要操作或正文末尾。 -- [ ] 图片、地图、评论区和底部操作栏在窄屏设备上不互相覆盖;滚动到首屏和底部各录一段证据。 -- [ ] 云接口失败、任务找不到、隐藏或无权限时显示可读降级态,不出现空白页、无限 loading 或诱导分享兜底。 - -## 安全与合规 - -- [ ] 不新增或记录任何诱导分享 UI:不得有奖励、排行榜、强制转发、倒计时、群发暗示或“必须分享”语义。 -- [ ] QA 不能伪造 UI passed:没有截图 / 录屏 / payload / 日志的 journey 不得写 passed。 -- [ ] 风险态、争议态、closed 态不鼓励扩散;分享标题和落地页文案都应收敛为查看线索、状态已变化或不可查看。 -- [ ] payload 不携带联系人、群、私聊上下文、openid / unionid、手机号、详细住址、评论全文、精确坐标或其他隐私字段。 -- [ ] 丢失招领、高价值物品或疑似敏感内容只保留必要线索,不把联系方式、交付方式或私人信息放入 timeline 标题 / query。 -- [ ] 证据文件如果含账号、昵称、头像、地理位置或云环境 id,提交前必须脱敏;真实文件仍保持 ignored/local。 - -## Checker 与模板更新指引 - -- [ ] 在 manual evidence schema 中新增 `timelineJourneys`,或将 timeline ids 加入 `journeys` 的 required 集合;字段名需保持稳定,方便 checker 输出具体缺口。 -- [ ] Checker 应要求新增两条 timeline required journey,并在其中覆盖四类观察:active 菜单、active payload、单页打开、风险 / closed guard。 -- [ ] Checker 应验证 timeline `passed` 的 evidence 类型和 `actual` 质量,不接受占位文本和空 evidence。 -- [ ] Checker 应对 `sharePayloadInspection` 做显式判断:有 payload 证据则检查关键字段;无法 inspect 则要求原因、替代证据和未验证字段。 -- [ ] Checker 应在无真实结果文件时继续不阻塞,但输出必须说明这不代表朋友圈 DevTools / 真机 UI passed。 -- [ ] 模板应给出可复制的 fixture 字段:`activeLowRiskPostId`、`timelinePostId`、`weakStalePostId`、`weakReportPostId`、`closedPostIds`、`hiddenPostId`。 -- [ ] 模板应保留 `environment` 的 DevTools 版本、base library、微信版本、设备型号、真机 / 模拟器、网络、CloudBase、数据来源。 -- [ ] 模板中的示例状态只能是 `not_run` 或 `blocked`,并显著声明不是证据。 -- [ ] Readiness 输出不得把 blocked draft、示例文件或自动 checker 通过描述为 timeline 已实测通过。 - -## 回归保护 - -- [ ] 现有五条 `from=share` receiver journeys 必须继续作为 required journeys:首跳接收、confirm 转化、comment 转化、二跳接收语境、普通 / 风险入口。 -- [ ] 新增 timeline journeys 时,不得删除或放宽现有 receiver journeys 的 share payload、receiver guide、action strip、conversion prompt 检查。 -- [ ] U 轮 `relayChannels` 仍需手测:confirm / comment 后 2-3 个接力场景建议可见,且不是联系人 / 群选择器。 -- [ ] U 轮 `shareReason` 仍需手测:分享理由位于 target rows 与分享按钮之间,并随 confirm / comment 场景变化。 -- [ ] U 轮 `receiverAction` 仍需手测:二跳 path 或 query 保留 `receiverAction=confirm` / `receiverAction=comment`,接收页文案能区分来源动作。 -- [ ] Timeline 新证据不能替代 receiver evidence;两组 journey 可以共用同一测试环境,但每条都要有独立 actual 和 evidence 引用。 - -## 交付检查 - -- [ ] 开发 agent 更新 checker 后,至少运行对应 manual evidence checker、`node harness/check-harness.mjs`、`git diff --check`。 -- [ ] QA agent 真实手测后,结果文件应记录分支、commit、testedAt、tester、环境、fixture、summary 聚合状态。 -- [ ] 任何 skipped DevTools / 真机检查都要写成未验证风险,不得在总结里暗示已通过。 -- [ ] 若本轮只能完成结构补充,最重要风险是:评测仍可能因为缺少真实 DevTools / 真机 timeline evidence 而停留在 99/100。 diff --git a/harness/viral-timeline-evidence-product-brief.md b/harness/viral-timeline-evidence-product-brief.md deleted file mode 100644 index e1cb316..0000000 --- a/harness/viral-timeline-evidence-product-brief.md +++ /dev/null @@ -1,102 +0,0 @@ -# 朋友圈真实渠道证据 Product Brief - -日期:2026-06-17 - -## 目标 - -W 轮不再改分享文案,不新增产品 UI;目标是把 V 轮已经新增的朋友圈系统渠道纳入 ignored local manual evidence schema 和 DevTools run package。评测者必须能按固定流程记录:低风险 active 任务是否真的出现朋友圈菜单、`onShareTimeline` payload 是否正确、朋友圈单页模式是否可读、风险/闭合态是否不开放 timeline,以及 U 轮 `from=share` 接收者链路是否没有回退。 - -## 产品假设 - -V 轮的高杠杆点已经完成:详情页有 `onShareTimeline`,低风险 active 才开启 `shareTimeline` 菜单,timeline query 使用 `id=&from=share&source=timeline&shareChannel=timeline`,风险态 payload 保守。此时继续调标题或描述,边际收益低于补证据 schema。 - -原因: - -- 当前 99/100 的主要扣分不是“文案不够好”,而是没有 DevTools/真机菜单截图、朋友圈卡片、单页模式、真实 query/payload、转化数据。 -- 没有 schema 时,后续 agent 容易把自动检查、blocked draft、DevTools readiness 或口头描述误写成 UI passed。 -- 朋友圈是系统渠道,必须用真实环境证据证明;产品价值来自“可验证的渠道存在”,不是又一轮更顺耳的分享话术。 -- W 轮应把 V 的扣分点转成必填字段和必跑 journey,让评测从“相信实现”变成“检查证据”。 - -## 建议新增 Required Journeys - -在现有五条 `from=share` / receiver journey 之外,新增两条 timeline required journey。它们不是替代原五条,而是扩展同一个传播证据包:原五条继续证明 U 轮接收者首跳、确认/评论转化、二跳语境、普通/风险入口;新增两条证明 V 轮朋友圈系统渠道和风控边界。 - -### `timeline-share-channel` - -名称:低风险 active 任务的朋友圈真实渠道。 - -入口:低风险 active 任务详情页,任务满足 `status === 'active'`、`staleCount === 0`、`reportCount === 0`。 - -关键观察: - -- 系统菜单同时出现“发送给朋友”和“分享到朋友圈”。 -- `onShareTimeline` 返回 title/query/image,query 至少包含 `id=&from=share&source=timeline&shareChannel=timeline`。 -- 从朋友圈卡片或等价 DevTools/真机入口打开后进入详情页单页模式,`tabBar` 不渲染也能读懂标题、状态、地点、确认/过时/举报信号和评论线索。 -- 原 U 轮链路不回退:`from=share` receiver confirm/comment 后仍展示接力语境,二跳 payload 仍保留 `source=receiver&receiverAction=confirm/comment`。 - -### `timeline-risk-gating` - -名称:风险或闭合任务不开放朋友圈。 - -入口:弱 stale、弱 report、`stale`、`resolved`、`expired`、`hidden`、状态缺失或远端刷新失败的详情页 fixture。 - -关键观察: - -- 系统菜单不出现“分享到朋友圈”,或环境无法触发时必须记录明确原因。 -- 如仍允许发送给朋友,payload 和文案必须是谨慎语义,不鼓励继续扩散。 -- 不能只因为页面可打开、普通分享可用、或 checker 通过,就判定 timeline 风控 passed。 -- 原 `ordinary-and-risk-entries` journey 继续检查 receiver guide/action strip 不鼓励扩散;本 journey 额外检查系统 timeline 菜单缺失。 - -## Timeline Evidence 必填项 - -每条 timeline journey 的结果必须写入 ignored local evidence 文件,例如 `harness/manual-test-results.local-viral-journey*.json`。example、readiness、dry-run、blocked draft 不可作为真实结果。 - -### 低风险 timeline 必填 - -- 菜单证据:截图、录屏或可复查描述,明确“发送给朋友”和“分享到朋友圈”是否同时存在;记录设备、基础库、DevTools/真机环境。 -- Payload 证据:`onShareTimeline` 的 `title`、`query`、`imageUrl` 或无图说明;query 必须能看到 `id`、`from=share`、`source=timeline`、`shareChannel=timeline`,且不含联系人、群、用户身份、评论正文或精确隐私字段。 -- 单页模式证据:从朋友圈卡片或等价入口打开后的页面状态,说明是否为单页模式、`tabBar` 是否缺失、核心任务信息是否仍可读。 -- U 链路不回退证据:同一次手测包内记录 receiver confirm/comment journey 仍通过,或引用同一 commit 下已有有效 evidence;不能只检查 timeline 后跳过原五条。 -- 环境限制说明:若 DevTools 无法暴露真实朋友圈卡片或 payload,必须写明限制、替代观察方式和仍未验证的部分。 - -### 风险/闭合 no timeline 必填 - -- Fixture 证据:post id、状态、`staleCount`、`reportCount`、闭合原因或远端刷新失败原因。 -- 菜单缺失证据:截图、录屏或可复查描述,明确“分享到朋友圈”不存在;若系统环境无法打开菜单,写明无法触发原因,不能填 passed。 -- Payload 证据:若 `onShareTimeline` 无法触发,应记录“未生成 timeline payload”;若异常生成了 payload,应记录为 failed 并附实际 title/query/image。 -- 风险语义证据:普通分享或页面文案如存在,必须说明其没有鼓励继续扩散。 -- U 风控不回退证据:原 `ordinary-and-risk-entries` 中 receiver guide/action strip 不应在风险态鼓励扩散;该结论必须与 timeline 缺失一起记录。 - -## 状态语义 - -### `passed` - -表示真实 DevTools 或真机观察已经覆盖该 journey 的关键行为,且 evidence 非空、可复查、与当前 branch/commit/environment 对齐。Timeline passed 必须包含菜单、payload、单页模式或风险态菜单缺失证据;低风险 journey 还必须说明 U 链路不回退。 - -### `failed` - -表示已经进入真实 journey,并观察到与预期相反的产品行为。例如低风险 active 看不到 timeline 菜单、timeline query 缺少 `shareChannel=timeline`、风险/闭合态仍出现 timeline、单页模式核心信息不可读、或 U 轮 confirm/comment 二跳链路回退。Failed 必须写实际观察、证据和下一步 follow-up。 - -### `blocked` - -表示当前无法得出 UI 结论,不等于 UI passed。典型情况包括 DevTools service port 不可用、系统分享面板无法打开、真机权限/登录态/基础库限制、fixture 不可准备、朋友圈卡片或 payload 当前环境无法观察。Blocked 必须写 blocker 和 follow-up;不能因为 blocked draft 合法、schema 可解析或 run package exit 0,就把 UI 写成 passed。 - -## DevTools Run Package 要求 - -- Required journeys 输出应从五条扩为七条,并固定包含 `timeline-share-channel` 与 `timeline-risk-gating`。 -- Evidence checker 必须校验 timeline journey 唯一性、状态字段、非空 evidence、菜单证据、payload 字段、单页模式字段、风险态菜单缺失字段、U 链路不回退说明和 `summary.overallStatus` 聚合。 -- 无 ignored local result 文件时,运行包仍可 success,但只能说明“没有检查真实 UI 结果”,不得生成 timeline passed。 -- 有 blocked timeline journey 时,`overallStatus` 应聚合为 blocked,除非另有 failed。 -- Readiness、diagnostic、dry-run、draft-created、checker-passed 都必须显式声明:它们不等于朋友圈菜单或单页模式真实通过。 - -## 下一轮评测重点 - -下一轮评测不应再问“朋友圈文案是否更像传播文案”,而应检查 W 是否把 V 的 99 扣分点变成可执行、可校验的手测流程: - -1. 是否必须记录低风险 active 的 timeline 菜单、payload 和单页模式。 -2. 是否必须记录风险/闭合态没有 timeline。 -3. 是否把 blocked 和 passed 严格区分,避免环境阻塞被误判为 UI 通过。 -4. 是否把新增 timeline journey 纳入同一份 ignored local evidence,而不是游离在口头结论里。 -5. 是否明确要求原五条 U 轮 receiver journeys 不回退。 - -核心判定:W 轮成功不是“证明朋友圈已经转化”,而是让评测者无法跳过 V 轮真实渠道证据;只要缺菜单、缺 payload、缺单页模式、缺风险态反证或缺 U 链路回归检查,就不能再给完整通过结论。 diff --git a/harness/viral-timeline-landing-checklist.md b/harness/viral-timeline-landing-checklist.md deleted file mode 100644 index ef498a6..0000000 --- a/harness/viral-timeline-landing-checklist.md +++ /dev/null @@ -1,86 +0,0 @@ -# 朋友圈落地页语境 QA Checklist - -日期:2026-06-17 - -## 范围与验收目标 - -- [ ] 本清单只覆盖 `source=timeline` 打开详情后的接收引导文案与 QA,不要求新增按钮、弹层、分享诱导 UI 或新的 evidence journey。 -- [ ] 低风险 active 任务从朋友圈进入时,接收引导应解释:这是在朋友圈看到的附近任务,先看状态和评论,再决定确认或补线索。 -- [ ] 风险态、关闭态、未知任务和数据异常仍优先走谨慎 / 历史 / 不可查看语义,不因为 `source=timeline` 增加鼓励传播。 -- [ ] X 的自动检查只能做回归保护;W 的七条真实 manual evidence 仍是朋友圈能力验收来源。 - -## 低风险 Active 落地页文案 - -- [ ] `entryFrom === 'share' && source === 'timeline'` 且任务为 active、低风险、非 weak stale/report 时,`shareReceiverGuide.kicker` 能看出来源是朋友圈,而不是泛化“来自转发”。 -- [ ] `title` 应短句表达“朋友圈看到的附近任务”或等价语义;不要写成求转发、帮扩散、立即行动。 -- [ ] `summary` 应说明用户先读任务状态、确认信号和评论,再决定是否确认或补线索。 -- [ ] 三行 `rows` 仍复用现有接收侧结构,不新增第四行;推荐语义为:为什么到你这、先做什么、不在现场。 -- [ ] `rows[0]` 说明朋友圈来源和附近任务语境,不暗示平台已验证或朋友关系已被识别。 -- [ ] `rows[1]` 明确第一步是看状态 / 评论 / 现场变化,然后确认或补线索。 -- [ ] `rows[2]` 允许“不在现场”时先看评论、补充已知信息或谨慎交给更可能路过的人;不要引导公开扩散。 -- [ ] `note` 应收束为谨慎提醒:确认不是证明,评论和状态要先看;不使用“继续转发”“帮忙扩散”等鼓励性文案。 - -## 行动入口与后续链路 - -- [ ] `source=timeline` 的低风险 active 落地页仍复用既有 `shareReceiverActionStrip` 两个第一步动作。 -- [ ] 两个按钮文案仍是确认和补线索方向,不出现“分享到朋友圈”“转发朋友圈”等 share 按钮语义。 -- [ ] 两个按钮不得使用 `open-type="share"`;确认仍走既有 `react`,补线索仍走既有评论入口。 -- [ ] 用户完成 confirm 后,仍优先进入 `receiverConversionPrompt`,且不被 `actionRelayPrompt` 抢占。 -- [ ] 用户完成 comment 后,仍优先进入 `receiverConversionPrompt`,且不被 `commentRelayPrompt` 抢占。 -- [ ] 后续链路继续保留 R/S/T/U 的要求:目标化 rows、`receiverAction=confirm/comment`、shareReason、relayChannels 都不因 timeline 入口退化。 -- [ ] 二跳分享路径仍使用 `from=share&source=receiver&receiverAction=confirm/comment`;不要把二跳 source 误写成 `timeline`。 - -## 风险 / Closed / Unknown 互斥 - -- [ ] weak stale(如 `staleCount > 0` 但未达 stale)不显示鼓励性 timeline 文案,不显示鼓励接力的 target rows、shareReason 或 relayChannels。 -- [ ] weak report(如 `reportCount > 0` 但未隐藏)不显示鼓励性 timeline 文案;提示先核对评论和现场变化。 -- [ ] `stale` 或 `staleCount >= 3` 只强调过时风险和最新情况核对,不写“朋友圈看到,帮忙看看”这类动员语气。 -- [ ] `resolved` 只作为已处理 / 历史结果查看,不鼓励确认、补线索或再转。 -- [ ] `expired` 只作为过期 / 历史线索查看,不使用紧急或附近求助语气。 -- [ ] `hidden` 只显示不可查看 / 已下线 / 管理处理语义,不显示 timeline 来源营销或行动入口。 -- [ ] unknown task、加载失败、缺少 `id` 或数据权限失败时,不显示鼓励性 timeline 文案;页面应走可读降级或错误态。 -- [ ] 不能削弱 W 的 `timeline-risk-gating` / no-shareTimeline evidence:风险和 closed guard 仍必须用真实菜单、payload 或落地页证据观察,而不是只凭新增文案判断。 - -## 布局与可读性 - -- [ ] 新增 timeline 文案应沿用现有 receiver guide 字段,不新增额外面板、按钮或视觉层级。 -- [ ] `kicker`、`title`、`summary`、三行 `rows`、`note` 的总长度不要比现有 receiver guide 明显更长;优先替换语义,不堆叠解释。 -- [ ] 标题保持一行短句优先,窄屏可自然换行;不得挤压状态标签、地点、正文或评论入口。 -- [ ] 三行 `rows` 的 label 保持短标签,value 控制在现有接收侧说明的密度内,避免每行变成段落。 -- [ ] `note` 不应把首屏推得过长;正文、评论、`shareReceiverActionStrip` 和信任动作按钮仍可滚动可达。 -- [ ] iPhone SE / 低宽度模拟器下检查:receiver guide、正文、评论区、action strip、底部按钮没有遮挡或重叠。 -- [ ] 微信单页模式固定顶部 / 底部区域不遮挡 timeline guide 首屏关键信息和行动按钮。 - -## Evidence QA - -- [ ] X 的脚本或静态检查不能替代 W 的七条真实 manual evidence;没有 DevTools / 真机证据时仍只能写 `blocked` / `unverified`。 -- [ ] 新增 `source=timeline` 手测观察点应并入 `timeline-share-channel` 的单页打开观察,或作为第二跳接收说明的附加 actual;不要默认新增第八条 required journey。 -- [ ] `timeline-share-channel` actual 应记录:从朋友圈 / 单页模式打开后,kicker/title/summary/rows/note 是否解释朋友圈来源、附近任务、先看状态评论、可确认或补线索。 -- [ ] `timeline-risk-gating` actual 应继续覆盖 weak stale/report、stale、resolved、expired、hidden、unknown,不得只记录 active 低风险成功。 -- [ ] 截图或录屏证据要覆盖完整 receiver guide 和 action strip,避免只截标题导致无法判断按钮不是 share 按钮。 -- [ ] 如果自动检查通过但缺少真实菜单、payload、单页打开或风险态证据,总结必须明确“不代表 W 七条 manual evidence passed”。 -- [ ] 证据里不要记录联系人、群、openid / unionid、手机号、详细住址、评论全文或精确坐标。 - -## 回归保护 - -- [ ] 普通 `from=share` 且没有 `source=timeline` 时,仍保持既有接收侧文案,不被朋友圈语境覆盖。 -- [ ] `source=receiver&receiverAction=confirm` 仍表达“上一位刚确认过”,不要回退成 timeline 或普通分享文案。 -- [ ] `source=receiver&receiverAction=comment` 仍表达“上一位刚补了线索”,不要回退成 timeline 或普通分享文案。 -- [ ] `source=comment` 仍表达评论接力 / 先看最新评论,不被 timeline 文案覆盖。 -- [ ] `source=confirm` 仍表达确认接力 / 先看确认和评论,不被 timeline 文案覆盖。 -- [ ] 普通详情页分享路径 `/pages/detail/detail?id=&from=share`、地图分享 `/pages/map/map?from=share` 和 `onShareTimeline` query 互不回退。 -- [ ] 接收侧普通分享面板仍在 `shareReceiverGuide`、`receiverConversionPrompt`、`actionRelayPrompt`、`commentRelayPrompt` 存在时隐藏,不出现同屏 CTA 竞争。 - -## 自动检查建议 - -- [ ] `node --check utils/share-receiver.js` -- [ ] `node --check utils/share-receiver-actions.js` -- [ ] `node --check utils/receiver-conversion.js` -- [ ] `node --check pages/detail/detail.js` -- [ ] `node --no-warnings scripts/check-share-receiver.mjs` -- [ ] `node --no-warnings scripts/check-share-receiver-action.mjs` -- [ ] `node --no-warnings scripts/check-receiver-conversion.mjs` -- [ ] `node --no-warnings scripts/check-viral-journey-evidence.mjs` -- [ ] `node --no-warnings scripts/check-viral-journey-manual-evidence.mjs` -- [ ] `node harness/check-harness.mjs` -- [ ] `git diff --check -- harness/viral-timeline-landing-checklist.md` diff --git a/harness/viral-timeline-landing-product-brief.md b/harness/viral-timeline-landing-product-brief.md deleted file mode 100644 index b641fa2..0000000 --- a/harness/viral-timeline-landing-product-brief.md +++ /dev/null @@ -1,157 +0,0 @@ -# 朋友圈落地语境产品 Brief - -日期:2026-06-17 - -## 背景 - -W 轮评测已经到 99/100,当前短板不在证据基础设施:W 已经把 V 轮新增的朋友圈系统渠道纳入手测 schema、七条 required journey 和 blocked/passed 判定。V 轮也已经补上真实朋友圈入口,timeline query 使用 `from=share&source=timeline&shareChannel=timeline`。 - -现在的问题是落地语境还没有吃到这个新增来源。当前 `utils/share-receiver.js` 对 `source=timeline` 没有专门分支,用户从朋友圈点进来时看到的仍是普通“来自转发”接收引导。对朋友圈这种弱关系、低上下文入口来说,这不够明确:用户容易把它当成广告、泛泛帖子或普通转发,而不是一条需要本地核对、补线索的附近任务。 - -## 产品假设 - -朋友圈入口和好友/接收者二跳不同。朋友圈通常是弱关系、异步曝光、上下文缺失:打开的人可能不知道发布者是谁,也不知道为什么自己会看到这条。落地页需要第一时间把语境补齐成“我是在朋友圈看到一条附近任务”,并把下一步收敛到本地核对和线索补充。 - -更好的 `source=timeline` receiver guide 应该让用户理解: - -- 这是一条附近任务,不是广告或泛泛动态。 -- 先看任务状态、确认信号和评论,别只看分享标题。 -- 如果自己在附近,可以确认是否仍有效。 -- 如果知道位置、时间、认领、绕行或求助补充,可以评论补线索。 -- 如果不在现场,不要盲目背书;只在合适时转给更可能路过的人。 - -核心不是鼓励扩散,而是把朋友圈曝光转成一次谨慎的本地核对。 - -## 范围 - -本轮只调整 `source=timeline` 的 receiver guide 文案、语义优先级和自动检查。 - -范围内: - -- 在 `from=share` 且 `source=timeline` 时,低风险 active 任务展示专门的朋友圈落地语境。 -- 保持现有 `kicker`、`title`、`summary`、三行 `rows`、`note` 信息结构,不新增复杂 UI。 -- 增加自动检查,证明 timeline 入口不会继续落到普通“来自转发”语境。 -- 自动检查风险、closed、unknown 状态仍优先使用谨慎语义。 -- 自动检查 U/R/S/T 的 receiver conversion、`source=receiver`、`receiverAction=confirm/comment` 语义不回退。 - -范围外: - -- 不新增系统渠道;朋友圈渠道仍由 V 轮的 `onShareTimeline` 和 `shareChannel=timeline` 承担。 -- 不新增按钮、弹窗、奖励、诱导分享、联系人读取、群选择器或复杂 UI。 -- 不改变 `shareTimeline` 风险门控,不放宽 stale/report/closed/unknown 的 gating。 -- 不改变普通 `from=share`、`source=receiver`、`source=confirm`、`source=comment` 的既有意图。 -- 不新增归因模型、埋点、持久化字段或分享奖励。 - -## 低风险 Active Key Copy 要求 - -适用条件:`from=share`、`source=timeline`,且任务为低风险 active:`status === 'active'`,无强 stale,无遮挡举报风险,未 resolved/expired/hidden。 - -推荐语义: - -- `kicker`:应明确是朋友圈入口,例如 `朋友圈看到`。 -- `title`:应同时点出附近任务和核对动作,例如 `附近任务,先核对一下`。 -- `summary`:应说明“这是朋友圈里看到的附近任务,先看状态和评论;在附近就确认,知道线索就评论”。不能写成平台背书或广告式召唤。 -- 第一行 `rows`:说明为什么到你这里。语义应是“朋友圈里的人可能不都在现场,你可能刚好更接近地点或知道线索”。 -- 第二行 `rows`:说明先做什么。语义应是“先看状态、确认信号和最新评论,再决定确认或补评论”。 -- 第三行 `rows`:说明不在现场怎么办。语义应是“不在现场不要盲目确认;适合时转给更可能路过的人”。 -- `note`:应收束风险,提醒“朋友圈只提供入口,转发前先核对状态和评论;能补线索优先补线索”。 - -建议文案骨架: - -| 字段 | 建议 copy | -| --- | --- | -| `kicker` | `朋友圈看到` | -| `title` | `附近任务,先核对一下` | -| `summary` | `这条任务是从朋友圈点进来的,先看状态和评论。你在附近就帮忙确认,知道线索就补一条。` | -| `rows[0].label` | `为什么到你这` | -| `rows[0].value` | `朋友圈里看到的人不一定在现场,你可能更接近地点,或知道新的位置、时间和处理情况。` | -| `rows[1].label` | `先做什么` | -| `rows[1].value` | `先看任务状态、确认信号和最新评论;能现场核对就确认,知道更多就评论补线索。` | -| `rows[2].label` | `不在现场` | -| `rows[2].value` | `不要盲目确认;如果这条更适合别人核对,可以转给更可能路过的人。` | -| `note` | `朋友圈只是入口,转发前先核对状态和评论;有线索时,先补评论会更有用。` | - -可接受变体: - -- 可以按分类轻微调整标题,例如失物类强调“附近线索”,求助类强调“附近求助”,地点动态强调“附近动态”。 -- 仍必须包含“朋友圈看到/附近任务/先核对状态评论/能确认或补线索/不在现场可转给更可能路过的人”这些语义。 -- 不能出现“已验证、属实、放心转发、帮忙扩散、必须转发、转发有奖”等诱导、背书或奖励词。 - -## 风险、Closed 和 Unknown 状态 - -`source=timeline` 不能覆盖风险优先级。只要任务处于风险、closed 或未知状态,receiver guide 应继续沿用更谨慎的风险语义。 - -要求: - -- `hidden`:强调已隐藏,只适合历史处理或给发布者/管理员看,不鼓励公开扩散。 -- `resolved`:强调已关闭,只当历史结果参考,不鼓励继续转。 -- `expired`:强调已过期,先看最新情况,不鼓励按旧信息传播。 -- `reportCount >= 2`:强调已有较多举报,先核对评论和现场变化。 -- `staleCount >= 3`:强调已有过时反馈,别把旧信息继续传开。 -- 弱 stale、弱 report 或远端刷新失败:即使没有达到强风险阈值,也不应因为 `source=timeline` 生成鼓励性朋友圈文案。 -- unknown 或 post 缺失:不生成 timeline 专属鼓励语境;宁可回落为空或谨慎文案。 - -判定原则:风险态的标题、摘要、rows、note 都应先解释风险,不因为“朋友圈看到”而鼓励扩散。 - -## 与 V/W/U/R/S/T 的关系 - -V 提供渠道:本 brief 不重新定义朋友圈分享能力,只消费 V 已有的 `source=timeline` 和 `shareChannel=timeline` 语义,让从朋友圈打开后的详情页更像“附近任务落地页”。 - -W 提供证据 schema:本 brief 不替代 W 的七条 journey 和真实手测证据要求。下一轮实现应把新增 timeline landing 语义纳入自动检查和真实手测记录,不能只靠口头说明。 - -U/R/S/T 的 receiver conversion 不回退: - -- `source=receiver&receiverAction=confirm` 仍表达上一位刚确认。 -- `source=receiver&receiverAction=comment` 仍表达上一位刚补线索。 -- `source=confirm`、`source=comment` 仍保留原有首跳或接力语义。 -- `from=share` 接收者完成 `confirm/comment` 后,`targetRows`、`relayChannels`、`shareReason`、二跳 path 和 action strip 不应被 timeline 文案覆盖。 -- 普通转发入口没有 `source=timeline` 时,不应被误判为朋友圈入口。 - -本轮是 X 组产品补语境,不是重做 V 的渠道、W 的证据或 U/R/S/T 的接收者转化。 - -## 自动检查要求 - -下一轮实现应增加或更新自动检查,至少覆盖: - -1. `source=timeline` 低风险 active 会生成 timeline 专属 guide,不再只是 `kicker: 来自转发`。 -2. 低风险 active 的 `kicker/title/summary/rows/note` 包含朋友圈、本地核对、状态评论、确认、补线索和更可能路过的人这些关键语义。 -3. timeline 专属文案不包含事实背书、诱导分享、奖励分享、强制扩散或联系人/群关系暗示。 -4. `hidden/resolved/expired/stale/report/unknown` 等状态下,风险语义优先,不能因为 `source=timeline` 出现鼓励性扩散文案。 -5. `source=receiver` 和 `receiverAction=confirm/comment` 的既有 guide 仍保持差异化,不被 timeline 分支覆盖。 -6. 普通 `from=share`、`source=confirm`、`source=comment` 和无来源入口不回退。 -7. 若检查 timeline query,仍应只要求 V/W 已定义的 `from=share&source=timeline&shareChannel=timeline`,不新增身份、联系人、群、评论正文或精确隐私字段。 - -## 下一轮评测标准 - -下一轮评测不应只问“朋友圈菜单是否存在”,而应补看朋友圈落地语境是否让用户知道自己该做什么。 - -通过标准: - -- 从 `from=share&source=timeline&shareChannel=timeline` 打开低风险 active 详情页,首屏 receiver guide 明确是“朋友圈看到的附近任务”。 -- 用户能从 guide 中读出顺序:先看状态和评论,再确认或补线索,最后才在合适时转给更可能路过的人。 -- 风险、closed、unknown 任务不因为 timeline 来源而变得更鼓励扩散。 -- V 的 timeline 渠道证据、W 的 evidence schema、U/R/S/T 的 receiver conversion 全部不回退。 -- 自动检查能稳定防止 timeline 入口回落成普通“来自转发”。 - -扣分标准: - -- 低风险 active 仍显示普通 `来自转发`,没有朋友圈语境。 -- 文案只说“分享/转发”,没有附近任务、本地核对、状态评论或补线索。 -- 文案把朋友圈写成扩散任务,出现强诱导或事实背书。 -- 风险或 closed 状态因为 `source=timeline` 出现“继续转给更多人”之类鼓励。 -- 为了优化落地页新增按钮、奖励、联系人读取、系统渠道或改变 risk gating。 -- 实现只改文案,没有自动检查保护。 - -## 真实手测要求 - -真实手测应继续按 W 的 evidence schema 记录,不能用自动 checker 通过替代 UI 通过。 - -建议手测: - -1. 低风险 active:用真实或等价入口打开 `/pages/detail/detail?id=&from=share&source=timeline&shareChannel=timeline`,截图记录 receiver guide 的 `kicker/title/summary/rows/note`。 -2. 核对落地理解:确认首屏能读出“朋友圈看到”“附近任务”“先看状态和评论”“在附近可确认”“知道线索可评论”“不在现场可转给更可能路过的人”。 -3. 风险反例:用 weak stale、weak report、`stale`、`resolved`、`expired`、`hidden`、unknown post 或远端刷新失败 fixture 打开同类 timeline 入口,记录没有鼓励扩散。 -4. 回归 U/R/S/T:同一 commit 下跑 receiver confirm/comment journey,确认 `source=receiver`、`receiverAction`、`targetRows`、`relayChannels`、`shareReason` 和二跳分享语义仍在。 -5. 渠道证据:继续保留 V/W 要求的朋友圈菜单、payload、单页模式和风险 no-timeline 证据;本 brief 只新增落地页文案截图和语义观察。 - -如果 DevTools 或真机环境无法真实打开朋友圈卡片,应把 timeline landing 记录为受限或 blocked,并明确哪些只是 query 模拟,不能写成完整 UI passed。 diff --git a/harness/viral-timeline-share-design-checklist.md b/harness/viral-timeline-share-design-checklist.md deleted file mode 100644 index a2271bc..0000000 --- a/harness/viral-timeline-share-design-checklist.md +++ /dev/null @@ -1,94 +0,0 @@ -# 朋友圈分享设计 / QA Checklist - -日期:2026-06-17 - -## 验收目标 - -- [ ] 详情页右上角系统分享菜单暴露“发送给朋友”和“分享到朋友圈” -- [ ] 朋友圈分享只用于内容展示与线索触达,不新增营销式 UI、不强推二次传播 -- [ ] 朋友圈落地页在单页模式下仍能读到任务核心内容、状态和谨慎提示 -- [ ] 风险态、关闭态或争议任务不会被包装成“请大家扩散”的诉求 -- [ ] 本清单是设计与验收要求;没有 WeChat DevTools 或真机证据时只能记为 `blocked` / `unverified` - -## 官方边界核对 - -- [ ] 以微信官方文档核对:`onShareTimeline` 只控制右上角“分享到朋友圈”的分享内容 -- [ ] `onShareTimeline` 返回内容不支持自定义页面 `path`;任务 id、来源等只能通过 `query` 携带 -- [ ] 页面必须同时定义 `onShareAppMessage` 和 `onShareTimeline`,否则朋友圈菜单不可视或分享链路不完整 -- [ ] 若调用 `wx.showShareMenu({ menus })`,包含 `shareTimeline` 时必须同时包含 `shareAppMessage` -- [ ] 朋友圈打开进入小程序单页模式,不等同于完整小程序会话 -- [ ] 不允许诱导分享、强制扩散、奖励传播,不能用样式或文案暗示“必须分享” - -参考入口: - -- `https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share-timeline.html` -- `https://developers.weixin.qq.com/miniprogram/dev/reference/api/Page.html#onShareTimeline` -- `https://developers.weixin.qq.com/miniprogram/dev/api/share/wx.showShareMenu.html` - -## 分享菜单 / 路径 / 标题 / 状态策略 - -- [ ] 详情页加载时只暴露系统分享菜单,不新增自制“发朋友圈”按钮或强引导弹层 -- [ ] 系统菜单检查包含 `shareAppMessage` 与 `shareTimeline` 两项,且没有只开朋友圈、关闭好友分享的配置 -- [ ] `onShareAppMessage` 继续返回完整 `path`,至少包含详情页路径、任务 `id` 和最小来源参数 -- [ ] `onShareTimeline` 返回 `query`,至少包含任务 `id` 和 `from=timeline` / 等价来源参数;不得尝试返回或拼接自定义 `path` -- [ ] `query` 只携带必要字段,不包含用户隐私、位置精度过高数据、评论全文或不可公开的上下文 -- [ ] 标题由任务标题、分类或地点摘要生成,优先表达“这里有一条附近线索 / 任务”,不承诺结果 -- [ ] active 且低风险任务可使用中性求助语气;`stale`、高 `staleCount`、高 `reportCount`、`resolved`、`expired`、`hidden` 必须收敛为查看线索、状态已变化或不可扩散语义 -- [ ] 分享图若使用任务图片,必须确认是可公开内容;无图或风险态使用默认图 / 安全占位,不用夸张营销图 -- [ ] 找不到任务、无权限、已隐藏或数据加载失败时,标题和落地页都走保底文案,不生成扩散诉求 - -## 单页模式风险检查 - -- [ ] 使用场景值 1154 或等价方式识别朋友圈单页模式,并有适配分支或明确的只读降级 -- [ ] 单页模式下 tabBar 不渲染,详情页不能依赖底部 tab 才能理解当前位置、返回地图或找到主要内容 -- [ ] 单页模式无登录态,不能要求登录后才显示任务标题、地点、正文、图片、状态和关键提示 -- [ ] 本地存储与普通模式不共用,不能依赖本地登录用户、已读状态、草稿或本地 reactions 才能展示核心详情 -- [ ] 路由跳转能力受限,`navigateTo`、`switchTab`、`reLaunch` 等失败时不能阻断阅读;跳转动作需有“前往小程序后继续”的降级语义 -- [ ] 位置、授权、联系人、媒体选择、电话、剪贴板、支付等受限 API 不应在落地页首屏自动触发 -- [ ] 地图、图片、评论和状态模块在单页模式下不重叠微信固定顶部导航栏与底部操作栏 -- [ ] 无登录、无定位、云资源未开启未登录访问或接口失败时,仍能读到任务静态信息和当前状态,不出现空白页 -- [ ] 文案不暗示“在朋友圈落地页一定能确认、评论、定位、联系或完成任务”;可提示“打开小程序后继续处理” - -## 风险任务互斥 - -- [ ] `hidden` 任务不出现在普通分享目标中;若被历史 query 打开,只显示不可查看 / 已下线语义 -- [ ] `resolved` 任务不使用“帮忙扩散”“还需要人来”等求助标题,只展示“已处理 / 可查看结果” -- [ ] `expired` 任务不使用紧急扩散语气,只展示“已过期 / 仅作历史线索” -- [ ] `stale` 或 `staleCount >= 3` 不出现请转发、请确认、马上帮忙等动作召唤 -- [ ] `reportCount > 0` 的任务标题和落地提示要降级谨慎;达到隐藏阈值时不允许公开扩散 -- [ ] 丢失招领类任务避免暴露高价值物品、精确交付方式或私人联系方式;朋友圈标题只保留必要线索 -- [ ] 评论数、确认数、附近距离等信号只能用于可信度说明,不能包装成“很多人都在转 / 你也要转” -- [ ] 风险态互斥优先级高于 viral / receiver / relay prompt:任何传播建议都不能覆盖关闭态和风险态 - -## 文案质量与合规 - -- [ ] 标题建议控制在 12-28 个中文字符,最长不超过一行主要展示区;超长任务标题需截断或摘要化 -- [ ] 中文语气克制、生活化、可核对,不使用夸张标点、全角感叹号堆叠或营销腔 -- [ ] 禁用诱导词:`帮我扩散`、`求转发`、`转发有奖`、`必须分享`、`马上刷屏`、`转到所有群`、`不转就错过` -- [ ] 禁用确定性承诺:`一定找到`、`官方认证`、`真实有效`、`已核实无误`,除非页面有对应证据且仍需谨慎表达 -- [ ] 不使用奖励、排行榜、倒计时、稀缺名额等刺激传播的文案或视觉样式 -- [ ] 分享标题、详情首屏、状态提示、错误态口径一致,不出现标题鼓励扩散、页面提示谨慎的冲突 -- [ ] 风险提示不应压过任务正文;但在争议、过期、已解决状态下必须在首屏可见 - -## 手动 DevTools / 真机验收 - -- [ ] WeChat DevTools 基础库不低于支持朋友圈分享的版本;记录基础库、微信版本、设备模拟器和日期 -- [ ] 打开 active 低风险任务详情页,右上角菜单能看到“发送给朋友”和“分享到朋友圈” -- [ ] 触发“发送给朋友”,检查 `onShareAppMessage` 的标题、完整 `path`、任务 `id`、来源参数和分享图 -- [ ] 触发“分享到朋友圈”,检查 `onShareTimeline` 的标题、`query`、分享图;确认没有自定义 `path` -- [ ] 用朋友圈单页模式入口打开同一任务,确认首屏能读到标题、地点/距离摘要、正文、图片、状态和谨慎提示 -- [ ] 在单页模式下验证 tabBar 缺失不会造成主要信息不可达;微信固定顶部/底部栏不遮挡关键内容 -- [ ] 在单页模式下断开登录态 / 拒绝定位 / 云接口失败时,确认页面不是空白,并出现合适只读降级 -- [ ] 分别构造 `resolved`、`expired`、`stale`、高举报、`hidden` 任务,确认菜单、标题和落地页均不鼓励扩散 -- [ ] 真机 Android 与 iOS 至少各跑一条 active 低风险任务和一条关闭 / 风险任务;如平台能力不可用,记录原因 -- [ ] 截图或录屏需覆盖:菜单项、朋友圈分享预览、单页模式首屏、风险态降级 -- [ ] 若没有 DevTools service port、真机、账号权限或截图证据,验收状态只能写 `blocked` / `unverified`,不得写 `passed` - -## 自动检查建议 - -- [ ] `node --check pages/detail/detail.js` -- [ ] `node --check utils/share-message.js` 或实际新增的分享 helper -- [ ] `node scripts/check-json.mjs` -- [ ] `node harness/check-harness.mjs` -- [ ] `git diff --check` -- [ ] 如新增脚本,覆盖 `onShareTimeline` query、状态互斥、禁用诱导词和 `showShareMenu` 菜单组合 diff --git a/harness/viral-timeline-share-product-brief.md b/harness/viral-timeline-share-product-brief.md deleted file mode 100644 index 46e6a7d..0000000 --- a/harness/viral-timeline-share-product-brief.md +++ /dev/null @@ -1,135 +0,0 @@ -# 朋友圈分享产品 Brief - -日期:2026-06-17 - -## 背景 - -U 轮已经把 `from=share` 接收者完成 `confirm` 或 `comment` 后的二跳提示做到接近可用上限:页面会展示 `targetRows`、`relayChannels`、`shareReason`,并通过 `from=share&source=receiver&receiverAction=confirm/comment` 保留二跳路径。用户评测 99/100 的主要缺口不是“还不知道转给谁/哪里”,而是仍然没有新增真实传播渠道,也没有系统分享面板和真实分享 payload 的证据。 - -V 轮探索在合规边界内补上微信原生“分享到朋友圈”。这不是替代 U 轮接收者接力,而是在详情页为低风险 active 任务增加一个更外层、弱关系、异步曝光的入口。 - -## 官方约束 - -- 朋友圈分享从基础库 `2.11.3` 开始支持;低版本必须退化为仅发送给朋友。 -- 页面必须同时允许“发送给朋友”即 `Page.onShareAppMessage`,和“分享到朋友圈”即 `Page.onShareTimeline`,才可分享到朋友圈。 -- `wx.showShareMenu` 的 `menus` 若展示 `shareTimeline`,也必须展示 `shareAppMessage`;禁止只打开朋友圈菜单。 -- `onShareTimeline` 只能返回 `title`、`query`、`imageUrl`、`promise`,不支持自定义 `path`。朋友圈打开是小程序单页模式,`tabBar` 不渲染,很多能力受限,不适合承载强交互流程。 -- 不做诱导分享、强制分享、奖励分享;不做联系人或群选择器;不自动触发分享面板。 - -## 产品假设 - -朋友圈可能带来自发裂变,是因为它降低了“选择具体接收人”的成本。附近任务有一类天然适合弱关系曝光:失物、地点动态、临时求助、路况或门店状态。发布者或接收者不一定知道该发给谁,但朋友圈里的邻居、同路线朋友、刚经过的人,可能自然补一条确认、评论或再转给更合适的人。 - -它和 U 轮 receiver relay 的差异很明确: - -- U 轮是高意图、窄范围、动作后的二跳:接收者已经完成 `confirm/comment`,页面再建议转给哪类人、怎么说。 -- V 轮是低意图、宽曝光、系统渠道补齐:用户可以直接通过微信原生菜单发到朋友圈,获得真实渠道和 payload 证据。 -- U 轮解决“转给谁/怎么说”的决策成本;V 轮解决“是否存在一个真实可用的外部传播面”的渠道成本。 -- V 轮不应把朋友圈当成更强的二跳转化页。朋友圈入口更适合让下一位先读懂任务、看状态和评论,再决定是否行动。 - -## 推荐实现范围 - -### 入口范围 - -- 只在任务详情页开放朋友圈分享,不扩到地图、发布页、个人页或活动页。 -- 仅低风险 `active` 任务展示 `shareTimeline` 菜单:`status === 'active'`,`staleCount === 0`,`reportCount === 0`。 -- `stale`、弱 stale、弱 report、`resolved`、`expired`、`hidden`、状态缺失或远端刷新失败时,不展示朋友圈入口;可以保留发送给朋友,但文案必须谨慎。 -- 若基础库低于 `2.11.3` 或环境不支持 `onShareTimeline`,退化为现有 `onShareAppMessage`。 - -### 菜单与生命周期 - -- 详情页继续实现 `onShareAppMessage`,并新增 `onShareTimeline`。 -- 只有决定开放朋友圈时,调用 `wx.showShareMenu({ menus: ['shareAppMessage', 'shareTimeline'] })`。 -- 不开放朋友圈时,不要在 `menus` 中放 `shareTimeline`;如需要保留现有分享,只展示 `shareAppMessage`。 -- 菜单显隐应基于当前加载后的最新任务状态,而不是只看入口 query。 - -### Timeline query - -`onShareTimeline` 不支持自定义 `path`,因此详情页分享必须依赖当前详情页路径和返回的 `query`。推荐最小 query: - -```text -id=&from=share&shareChannel=timeline -``` - -如果当前页面本身来自 U 轮接收者二跳,且用户已经完成 `confirm/comment`,可以在 query 中保留既有语义: - -```text -id=&from=share&source=receiver&receiverAction=&shareChannel=timeline -``` - -规则: - -- `from=share` 继续作为“分享入口”的通用门,复用现有接收侧引导。 -- `shareChannel=timeline` 只用于区分朋友圈来源,不改变 `source=receiver` 和 `receiverAction` 的含义。 -- query 不携带联系人、群、用户昵称、头像、手机号、精确坐标或评论正文。 -- 下一位从朋友圈打开后,仍先走现有 `from=share` 接收者逻辑;只有完成 `confirm/comment` 后,才出现 U 轮的 `targetRows`、`relayChannels`、`shareReason`。 - -### 标题与图片策略 - -标题只表达“需要核对/有线索/看最新评论”,不表达事实保证。 - -建议标题: - -- 普通低风险 active:`附近有一条信息需要核对` -- `lost_found`:`附近有失物线索,帮忙核对` -- `help_needed`:`附近有人求助,看看能否帮上` -- `street_update`:`附近有地点动态,帮忙看是否仍有效` -- `check_in`:`附近有地点反馈,帮忙补充一下` -- 接收者刚确认后:`刚有人确认,帮忙再核对` -- 接收者刚评论后:`有新线索,先看最新评论` - -避免标题: - -- `已确认属实,快转朋友圈` -- `平台已验证,放心扩散` -- `帮我转发领福利` -- `必须转发给附近的人` - -`imageUrl` 优先使用安全的分类默认图或应用默认分享图。含隐私风险的失物照片、个人头像、聊天截图、可识别住址门牌等,不应作为朋友圈卡片图。没有安全图片时可以不返回 `imageUrl`。 - -### 单页模式处理 - -朋友圈打开后是小程序单页模式,不能假设 `tabBar` 存在,也不能把它当完整 app 首页。详情页在这个模式下至少要做到: - -- 标题、地点名、状态、确认/过时/举报信号、评论摘要可读。 -- 风险或关闭状态优先可见,不被分享引导覆盖。 -- 不依赖 `switchTab`、复杂跨页跳转、发布流程或登录后强交互才能理解任务。 -- 若某些互动能力在单页模式受限,文案应引导“先核对状态和评论”,而不是承诺一定能直接完成强交互。 - -## 成功标准 - -- 自动检查:详情页同时存在 `onShareAppMessage` 和 `onShareTimeline`;任何包含 `shareTimeline` 的 `wx.showShareMenu` 配置也包含 `shareAppMessage`。 -- 自动检查:`onShareTimeline` 返回值只使用 `title/query/imageUrl/promise`,不返回 `path`。 -- 自动检查:timeline query 至少包含 `id`、`from=share`、`shareChannel=timeline`;接收者二跳场景可保留 `source=receiver&receiverAction=confirm/comment`,但不得新增联系人、群或用户身份字段。 -- 自动检查:弱 stale、弱 report、`stale/resolved/expired/hidden`、状态缺失或刷新失败时不暴露 `shareTimeline` 菜单,也不生成鼓励性朋友圈标题。 -- 自动检查:标题不包含诱导、奖励、事实保证或强制扩散词,例如“属实、已验证、可靠、放心转发、领福利、必须转发、扩散有奖”。 -- 手动评测:基础库 `2.11.3+` 的 DevTools 或真机里,低风险 active 详情页系统菜单同时出现“发送给朋友”和“分享到朋友圈”。 -- 手动评测:从朋友圈卡片打开后进入当前详情页单页模式,`tabBar` 不渲染也能读懂任务、状态和评论线索,页面不把用户卡在需要多页导航的流程里。 -- 手动评测:U 轮链路不回退;`from=share` 接收者完成 `confirm/comment` 后仍展示 `targetRows`、`relayChannels`、`shareReason`,二跳发送给朋友路径仍保留 `source=receiver&receiverAction`。 -- 手动评测:真实分享证据能记录菜单截图、分享卡片标题、打开后的 query 或调试日志;若环境不能真实发朋友圈,必须标注为 DevTools 辅助验证,不得算作真机通过。 - -## 边界和反模式 - -- 不诱导分享:不写“帮我扩散一下”“转发后更多人帮你”“分享到朋友圈让任务更快解决”等压力文案。 -- 不奖励分享:不做积分、徽章、优先展示、解锁功能、抽奖或任何转发收益。 -- 不强制分享:发布、确认、评论、查看评论、关闭弹窗都不能以分享到朋友圈为前置条件。 -- 不自动触发:不在 `confirm/comment` 后自动拉起分享面板,不做倒计时、弹窗轰炸或默认勾选。 -- 不做联系人或群选择器:不读取、不推断、不展示具体联系人、微信群、群名、头像或“你的某某群”。 -- 不泄露隐私:朋友圈标题、query、卡片图不带发布者身份、接收者身份、评论正文、联系方式、精确门牌或敏感图片。 -- 不把 timeline 当完整交互页:朋友圈入口首先是可读详情,不承诺完整地图、tabBar、发布、管理或复杂登录链路可用。 -- 不让风险任务继续扩散:弱 stale/report 也要停止朋友圈鼓励;closed 状态只允许结果说明,不出现“继续转发/帮忙扩散”。 -- 不用朋友圈替代定向接力:U 轮的 `relayChannels` 仍是“转给哪类人”的建议,朋友圈只是新增系统渠道。 - -## 用户评测重点:如何比较 U 与 V - -评测 U 时,重点看接收者完成行动后是否知道“转给谁、为什么、怎么说”。评测 V 时,重点要换成“是否真的多了一个合规的系统传播渠道,并且没有破坏 U 的接力链路”。 - -建议评测顺序: - -1. 先跑 U 轮基线:从 `/pages/detail/detail?id=&from=share` 进入,分别完成 `confirm` 和 `comment`,确认 `targetRows`、`relayChannels`、`shareReason` 和二跳 `receiverAction` 没有回退。 -2. 再看 V 轮新增:同一低风险详情页打开系统菜单,必须看到“发送给朋友”和“分享到朋友圈”同时存在,并记录证据。 -3. 检查朋友圈 payload:标题克制,query 有 `id/from=share/shareChannel=timeline`,无自定义 `path` 假设,无联系人/群/用户身份字段。 -4. 从朋友圈入口打开详情页或用等价调试入口模拟,确认单页模式下内容可读、风险优先、无 tabBar 也不破版。 -5. 用 stale、弱 report、resolved、expired、hidden 任务做反向评测,确认朋友圈入口消失或转为非鼓励语义。 - -核心判定:U 是“更会建议接力”,V 必须证明“真的多了朋友圈这个渠道”。如果 V 只是把页面文案写成“可以发朋友圈”,但没有 `onShareTimeline`、没有菜单证据、没有真实 query 证据,应判为没有补上本轮缺口。 diff --git a/knowledge/AGENTS.md b/knowledge/AGENTS.md new file mode 100644 index 0000000..6464bc7 --- /dev/null +++ b/knowledge/AGENTS.md @@ -0,0 +1,35 @@ +# 知识库维护约定 + +本目录是 Street Tasks 的项目知识库,记录当前有效的产品、架构、数据、安全、 +验证和上线知识。它不保存开发会话、模型交接、逐轮证据或个人机器状态。 + +## 文章结构 + +- `index.md` 是唯一入口,所有主题文章都必须从这里可达。 +- 主题文章只描述当前事实;历史变化只在确有决策价值时保留。 +- `log.md` 只记录知识库本身的结构性更新,不记录开发流水账。 +- 文章之间使用 `[[文件名|显示名称]]` 形式的 wikilink。 +- 代码、配置和实时平台状态优先于文档;发现不一致时应同步修正文档。 + +## 状态表达 + +- `已实现`:代码路径存在,相关静态检查已通过。 +- `已验证`:明确写出实际运行过的验证环境和结果。 +- `未验证`:尚未在真实 AppID、CloudBase、WeChat DevTools 或真机确认。 +- `阻塞上线`:缺失会使正式发布不安全或不可验收。 + +禁止用“有脚本”“有模板”替代真实 UI、真机或云端验证结论。 + +## 内容边界 + +知识库应保留: + +- 产品定位、用户旅程、业务规则和非目标。 +- 系统边界、数据模型、隐私、安全和运维契约。 +- 当前能力、已知限制、上线门槛和可复用验证方法。 + +知识库不应保留: + +- AI/开发协作流程、会话编号、交接人或轮次。 +- 重复的产品 brief、证据模板和逐次命令输出。 +- 本地绝对路径、AppID、密钥、用户标识或测试账号。 diff --git a/knowledge/architecture.md b/knowledge/architecture.md new file mode 100644 index 0000000..fd6c4f8 --- /dev/null +++ b/knowledge/architecture.md @@ -0,0 +1,92 @@ +# 系统架构 + +## 技术形态 + +项目是原生微信小程序: + +- 页面:`.js`、`.json`、`.wxml`、`.wxss`。 +- 模块:JavaScript ES Modules。 +- 本地状态:`wx.getStorageSync` / `wx.setStorageSync`。 +- 地图定位:微信 `map` 与 `wx.getLocation`,坐标系 `gcj02`。 +- 共享数据:CloudBase 云函数、数据库和云存储。 +- 构建:无前端框架、无页面 bundler、无小程序 npm 组件依赖。 + +## 分层 + +```text +页面与组件 + pages/* custom-tab-bar/* + │ + ▼ +共享业务模块 + utils/store.js utils/auth.js utils/privacy.js + utils/feedback.js utils/viral-attribution.js + utils/geo.js utils/format.js utils/post-presenter.js + │ + ├──────────────┐ + ▼ ▼ +本地开发模式 CloudBase 共享模式 +wx storage posts / getMyRole 云函数 +mock posts 数据库集合 / Storage / OpenAPI +``` + +页面层不应复制持久化、云端回退、权限或数据清洗逻辑。`utils/store.js` 是任务、评论、 +信任动作和图片上传的主要页面侧边界。 + +## 页面职责 + +| 路径 | 职责 | +| --- | --- | +| `pages/map/*` | 地图、marker、分类筛选、列表抽屉、定位和随机发现 | +| `pages/publish/*` | 发布表单、位置、有效期、图片选择与上传 | +| `pages/detail/*` | 详情、评论、信任动作、关闭、分享和接收者转化 | +| `pages/admin/*` | 管理员任务与评论审核、反馈查看 | +| `pages/me/*` | 登录、资料、统计、下一步建议、管理和隐私入口 | +| `pages/my-posts/*` | 当前用户发布记录 | +| `pages/activities/*` | 当前用户信任动作记录 | +| `pages/feedback/*` | 普通反馈与个人信息权利申请 | +| `pages/agreement/*` | 用户协议工程草案 | +| `pages/privacy/*` | 隐私政策、同意与撤回入口 | + +## 共享模块 + +| 模块 | 稳定职责 | +| --- | --- | +| `utils/config.js` | 应用身份、默认中心、分类、有效期、CloudBase 配置 | +| `utils/store.js` | 任务、评论、反应、处置和图片上传 API | +| `utils/auth.js` | 本地用户、资料、管理员角色刷新和权限 | +| `utils/privacy.js` | 版本化法律同意、位置单独同意和撤回 | +| `utils/feedback.js` | 反馈创建、列表和权利申请送达 | +| `utils/viral-attribution.js` | 分享链路参数、风险判断和最小事件 | +| `utils/geo.js` | 距离计算、格式化和 marker 映射 | +| `utils/format.js` | 分类、状态、时间、有效期和信任摘要文案 | +| `utils/post-presenter.js` | “我的发布”和“动态”的展示模型 | +| `utils/diagnostics.js` | 地图启动诊断,不承载业务状态 | + +## 云端边界 + +`cloudfunctions/posts/index.js` 统一处理: + +- `list`、`get`、`create`。 +- `react`、`resolve`、`hide`。 +- `listComments`、`createComment`、`reportComment`、`hideComment`、 + `listReportedComments`。 +- `createFeedback`、`listFeedback`。 +- `recordViralAttribution`。 +- `prepareUpload`。 + +`cloudfunctions/getMyRole/index.js` 单独处理管理员角色查询。客户端不应直接获得 raw OpenID, +也不应绕过云函数直接读写业务集合。 + +## 本地与云端模式 + +- CloudBase 未启用时,本地 mock 与 `wx` storage 支撑开发和演示。 +- CloudBase 已配置且用户已完成法律同意时,跨用户数据走云函数。 +- 发布、评论、评论举报/处置和个人信息权利申请属于共享事实;云端不可用时必须显式失败, + 不能只在本机显示成功。 +- 分享归因属于 best-effort 指标:先写本地最小事件,云端上报失败不得阻塞详情、评论、 + 确认或分享主路径。 +- 读取路径可有受控缓存或本地降级,但界面必须区分加载失败与空数据。 + +实体与字段见 [[data-model|数据模型与状态规则]],部署资源见 +[[deployment-operations|部署与运行]]。 diff --git a/knowledge/data-model.md b/knowledge/data-model.md new file mode 100644 index 0000000..423efde --- /dev/null +++ b/knowledge/data-model.md @@ -0,0 +1,71 @@ +# 数据模型与状态规则 + +## 任务 Post + +核心字段: + +| 字段 | 含义 | +| --- | --- | +| `id` / `markerId` | 任务字符串 ID / 地图数字 marker ID | +| `title` / `body` | 标题和正文 | +| `category` / `intent` | 分类;失物招领可带 `lost` / `found` | +| `placeName` | 公开地点名称 | +| `latitude` / `longitude` | 公开任务坐标 | +| `imageUrls` | 图片地址;共享图片应为 `cloud://` fileID | +| `status` | `active`、`stale`、`resolved`、`expired`、`hidden` | +| `confirmations` / `lastConfirmedAt` | 确认次数与最近确认时间 | +| `staleCount` / `reportCount` | 过时反馈和任务举报次数 | +| `createdAt` / `expiresAt` | 创建和过期毫秒时间戳 | +| `expiryType` | 普通时长、自定义或 `long_term` | +| `publisherId` | 内部发布者标识,不应直接出现在公共 DTO | +| `publisher` / `publisherAvatarUrl` | 公开展示名称与头像 | + +`listPosts(center)` 派生距离和过期状态,过滤隐藏任务,按状态及发布时间排序,并受 +`config.maxVisiblePosts = 100` 限制。 + +## 任务状态迁移 + +```text +active ──3 次 stale──▶ stale + │ │ + ├──2 次 report────────┴──▶ hidden + ├──到期──────────────────▶ expired + └──发布者/管理员关闭─────▶ resolved +``` + +- `confirm` 增加 `confirmations` 并更新 `lastConfirmedAt`。 +- 同一用户对同一任务的同一动作只能计入一次。 +- `stale` 达到 3 次后任务状态变为 `stale`。 +- 任务举报达到 2 次后状态变为 `hidden`。 +- `hidden` 不出现在普通列表;管理员可在审核面查看。 +- `hidden`、`resolved` 不接受新评论或信任动作;过期任务在用户界面只读。 +- 只有发布者或管理员可以主动关闭任务。 + +## 评论 Comment + +主要字段: + +- `id`、`postId`、`body`。 +- `status`:`visible` 或 `hidden`。 +- `reportCount`、`lastReportedAt`、`lastReportReason`。 +- `authorId`:内部身份;公共 DTO 不返回 raw 标识。 +- `author`、`authorAvatarUrl`、`createdAt`。 +- `hasReported`:面向当前调用者的派生状态。 + +每个任务普通列表最多返回 50 条最新评论。举报原因限定为: + +`spam`、`inappropriate`、`advertising`、`abuse`、`other`。 + +同一用户不能重复举报同一评论;举报达到 2 次自动隐藏。管理员举报队列最多聚合 500 条, +按最近举报时间倒序。云端实现必须校验评论确实属于给定任务,并在事务中完成去重、计数和 +阈值隐藏。 + +## 其他实体 + +- `post_reactions`:按用户、任务和动作去重的信任动作。 +- `feedback_items`:建议、问题、内容体验、个人信息权利申请和其他反馈。 +- `viral_attribution_events`:分享落地、转化和接力的最小归因事件。 +- `admins`:由服务端读取的管理员身份记录。 + +归因字段白名单见 [[sharing-attribution|分享与归因]];公共数据限制见 +[[privacy-security|隐私与安全]]。 diff --git a/knowledge/deployment-operations.md b/knowledge/deployment-operations.md new file mode 100644 index 0000000..d02b76b --- /dev/null +++ b/knowledge/deployment-operations.md @@ -0,0 +1,76 @@ +# 部署与运行 + +## CloudBase 资源 + +正式共享模式需要: + +| 资源 | 用途 | +| --- | --- | +| `posts` | 任务 | +| `post_reactions` | 用户信任动作与去重 | +| `post_comments` | 评论、举报和隐藏状态 | +| `feedback_items` | 反馈与个人信息权利申请 | +| `viral_attribution_events` | 最小分享归因事件 | +| `admins` | 管理员身份 | +| Cloud Storage | 任务图片 | + +客户端不应直接读写这些业务集合,生产安全规则应只允许云函数访问。 + +## 必要索引 + +- `post_comments(postId, status, createdAt desc)`:普通评论列表。 +- `post_comments(reportCount, lastReportedAt desc)`:管理员举报队列。 + +实际索引字段顺序必须以部署后的查询计划验证;缺失索引是部署阻塞,不应被解释为空数据。 + +## 云函数 + +部署: + +- `cloudfunctions/posts` +- `cloudfunctions/getMyRole` + +`posts/config.json` 必须声明: + +- `security.msgSecCheck` +- `security.imgSecCheck` + +部署后权限可能存在缓存延迟。必须分别验证正常文本、`review/risky`、错误码 `87014`、 +权限缺失和图片超限/异常,而不是只验证一次成功请求。 + +## 云存储 + +- 上传路径在 `posts/` 下随机化,不包含稳定用户标识。 +- 保存到任务中的值只使用 `cloud://` fileID。 +- 上传后由云函数下载并执行图片安全检查。 +- 配置容量上限、访问规则和过期任务图片清理策略。 + +## 运行模式 + +| 条件 | 行为 | +| --- | --- | +| CloudBase 未启用 | 使用本地 mock / storage,适合开发 | +| CloudBase 已启用且法律同意有效 | 使用共享云端路径 | +| 法律同意缺失或已撤回 | 停止新的云端业务读取和归因 | +| 内容安全不可用 | 发布/评论明确失败 | +| 管理通道不可用 | 举报/隐藏/审核明确失败 | +| 归因上报失败 | 不阻塞主业务,保留最小本地事件 | + +## DevTools 服务端口 + +仓库保留只读检查和显式恢复工具用于诊断 WeChat DevTools 服务端口。端口不可达只说明当前 +环境无法执行自动 smoke;它不能证明 UI 失败或通过。任何退出/重开 DevTools 的动作都应 +显式选择,默认诊断不得产生副作用。 + +## 上线后监控 + +至少监控: + +- 云函数调用量、错误率、延迟和冷启动。 +- 数据库读写量与索引错误。 +- 存储用量、上传失败和清理任务。 +- 内容安全不可用/风险拦截比例。 +- 用户举报、评论审核积压和反馈。 +- 分享落地、转化和二跳事件的聚合趋势,不采集额外个人信息。 + +完整发布门槛见 [[release-readiness|上线状态与路线图]]。 diff --git a/knowledge/design-ui.md b/knowledge/design-ui.md new file mode 100644 index 0000000..d843ee3 --- /dev/null +++ b/knowledge/design-ui.md @@ -0,0 +1,57 @@ +# 设计与界面约束 + +## 视觉语言 + +界面使用原生微信组件和克制的 TDesign 风格: + +- 主色为深绿色,背景使用暖白/浅米色。 +- 卡片强调信息层级,不堆叠装饰性统计和说明。 +- 主要动作清楚,次要提示短而谨慎。 +- 公共文案以中文为主,不使用平台保证真实性的措辞。 + +详细 token 和组件规范仍以根目录 `DESIGN_SYSTEM.md` 为参考;本篇记录容易引发行为回归的 +产品约束。 + +## 地图原生层 + +微信 `map` 是原生组件,普通 `view` / `button` 叠在其上可能出现层级或点击问题。 + +- 地图上的列表入口、定位和“找一找”使用 `cover-view` / `cover-image`。 +- 折叠态入口位于右上角,使用单行“列表 N”并保持文字居中。 +- 定位与发现工具位于右下角并避让 tabBar。 +- 选中任务卡避让右下工具。 +- 打开列表时缩小地图,让普通任务卡抽屉位于地图下方,而不是覆盖原生地图。 +- 自定义 tabBar 使用普通 `view/image`,避免滚动时出现重复原生层残影。 + +## 列表与卡片 + +- 长标题、长正文、图片、多标签和 footer 同时存在时,卡片不能横向溢出。 +- 列表抽屉需在窄屏、安全区和不同图片数量下保持可滚动。 +- 地图筛选只向视图层发送实际消费的展示数据,原始帖子保留在控制器实例上。 +- 普通地图拖动不应重复重建并传输不变的任务列表。 + +## 发布页 + +- 底部发布主动作在键盘、安全区和长表单下仍可达。 +- 定位中、定位失败、重新定位和已确认位置必须是不同状态。 +- 图片选择、压缩、超限、审核、上传和创建失败分别给出可理解反馈。 +- 不恢复已移除的独立“发布准备度”装饰卡;校验服务于主流程,不制造第二套状态看板。 + +## 详情与信任 + +- 任务状态、风险信号、评论和动作按重要性排列。 +- 发布成功、分享接收、接收者二跳、评论接力、确认接力和普通分享面板互斥。 +- `resolved` / `expired` 页面保持可读,但不暴露会改变关闭任务的动作。 +- 从朋友圈单页模式(scene `1154`)打开时,详情页无 `tabBar`、无登录态且能力受限,必须保持 + 只读可读,不自动触发定位等受限能力。完整规则见 [[sharing-attribution|分享与归因]]。 +- 评论举报入口不能因任务不可评论而隐藏;历史评论仍可能需要治理。 + +## 管理与个人中心 + +- 管理卡直接解释风险原因和建议动作,隐藏与关闭必须使用不同确认文案。 +- 处置后保留当前搜索/筛选上下文,并立即反映新状态。 +- 加载失败、权限失败和空状态不可共用同一文案。 +- “我的”页优先显示下一步建议和最近活动,不重复堆砌无行动价值的统计。 +- 管理员入口不应让普通用户误以为已经获得管理权限。 + +视觉验收需要 WeChat DevTools 和真机,静态 WXML/WXSS 检查只能防止结构性回归。 diff --git a/knowledge/development-verification.md b/knowledge/development-verification.md new file mode 100644 index 0000000..e04712c --- /dev/null +++ b/knowledge/development-verification.md @@ -0,0 +1,71 @@ +# 开发与验证 + +## 本地准备 + +1. 安装 Node.js 20 或兼容版本。 +2. 在仓库根目录运行 `npm ci --ignore-scripts`。 +3. 运行 `npm run check`。 +4. 使用 WeChat DevTools 打开仓库。 +5. 公共 `project.config.json` 保持 `touristappid`;真实 AppID 只写入被忽略的 + `project.private.config.json`。 + +本地模式可使用 mock 数据和 `wx` storage。共享数据、图片、管理员身份和微信内容安全必须 +在真实 CloudBase 环境验证。 + +## 自动检查 + +统一入口: + +```bash +npm run check +``` + +它覆盖: + +- JSON 配置语法。 +- 知识库结构和 wikilink。 +- 发布、详情、信任动作、评论、管理、隐私、内容安全和传播规则。 +- 地图列表布局、生命周期和性能防回归。 +- CloudBase 数据边界与权限相关静态检查。 + +修改 JavaScript 后还应对相关文件运行 `node --check`;提交前运行: + +```bash +git diff --check +``` + +CI 在 push 和 pull request 上执行相同的 `npm run check`。本地检查、CI、分支保护和真实 +小程序验收是四个独立层次,任何一个通过都不能替代其他层。 + +## WeChat DevTools 验收 + +至少覆盖: + +- 首次加载、定位允许/拒绝、地图 marker、列表抽屉、分类和随机发现。 +- 游客登录引导、发布定位重试、四类任务、有效期、自定义时间和必填校验。 +- 无图/带图发布、压缩、上传、安全审核、成功后详情回读。 +- 详情评论、确认/过时/举报去重、关闭态与过期态。 +- 评论举报原因、阈值隐藏、管理员队列与手动隐藏。 +- 好友分享、朋友圈、接收者确认/评论和二跳来源。 +- 隐私协议、位置单独同意、撤回和个人信息权利申请回执。 +- 窄屏、键盘、安全区、长文案、原生地图层和列表滚动。 + +## 真机验收 + +至少一台 iPhone 和一台 Android,覆盖: + +- 冷启动、热启动、前后台切换。 +- 定位允许、拒绝和系统设置变更。 +- 相机/相册、图片压缩和上传。 +- 弱网、断网和云函数失败。 +- 跨用户发布、评论、举报、管理员处置和回读。 +- 不同屏幕、安全区域和微信版本。 + +弱网下可以明确显示读取降级,但发布、评论、举报、管理和权利申请不得出现“仅本机成功” +的假成功。 + +## 证据原则 + +知识库不保存逐轮测试台账。验收结论应附在对应发布、缺陷或变更记录中,并至少包含环境、 +版本、执行动作和实际结果。无法连接 DevTools、没有真实 AppID 或未部署云函数时,状态是 +“未验证”,不是“通过”或“没有问题”。 diff --git a/knowledge/index.md b/knowledge/index.md new file mode 100644 index 0000000..2b1d75b --- /dev/null +++ b/knowledge/index.md @@ -0,0 +1,26 @@ +# Street Tasks 项目知识库 + +Street Tasks(街区任务)是一个地图优先的原生微信小程序,用于发布、浏览和校准 +附近的短时信息。知识库以当前代码和发布约束为准,替代原有的开发协作台账。 + +## 快速入口 + +- [[product-overview|产品定位与范围]]:解决什么问题、当前包含什么、明确不做什么。 +- [[user-journeys|用户旅程]]:浏览、发布、详情、评论、信任动作、个人中心和管理。 +- [[architecture|系统架构]]:页面层、共享模块、CloudBase 和本地模式的职责边界。 +- [[data-model|数据模型与状态规则]]:任务、评论、反馈、信任动作和状态迁移。 +- [[trust-safety|信任、安全与治理]]:内容审核、举报阈值、管理员处置和风险传播。 +- [[privacy-security|隐私与安全]]:同意链路、公开数据边界、权限和密钥管理。 +- [[sharing-attribution|分享与归因]]:分享入口、接收者转化、二跳接力和最小归因事件。 +- [[design-ui|设计与界面约束]]:地图原生层、视觉语言、交互层级和窄屏风险。 +- [[development-verification|开发与验证]]:本地启动、自动检查和必须人工验证的范围。 +- [[deployment-operations|部署与运行]]:CloudBase 集合、索引、云函数、存储和故障边界。 +- [[release-readiness|上线状态与路线图]]:已实现、未验证、阻塞上线和后续能力。 + +## 当前结论 + +代码已经覆盖地图浏览、发布、详情、评论、任务与评论治理、个人中心、反馈、分享传播、 +隐私同意和 CloudBase 数据路径。静态检查不能替代真实环境验收;正式上线仍受真实 +AppID、CloudBase 部署与权限、法律文本、WeChat DevTools 以及双端真机验证阻塞。 + +维护本知识库时遵循 [[AGENTS|知识库维护约定]],结构性变化记录在 [[log|知识库日志]]。 diff --git a/knowledge/log.md b/knowledge/log.md new file mode 100644 index 0000000..9a101a9 --- /dev/null +++ b/knowledge/log.md @@ -0,0 +1,9 @@ +# 知识库日志 + +## 2026-07-28 + +- 从原 `harness/` 中提炼当前有效的产品、架构、数据、安全、隐私、传播、验证和上线知识。 +- 移除开发会话、AI 交接、逐轮 feature evidence、重复 brief 和手测证据模板。 +- 将可执行的产品检查保留在 `scripts/`,知识库不承担测试结果台账职责。 +- 删除 `harness/` 目录及其配套的证据生成/校验脚本;补齐朋友圈单页平台约束和归因 + `user_id_hash` 字段说明;`npm run check` 改为 JSON、知识库结构和产品行为三段检查。 diff --git a/knowledge/privacy-security.md b/knowledge/privacy-security.md new file mode 100644 index 0000000..0f4b5ca --- /dev/null +++ b/knowledge/privacy-security.md @@ -0,0 +1,64 @@ +# 隐私与安全 + +## 同意模型 + +应用将三类同意分开并按版本保存: + +- 用户协议版本。 +- 隐私政策版本。 +- 位置处理目的版本。 + +法律同意必须同时覆盖用户协议和隐私政策。位置属于敏感个人信息,必须在法律同意之后 +单独询问。版本变化后需要重新同意。 + +用户拒绝位置时: + +- 可以使用默认中心浏览本地示例。 +- 不调用定位。 +- 不能发布依赖当前位置的任务。 + +用户撤回后,应用停止新的定位、CloudBase 业务读取和分享归因上报;微信系统权限仍需用户 +在微信设置中单独管理。 + +## 数据公开边界 + +任务本身会向附近用户公开地点名称和精确经纬度,因此发布前必须明确告知。公共 DTO 只 +返回业务展示所需字段,不返回: + +- raw OpenID、UnionID 或内部登录时间。 +- 举报人的身份。 +- 管理员记录。 +- 云端内部状态或异常栈。 + +公开图片路径应随机化,不包含稳定用户哈希。分享归因不能包含评论正文、联系人、群聊、 +精确坐标或原始身份,详见 [[sharing-attribution|分享与归因]]。 + +## 云端访问控制 + +生产集合应禁止客户端直接读写,统一通过云函数做身份、权限、输入和状态校验。 + +- 普通用户不能进入管理员任务或评论审核面。 +- 只有发布者或管理员可以关闭任务。 +- 只有管理员可以隐藏任务、隐藏评论和读取审核队列。 +- 个人信息权利申请只有成功写入云端并获得回执才算送达。 +- 角色校验失败不得降级成管理员,也不得显示为空审核队列。 + +## 本地文件与密钥 + +- `project.config.json` 永远保留公开占位 AppID `touristappid`。 +- 真实 AppID 放在被忽略的 `project.private.config.json`。 +- 不提交 API key、token、cookie、私钥、云函数密钥、用户标识或测试账号。 +- `aaa` 是被忽略的本地文件,可能含代理敏感配置,不得强制加入版本库。 +- 云函数日志不得输出 raw OpenID、请求凭证或完整敏感输入。 + +## 正式上线前的合规缺口 + +仓库内协议和政策是工程草案。上线前必须由实际运营方或法务确认并补齐: + +- 处理者主体和直接联系方式。 +- 各类数据保存期限、删除机制和请求处置时限。 +- CloudBase 实际处理地域和第三方说明。 +- 未成年人规则。 +- 微信公众平台《小程序用户隐私保护指引》及位置、相册、昵称头像声明。 + +这些缺口属于 [[release-readiness|上线阻塞项]],不能以代码完成代替。 diff --git a/knowledge/product-overview.md b/knowledge/product-overview.md new file mode 100644 index 0000000..24b63dc --- /dev/null +++ b/knowledge/product-overview.md @@ -0,0 +1,58 @@ +# 产品定位与范围 + +## 一句话定位 + +Street Tasks(街区任务)把附近的短时信息变成可确认、可更新、可关闭的轻量任务流。 +它不是完整社区平台,而是一个帮助邻近用户快速判断“这里现在发生了什么”的地图工具。 + +## 核心价值 + +- 地图优先:从当前位置或可见地图区域发现信息,不绑定固定城市或服务区。 +- 时间敏感:任务有有效期,并可被标记过时、关闭或隐藏。 +- 结构化校准:确认有效、标记过时、举报和评论比无序聊天更容易形成可信上下文。 +- 低成本闭环:发布者可以关闭任务,管理员可以处理风险任务和被举报评论。 +- 可选共享数据:本地模式便于开发,CloudBase 模式支持跨用户共享。 + +## 当前产品面 + +| 产品面 | 当前能力 | +| --- | --- | +| 地图 | marker、分类筛选、列表抽屉、当前位置、随机发现、任务聚焦 | +| 发布 | 四类任务、失物/拾获方向、位置确认、有效期、最多四张图片 | +| 详情 | 内容与图片、距离与时间、评论、确认/过时/举报、发布者关闭 | +| 分享 | 好友分享、低风险任务朋友圈分享、接收者行动与二跳接力 | +| 我的 | 本地登录、头像昵称、我的发布、参与记录、反馈、隐私入口 | +| 管理 | 管理员鉴权、任务风险队列、搜索筛选、隐藏/关闭、评论审核 | +| 合规 | 用户协议与隐私政策草案、版本化同意、位置单独同意、撤回 | + +完整页面和模块关系见 [[architecture|系统架构]],逐条操作见 +[[user-journeys|用户旅程]]。 + +## 分类与有效期 + +任务分类: + +- `check_in`:打卡。 +- `lost_found`:失物招领,进一步区分 `lost` 和 `found`。 +- `street_update`:地点动态。 +- `help_needed`:求助问答。 + +有效期预设为 1 周、1 月、长期和自定义。长期当前按 10 年时长保存,用于避免默认任务 +短期自动过期;它仍可被关闭、标记过时或举报。 + +## 明确非目标 + +当前版本不计划扩展为: + +- 完整社交网络、私信、群聊、关注或联系人匹配。 +- 多角色审批后台、批量处置或用户封禁系统。 +- 城市级推荐系统和无限量地图数据。 +- 自动认定内容真实的平台背书。 +- 广告平台、订阅消息或复杂商业化体系。 + +## 成功标准 + +首版应能让用户完成“发现附近任务 → 查看上下文 → 采取一个校准动作 → 信息被更新或 +关闭”的闭环,同时确保风险内容、敏感位置、用户身份和云端失败不会被静默掩盖。 + +上线门槛见 [[release-readiness|上线状态与路线图]]。 diff --git a/knowledge/release-readiness.md b/knowledge/release-readiness.md new file mode 100644 index 0000000..9d111b0 --- /dev/null +++ b/knowledge/release-readiness.md @@ -0,0 +1,63 @@ +# 上线状态与路线图 + +## 已实现但仍需真实环境验收 + +- 地图浏览、分类、列表抽屉、定位降级和随机发现。 +- 四类任务发布、有效期、图片约束和发布后详情。 +- 评论、确认/过时/举报、发布者关闭和重复动作防护。 +- 评论举报、2 次自动隐藏、管理员举报队列和手动隐藏。 +- 文本/图片内容安全 fail closed 路径。 +- 用户协议/隐私政策工程页面、版本化同意、位置单独同意和撤回。 +- 公共 DTO 白名单、随机图片路径和最小分享归因字段。 +- 好友分享、低风险朋友圈、接收者行动和二跳接力。 +- 个人中心、我的发布、参与记录、反馈和管理页。 + +“已实现”只表示代码和静态检查存在,不表示 CloudBase、DevTools 或真机已经通过。 + +## 阻塞正式上线 + +### 平台与部署 + +- 注册/认证小程序并取得真实 AppID。 +- 绑定 CloudBase 环境,创建 6 个集合、生产安全规则和评论组合索引。 +- 部署 `posts` / `getMyRole`,安装依赖并验证运行时。 +- 开通云存储并验证上传、读取和清理策略。 +- 验证内容安全 OpenAPI 权限及缓存生效。 + +### 合规 + +- 运营方/法务确认正式用户协议和隐私政策。 +- 补齐主体、联系方式、保存期限、删除机制、处理地域和未成年人规则。 +- 在公众平台配置并审核小程序隐私保护指引及相关权限声明。 +- 建立内容审核、举报响应、个人信息请求和应急处置 SOP。 + +### 验收 + +- WeChat DevTools 全流程。 +- iPhone 与 Android 真机。 +- 真实 CloudBase 跨用户读写、权限、并发举报和分页。 +- 弱网/无网失败语义。 +- 好友/朋友圈真实分享与归因读回。 +- 窄屏、键盘、安全区、地图原生层和图片边界。 + +详细步骤见 [[development-verification|开发与验证]]。 + +## 上线前性能风险 + +- 普通列表和 marker 上限为 100;高密度区域需验证是否足够。 +- 地图尚无聚合标记、城市级分页和服务端地理查询。 +- 图片上传超时、失败重试和过期图片清理需线上环境验证。 +- 云函数冷启动和审核 API 延迟可能影响发布体验。 + +## 后续路线图 + +在核心闭环被真实使用验证之后再考虑: + +- 评论作者删除、申诉和更完整的审核工作流。 +- 保存地点、订阅消息和完整账户中心。 +- 地图聚合、分页和高密度查询。 +- `mediaCheckAsync` 异步内容安全迁移。 +- `miniprogram-ci` 自动上传与体验版发布。 +- 更系统的 `utils/store.js` 行为测试和小程序端到端测试。 + +路线图项目不是当前上线完成声明的一部分。 diff --git a/knowledge/sharing-attribution.md b/knowledge/sharing-attribution.md new file mode 100644 index 0000000..de9d781 --- /dev/null +++ b/knowledge/sharing-attribution.md @@ -0,0 +1,107 @@ +# 分享与归因 + +## 设计目标 + +分享帮助短时信息触达可能在现场的人,但不能把社区信号包装成真实性保证,也不能借分享 +读取或推断联系人、群聊和社交关系。 + +## 四种上下文 + +| 上下文 | 入口 | 主要界面 | +| --- | --- | --- | +| 发布成功 | `from=publish` | 发布者专属扩散提示 | +| 普通详情 | 无特殊来源 | 谨慎的好友分享入口 | +| 分享接收 | `from=share` | 先看状态与评论,再确认或补线索 | +| 接收者二跳 | `source=receiver` | 动作成功后的转述理由、场景建议和接力 | + +这些提示互斥展示,避免同屏出现多个竞争性传播 CTA。发布者专属状态不会继续传给接收者。 + +## 分享渠道 + +- 好友分享:低风险详情可用,路径至少包含任务 `id` 和 `from=share`。 +- 朋友圈:仅低风险 `active` 任务开放,使用 `source=timeline` 和 + `shareChannel=timeline` 区分渠道。 +- 风险、关闭、过期、隐藏、未知或加载失败的任务不开放鼓励性朋友圈/二跳入口。 + +## 朋友圈平台约束 + +朋友圈分享受微信平台能力限制,属于产品必须遵守的工程边界: + +- 朋友圈从基础库 `2.11.3` 起支持;低版本或不支持 `onShareTimeline` 的环境必须退化为 + 仅好友分享。 +- 详情页必须同时实现 `onShareAppMessage` 和 `onShareTimeline`。`wx.showShareMenu` 的 + `menus` 若展示 `shareTimeline`,就必须同时展示 `shareAppMessage`,禁止只开朋友圈菜单。 +- 菜单显隐基于加载后的最新任务状态,而不是只看入口 query;只有确认开放朋友圈才传 + `menus: ['shareAppMessage', 'shareTimeline']`。 +- `onShareTimeline` 只能返回 `title`、`query`、`imageUrl`、`promise`,不支持自定义 + `path`。任务 `id`、来源和渠道只能挂在 `query` 上。 +- 朋友圈打开是小程序单页模式(scene `1154`):`tabBar` 不渲染、无登录态、本地存储隔离、 + 定位/联系人/媒体等能力受限。详情页在该模式下必须保持只读可读,不得自动触发受限能力或 + 把用户困在多页流程中。 +- 朋友圈卡片 `imageUrl` 应使用安全的分类默认图或应用默认分享图;失物照片、个人头像、 + 聊天截图、可识别门牌等隐私风险图片不得作为卡片图,没有安全图片时可不返回 `imageUrl`。 + +## 接收者行动与二跳 + +低风险分享接收者可直接: + +- 确认任务仍有效。 +- 补充一条评论线索。 + +只有动作真正成功后才出现二跳提示。重复动作、失败、被拦截或风险态不能显示“已成功” +语义。二跳路径可携带 `receiverAction=confirm` 或 `receiverAction=comment`,让下一位用户 +理解上一跳发生了什么。 + +场景建议是“可能适合转给现场附近的人”等泛化建议,不是联系人推荐。禁止在路径或事件中 +加入 `contactId`、`groupId`、`openId`、好友列表、群名或受众身份。 + +## 归因链路 + +客户端使用: + +- `share_id`:当前分享节点。 +- `parent_share_id`:上一个分享节点。 +- `share_depth`:`1`、`2` 或 `2_plus`。 +- `attribution_session_id`:本次落地会话。 + +事件类型: + +- `share_detail_landing` +- `share_detail_loaded` +- `share_detail_blocked` +- `share_confirm_success` +- `share_comment_success` +- `share_relay_intent` +- `share_relay_success` + +允许的业务字段只包括事件类型/时间、会话和分享 ID、任务 ID/分类/粗粒度状态、来源与渠道、 +转化动作、结果、阻塞原因、是否发布者、粗粒度距离桶和应用版本。 + +可选的 `user_id_hash` 是对现有用户 ID 的不可逆哈希(如 `u_hash_` 前缀),仅用于粗粒度去重, +不得写入 raw OpenID、UnionID 或可反推的原始身份。 + +严禁记录: + +- 评论正文、任务正文或公开联系方式。 +- 联系人、群聊、群成员或分享目标。 +- raw OpenID、UnionID 或内部用户 ID。 +- 精确经纬度;距离只能使用 `0_500m`、`500m_1km`、`1km_3km`、`3km_plus`。 + +## 失败语义 + +归因是 best-effort 指标,不得阻塞详情、评论、确认或分享。客户端可先保存最多 100 条本地 +最小事件;云端上报失败不能显示为主业务失败,也不能扩大采集字段来“补证据”。 + +## 人工验收旅程 + +真实 WeChat 环境至少覆盖: + +1. 首跳好友分享与 `from=share` 落地。 +2. 接收者确认成功与二跳 `receiverAction=confirm`。 +3. 接收者评论成功与二跳 `receiverAction=comment`。 +4. 二跳来源、父子 share ID 和深度连续。 +5. 普通详情与风险态入口互斥。 +6. 朋友圈渠道 payload 和单页落地语境。 +7. 风险任务不暴露朋友圈或鼓励性接力。 + +这些旅程当前属于 [[release-readiness|未验证项]]。 diff --git a/knowledge/trust-safety.md b/knowledge/trust-safety.md new file mode 100644 index 0000000..f6c107c --- /dev/null +++ b/knowledge/trust-safety.md @@ -0,0 +1,70 @@ +# 信任、安全与治理 + +## 信任不是平台背书 + +确认次数、过时反馈、举报、评论和最近确认时间都是社区信号,不代表平台证明内容真实。 +展示文案应使用“确认信号”“待核对”“可能过时”等表达,避免“官方确认”“可靠” +“放心转发”等保证性措辞。 + +冲突信号的优先级: + +1. 已关闭、已过期或已隐藏。 +2. 多次举报。 +3. 达到过时阈值。 +4. 确认信号。 +5. 评论线索或中性提示。 + +## 文本内容安全 + +CloudBase 模式在创建任务和评论前调用微信 `msgSecCheck` v2: + +- 任务标题和正文使用场景 `3`。 +- 评论使用场景 `2`。 +- OpenID 只能来自云函数可信上下文。 +- 只有 `result.suggest === "pass"` 放行。 +- `review`、`risky` 和错误码 `87014` 作为风险内容拒绝。 +- API 错误、权限错误、格式异常或空结果按审核不可用处理,采用 fail closed。 + +本地开发模式有基础关键词拦截,但它不能替代微信内容安全服务。 + +## 图片内容安全 + +当前同步 `imgSecCheck` 路径的边界: + +- 最多 4 张。 +- 仅 JPG/JPEG/PNG。 +- 每张小于 1MB。 +- 短边不超过 750,长边不超过 1334。 +- 类型或尺寸未知即拒绝。 +- 云函数下载 `cloud://` 文件后复查 Buffer 大小和类型,再调用安全 API。 +- API、下载、权限或审核失败时发布失败,不保存本地临时路径。 + +同步接口长期需要迁移到 `mediaCheckAsync`;迁移前不能放宽现有限制。 + +## 任务治理 + +- 用户可确认、标记过时或举报任务,每类动作按用户去重。 +- 3 次过时反馈使任务进入 `stale`。 +- 2 次任务举报使任务进入 `hidden`。 +- 管理员可搜索、筛选、关闭或隐藏任务。 +- 隐藏是风险处置,关闭是任务完成;确认文案必须清楚区分影响。 + +## 评论治理 + +- 登录用户可选择 5 种原因举报评论。 +- 同一用户只计一次。 +- 2 次举报自动隐藏。 +- 管理员可查看举报原因、次数、最近举报时间并手动隐藏。 +- 云端不可用时,举报和隐藏不得只写本地。 +- 加载失败必须与“没有举报评论”分开显示。 + +## 传播风险门槛 + +仅低风险 `active` 任务显示鼓励性转发、二跳理由或朋友圈能力。以下情况关闭传播鼓励: + +- `hidden`、`resolved`、`expired`。 +- `stale`。 +- 存在过时或举报信号。 +- 任务不存在、加载失败或状态未知。 + +治理部署和人工验收项见 [[release-readiness|上线状态与路线图]]。 diff --git a/knowledge/user-journeys.md b/knowledge/user-journeys.md new file mode 100644 index 0000000..86d7f1c --- /dev/null +++ b/knowledge/user-journeys.md @@ -0,0 +1,71 @@ +# 用户旅程 + +## 浏览附近任务 + +1. 用户进入地图页。 +2. 已完成法律同意且同意定位时,应用请求 `gcj02` 位置并移动地图。 +3. 拒绝定位或定位失败时,地图使用默认中心;这不等于获得发布位置。 +4. 用户可按分类筛选、打开列表抽屉、点击 marker,或使用“找一找”聚焦附近任务。 +5. 普通列表过滤 `hidden`,按状态与发布时间排序,最多展示 100 条。 + +地图采用微信原生 `map` 层。覆盖在地图上的折叠入口、定位和发现按钮必须使用 +`cover-view` / `cover-image`;列表打开后地图缩到约 `38vh`,避免普通视图与原生层 +叠放不稳定。更多约束见 [[design-ui|设计与界面约束]]。 + +## 发布任务 + +1. 游客先完成协议/政策同意和登录。 +2. 选择分类;失物招领还需选择“我丢了”或“我捡到”。 +3. 填写标题、正文、地点和有效期。 +4. 单独同意位置处理后获取当前位置;拒绝位置时不可发布。 +5. 可选择最多 4 张 JPG/JPEG/PNG 图片,每张小于 1MB,尺寸不超过 + 750×1334,未知类型或尺寸直接拒绝。 +6. 已启用 CloudBase 时,文本和图片必须通过内容安全检查并成功云端落库;失败不可伪装 + 为本地发布成功。 +7. 发布成功后进入详情页,并出现仅面向发布成功上下文的分享提示。 + +## 查看详情与校准信息 + +详情页展示任务内容、图片、发布者展示信息、地点、距离、时间、状态和评论。用户可: + +- `confirm`:确认信息仍有效。 +- `stale`:认为信息可能已过时。 +- `report`:举报任务。 +- 评论:补充线索或现场信息。 +- 关闭:仅发布者或管理员可将任务设为 `resolved`。 + +同一用户不能重复执行同一任务的同一种信任动作。`hidden`、`resolved` 不再接受评论或 +信任动作;过期任务在界面上只读。详细阈值见 [[data-model|数据模型与状态规则]]。 + +## 评论举报 + +登录用户可对评论选择垃圾信息、不当内容、广告、辱骂或其他原因。单个用户不能重复举报 +同一评论;达到 2 次举报后评论自动隐藏。管理员可查看最多 500 条举报队列并手动隐藏。 +评论治理仍需真实 CloudBase 并发、分页、索引与跨设备回读验证。 + +## 分享接收与二跳 + +低风险 `active` 任务可分享给好友;满足风险门槛时也可分享到朋友圈。通过分享进入详情的 +接收者会先看到来源说明,再执行确认或评论。接收者完成动作后,可以获得谨慎的二跳转述 +理由和泛化场景建议,但应用不读取联系人、微信群或群成员,也不生成定向用户参数。 + +风险态、关闭态、过期态或存在过时/举报信号时,鼓励性传播入口被隐藏。完整规则见 +[[sharing-attribution|分享与归因]]。 + +## 个人中心与反馈 + +“我的”页根据游客、资料未完成、无活动、有处理中发布或有参与记录等状态给出下一步建议。 +用户可以查看自己的发布和信任动作历史,并提交问题、建议、内容体验或个人信息权利申请。 +权利申请只有在云端成功送达并返回回执时才能显示为已提交。 + +## 管理员处理 + +管理员身份由 `getMyRole` 云函数和 `admins` 集合决定。管理页支持: + +- 按待处理、举报、过时、有效、已关闭、已隐藏筛选任务。 +- 按标题、地点、发布者或 ID 搜索。 +- 查看风险原因并隐藏或关闭任务。 +- 查看用户反馈和被举报评论。 +- 手动隐藏评论。 + +无法校验角色、加载失败和空列表是三个不同状态;权限或云端失败不能显示成“没有风险项”。 diff --git a/package.json b/package.json index 0f65025..9e779a3 100644 --- a/package.json +++ b/package.json @@ -5,27 +5,17 @@ "type": "module", "description": "WeChat mini program for short-lived neighborhood tasks.", "scripts": { - "check": "npm run check:json && npm run check:harness && npm run check:readiness", - "check:blocked-summary": "node scripts/check-map-list-blocked-summary-preflight.mjs", - "capture:viral-blocked-evidence": "node scripts/capture-viral-journey-blocked-evidence.mjs", - "check:devtools-recovery-report": "node scripts/check-devtools-recovery-report.mjs", - "check:devtools-recovery-report-preflight": "node scripts/check-devtools-recovery-report-preflight.mjs", + "check": "npm run check:json && npm run check:knowledge && npm run check:readiness", "check:devtools-recovery": "node scripts/check-devtools-recovery.mjs", "check:devtools-port-forensics": "node scripts/check-devtools-port-forensics.mjs", - "check:devtools-ui-confirmation": "node scripts/check-devtools-ui-confirmation.mjs", "check:devtools-smoke": "node scripts/check-devtools-smoke-access.mjs --strict", - "check:harness": "node harness/check-harness.mjs", "check:json": "node scripts/check-json.mjs", - "check:readiness": "node --no-warnings scripts/check-devtools-readiness.mjs", - "check:viral-manual-artifact-manifest": "node scripts/check-viral-manual-artifact-manifest-preflight.mjs", - "check:viral-manual-summary-integrity": "node scripts/check-viral-manual-summary-integrity-preflight.mjs", - "check:viral-journey-evidence-packet": "node scripts/check-viral-journey-evidence-packet.mjs", + "check:knowledge": "node scripts/check-knowledge.mjs", + "check:map-feed": "node --no-warnings scripts/check-map-feed.mjs", + "check:readiness": "node --no-warnings scripts/check-readiness.mjs", + "check:trust-insight": "node --no-warnings scripts/check-trust-insight.mjs", "inspect:devtools-port": "node scripts/inspect-devtools-port-state.mjs", "inspect:devtools-recovery": "node scripts/recover-devtools-service-port.mjs --dry-run", - "prepare:devtools-recovery-report": "node scripts/prepare-devtools-recovery-report.mjs", - "prepare:devtools-ui-confirmation": "node scripts/prepare-devtools-ui-confirmation-run.mjs", - "prepare:viral-journey-evidence-packet": "node scripts/prepare-viral-journey-evidence-packet.mjs", - "prepare:viral-journey-run": "node --no-warnings scripts/prepare-viral-journey-devtools-run.mjs", "recover:devtools-app-quit": "node scripts/recover-devtools-service-port.mjs --app-quit-reopen" } } diff --git a/pages/admin/admin.js b/pages/admin/admin.js index 1ec9244..d4bba6a 100644 --- a/pages/admin/admin.js +++ b/pages/admin/admin.js @@ -1,11 +1,18 @@ import { getCurrentUser, isAdmin, refreshAdminRole } from '../../utils/auth.js'; import { feedbackTypeLabel, listFeedback } from '../../utils/feedback.js'; -import { hidePost, listAllPosts, resolvePost } from '../../utils/store.js'; +import { hideComment, hidePost, listAllPosts, listReportedComments, resolvePost } from '../../utils/store.js'; import { syncTabBar } from '../../utils/tab-bar.js'; import { formatCreatedAt } from '../../utils/format.js'; import { buildAdminReviewState, adminFilterOptions } from './admin-review.js'; const app = getApp(); +const COMMENT_REPORT_REASON_LABELS = { + spam: '垃圾广告', + inappropriate: '内容不当', + advertising: '商业推广', + abuse: '辱骂攻击', + other: '其他' +}; function decorateFeedback(item) { return { @@ -22,17 +29,34 @@ function briefPostTitle(post, fallbackId) { return title.length > 18 ? `${title.slice(0, 18)}...` : title; } +function decorateReportedComment(comment) { + return { + ...comment, + authorText: comment.author || '附近用户', + createdText: formatCreatedAt(comment.createdAt), + reportTimeText: formatCreatedAt(comment.lastReportedAt), + bodyText: String(comment.body || '').trim(), + statusText: comment.status === 'hidden' ? '已隐藏' : '可见', + reportReasonText: COMMENT_REPORT_REASON_LABELS[comment.lastReportReason] || '未记录', + canHide: comment.status !== 'hidden' + }; +} + Page({ data: { authorized: false, query: '', activeFilter: 'needs_review', busyPostId: '', + busyCommentId: '', filterOptions: adminFilterOptions.map((item) => ({ ...item, count: 0 })), posts: [], visiblePosts: [], feedbacks: [], feedbackError: '', + reportedComments: [], + reportedCommentsLoading: false, + reportedCommentsError: '', stats: { total: 0, needsReview: 0, @@ -84,6 +108,29 @@ Page({ stats: reviewState.stats, filterOptions: reviewState.filterOptions }); + this.loadReportedComments(); + }, + + async loadReportedComments() { + this.setData({ + reportedCommentsLoading: true, + reportedCommentsError: '' + }); + try { + const comments = (await listReportedComments()).map(decorateReportedComment); + this.setData({ + reportedComments: comments, + reportedCommentsError: '' + }); + } catch (error) { + console.error('[admin] reported comments list failed', error); + this.setData({ + reportedComments: [], + reportedCommentsError: '评论审核队列读取失败,请检查云函数和 post_comments 集合' + }); + } finally { + this.setData({ reportedCommentsLoading: false }); + } }, applyFilters() { @@ -181,5 +228,49 @@ Page({ await this.runPostAction(postId, () => resolvePost(postId), '已关闭'); } }); + }, + + async runCommentAction(commentId, action, successTitle) { + if (this.data.busyCommentId) { + return; + } + this.setData({ busyCommentId: commentId }); + try { + const updated = await action(); + if (!updated) { + throw new Error('Comment no longer exists or does not match the selected post'); + } + await this.loadReportedComments(); + if (successTitle) { + wx.showToast({ title: successTitle, icon: 'success' }); + } + } catch (error) { + console.error('[admin] comment action failed', error); + wx.showToast({ title: '处理失败,请稍后再试', icon: 'none' }); + } finally { + this.setData({ busyCommentId: '' }); + } + }, + + hideComment(event) { + const commentId = event.currentTarget.dataset.id; + const comment = this.data.reportedComments.find((item) => item.id === commentId); + const body = comment ? comment.bodyText : ''; + const briefBody = body.length > 20 ? `${body.slice(0, 20)}...` : body; + wx.showModal({ + title: '隐藏评论', + content: `隐藏「${briefBody}」\nID:${commentId}\n普通用户不会再看到这条评论。`, + confirmText: '隐藏', + success: async (result) => { + if (!result.confirm) { + return; + } + await this.runCommentAction( + commentId, + () => hideComment(comment ? comment.postId : '', commentId), + '已隐藏' + ); + } + }); } }); diff --git a/pages/admin/admin.wxml b/pages/admin/admin.wxml index 47ca1a1..eee3fe2 100644 --- a/pages/admin/admin.wxml +++ b/pages/admin/admin.wxml @@ -52,6 +52,53 @@ + + + + 被举报评论 + {{reportedComments.length}} 条举报记录 + + + + + + + 评论审核队列读取失败 + {{reportedCommentsError}} + + + + 暂无被举报评论 + + + + + + {{item.statusText}} + 举报于 {{item.reportTimeText}} + + {{item.id}} + + {{item.bodyText}} + + {{item.authorText}} + 举报 {{item.reportCount}} 次 + 最近原因 {{item.reportReasonText}} + 任务 {{item.postId}} + + + + + + + + + 街区任务用户协议 + 版本:{{version}} · 更新日期:2026-07-28 + + + 一、服务说明 + 欢迎使用「街区任务」(以下简称"本服务")。本服务由运营方提供,用于帮助用户在附近发布、确认、更新和关闭短时信息(如失物招领、地点动态、求助问答、打卡等)。 + + + + 二、账号与登录 + 你可以使用微信登录本服务。登录后,你可以发布任务、评论、进行确认/过时/举报等操作。你应当对自己发布的内容负责,不得发布违法、违规、侵犯他人权益的内容。 + + + + 三、用户行为规范 + 你不得利用本服务从事下列行为: + 1. 发布违反法律法规、公序良俗的内容; + 2. 发布虚假信息、骚扰、广告、垃圾信息; + 3. 侵犯他人隐私、名誉、知识产权等合法权益; + 4. 干扰本服务正常运行或试图获取未授权的数据。 + + + + 四、内容审核与处理 + 本地代码已接入微信内容安全接口,正式环境是否生效以真实 AppID、云函数部署和权限验证为准;服务也会对被举报内容进行审核。对违规内容,运营方有权隐藏、删除,并可对相关用户采取限制措施。 + + + + 五、免责声明 + 本服务仅提供信息展示平台,不对用户发布内容的真实性、准确性、完整性作出保证。因依赖任务内容造成的损失,由用户自行承担。 + + + + 六、协议变更 + 运营方可根据法律法规或业务需要修改本协议。版本更新后将重新展示并由你主动确认;不同意时可以停止登录、发布和互动。 + + + + 七、隐私政策 + 个人信息处理规则、公开范围和撤回方式以《隐私政策》为准。 + + + + + + + + diff --git a/pages/agreement/agreement.wxss b/pages/agreement/agreement.wxss new file mode 100644 index 0000000..5a58837 --- /dev/null +++ b/pages/agreement/agreement.wxss @@ -0,0 +1,77 @@ +.legal-page { + min-height: 100vh; + background: #F4F1EA; + padding-bottom: 140rpx; + box-sizing: border-box; +} + +.legal-content { + padding: 32rpx; +} + +.legal-title { + font-size: 40rpx; + font-weight: 600; + color: #173B33; + margin-bottom: 12rpx; +} + +.legal-updated { + font-size: 24rpx; + color: #8A968E; + margin-bottom: 32rpx; +} + +.legal-section { + margin-bottom: 28rpx; +} + +.legal-h { + font-size: 30rpx; + font-weight: 600; + color: #173B33; + margin-bottom: 12rpx; +} + +.legal-p, +.legal-li { + font-size: 28rpx; + line-height: 1.7; + color: #3B4740; + margin-bottom: 8rpx; +} + +.legal-footer { + position: fixed; + left: 0; + right: 0; + bottom: 0; + padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom)); + background: #FFFEFA; + border-top: 1rpx solid #E5E0D4; +} + +.legal-btn { + width: 100%; + height: 88rpx; + line-height: 88rpx; + border-radius: 44rpx; + background: #1F6658; + color: #fff; + font-size: 30rpx; + font-weight: 600; +} + +.legal-link-btn { + margin: 16rpx 0 0; + padding: 0; + background: transparent; + color: #1F6658; + font-size: 28rpx; + line-height: 1.6; + text-align: left; +} + +.legal-link-btn::after { + border: 0; +} diff --git a/pages/detail/detail.js b/pages/detail/detail.js index 6eacdbb..0fd57d2 100644 --- a/pages/detail/detail.js +++ b/pages/detail/detail.js @@ -13,8 +13,10 @@ import { createPostComment, getPost, hasReactedToPost, + hasReportedComment, listPostComments, reactToPost, + reportComment, resolvePost } from '../../utils/store.js'; import { buildDetailShareMessage } from '../../utils/share-message.js'; @@ -45,6 +47,14 @@ import { const MAX_COMMENT_LENGTH = 120; +const COMMENT_REPORT_REASONS = [ + { value: 'spam', label: '垃圾广告' }, + { value: 'inappropriate', label: '内容不当' }, + { value: 'advertising', label: '商业推广' }, + { value: 'abuse', label: '辱骂攻击' }, + { value: 'other', label: '其他' } +]; + function publisherInitial(name) { return (String(name || '').trim().slice(0, 1) || '邻').toUpperCase(); } @@ -52,7 +62,7 @@ function publisherInitial(name) { function decorateDetailPost(raw) { const user = getCurrentUser(); const canReact = raw.status === 'active' || raw.status === 'stale'; - const canResolve = canReact && (isAdmin(user) || raw.publisherId === user.id); + const canResolve = canReact && (isAdmin(user) || raw.isMine); const canShowResolve = canResolve && raw.category !== 'check_in'; const publisherName = String(raw.publisher || '附近用户').trim() || '附近用户'; return { @@ -83,7 +93,8 @@ function decorateComment(raw) { ...raw, authorText: raw.author || '附近用户', createdText: formatCreatedAt(raw.createdAt), - badgeText: raw.isMine ? '我' : (raw.authorRole === 'admin' ? '管理' : '') + badgeText: raw.isMine ? '我' : (raw.authorRole === 'admin' ? '管理' : ''), + reportedByMe: Boolean(raw.reportedByMe || hasReportedComment(raw.id)) }; } @@ -119,6 +130,11 @@ Page({ commentDraftLength: 0, commentSubmitting: false, showCommentDialog: false, + showCommentReportDialog: false, + reportingCommentId: '', + commentReportReason: '', + commentReportReasons: COMMENT_REPORT_REASONS, + commentReporting: false, busyAction: '', resolving: false, maxCommentLength: MAX_COMMENT_LENGTH, @@ -426,6 +442,14 @@ Page({ this.promptLogin(); return; } + if (error && error.code === 'CONTENT_SECURITY') { + wx.showToast({ title: '内容包含不当信息,请修改后重试', icon: 'none' }); + return; + } + if (error && error.code === 'CONTENT_SECURITY_UNAVAILABLE') { + wx.showToast({ title: '内容审核暂不可用,请稍后再试', icon: 'none' }); + return; + } console.warn('[detail] failed to submit comment', error); wx.showToast({ title: error && error.code === 'POST_CLOSED' ? '当前任务暂不能评论' : '评论失败,请稍后再试', @@ -436,6 +460,86 @@ Page({ } }, + openCommentReportDialog(event) { + if (getCurrentUser().isGuest) { + this.setData({ isGuest: true }); + this.promptLogin(); + return; + } + const commentId = event.currentTarget.dataset.id; + if (hasReportedComment(commentId)) { + wx.showToast({ title: '已举报过这条评论', icon: 'none' }); + return; + } + this.setData({ + showCommentReportDialog: true, + reportingCommentId: commentId, + commentReportReason: '' + }); + }, + + closeCommentReportDialog() { + if (this.data.commentReporting) { + return; + } + this.setData({ + showCommentReportDialog: false, + reportingCommentId: '', + commentReportReason: '' + }); + }, + + selectCommentReportReason(event) { + this.setData({ + commentReportReason: event.currentTarget.dataset.value || '' + }); + }, + + async submitCommentReport() { + if (this.data.commentReporting) { + return; + } + if (!this.data.commentReportReason) { + wx.showToast({ title: '请选择举报原因', icon: 'none' }); + return; + } + this.setData({ commentReporting: true }); + try { + const comment = await reportComment( + this.data.id, + this.data.reportingCommentId, + this.data.commentReportReason + ); + const reportingCommentId = this.data.reportingCommentId; + let nextComments = this.data.comments; + if (comment) { + nextComments = this.data.comments.map((item) => ( + item.id === comment.id ? { ...item, ...comment, reportedByMe: true } : item + )).filter((item) => item.status !== 'hidden'); + } else { + nextComments = this.data.comments.filter((item) => item.id !== reportingCommentId); + } + this.setData({ + comments: nextComments, + trustInsight: formatTrustInsight(this.data.post, nextComments.length), + showCommentReportDialog: false, + reportingCommentId: '', + commentReportReason: '' + }); + wx.showToast({ title: '已收到举报', icon: 'success' }); + } catch (error) { + if (error && error.code === 'AUTH_REQUIRED') { + this.setData({ isGuest: true }); + this.promptLogin(); + return; + } + console.warn('[detail] failed to report comment', error); + wx.showToast({ title: '举报失败,请稍后再试', icon: 'none' }); + } finally { + this.setData({ commentReporting: false }); + } + }, + async react(event) { const action = event.currentTarget.dataset.action; if (this.data.busyAction) { diff --git a/pages/detail/detail.wxml b/pages/detail/detail.wxml index eae28f6..b49b365 100644 --- a/pages/detail/detail.wxml +++ b/pages/detail/detail.wxml @@ -228,6 +228,9 @@ {{item.createdText}} {{item.body}} + + + @@ -260,6 +263,32 @@ + + + + + + 举报评论 + 选择举报原因,我们会尽快处理 + + + + + + + + 举报后该评论将被复核 + + + + diff --git a/pages/detail/detail.wxss b/pages/detail/detail.wxss index a93c6c2..f3fac3f 100644 --- a/pages/detail/detail.wxss +++ b/pages/detail/detail.wxss @@ -891,6 +891,64 @@ border-radius: var(--radius-md); } +/* ============================================================ + 评论举报 + ============================================================ */ +.comment-report-row { + margin-top: var(--space-2); + display: flex; + justify-content: flex-end; +} + +.comment-report-button { + width: auto; + height: 44rpx; + padding: 0 16rpx; + margin: 0; + border: 1rpx solid var(--border); + border-radius: var(--radius-pill); + background: var(--surface); + color: var(--text-muted); + font-size: 22rpx; + line-height: 42rpx; +} + +.comment-report-button::after { + border: none; +} + +.report-reason-list { + display: flex; + flex-direction: column; + gap: var(--space-3); + margin-top: var(--space-4); +} + +.report-reason-item { + width: 100%; + height: 72rpx; + margin: 0; + padding: 0 var(--space-5); + border: 1rpx solid var(--border); + border-radius: var(--radius-md); + background: var(--surface); + color: var(--text-normal); + font-size: var(--fs-body); + line-height: 70rpx; + text-align: left; +} + +.report-reason-item::after { + border: none; +} + +.report-reason-item.active { + border-color: var(--primary); + background: var(--primary-surface); + color: var(--primary-text); + font-weight: var(--fw-bold); +} + /* ============================================================ 加载 & 空状态 ============================================================ */ diff --git a/pages/feedback/feedback.js b/pages/feedback/feedback.js index 534796d..e21b12a 100644 --- a/pages/feedback/feedback.js +++ b/pages/feedback/feedback.js @@ -5,6 +5,7 @@ Page({ data: { typeOptions: config.feedbackTypes, typeIndex: 0, + isPrivacyRequest: false, submitting: false, form: { type: config.feedbackTypes[0].value, @@ -13,11 +14,25 @@ Page({ } }, + onLoad(options = {}) { + const requestedType = String(options.type || ''); + const typeIndex = this.data.typeOptions.findIndex((item) => item.value === requestedType); + if (typeIndex < 0) { + return; + } + this.setData({ + typeIndex, + isPrivacyRequest: requestedType === 'privacy', + 'form.type': requestedType + }); + }, + onTypeChange(event) { const index = Number(event.detail.value); const type = this.data.typeOptions[index].value; this.setData({ typeIndex: index, + isPrivacyRequest: type === 'privacy', 'form.type': type }); }, @@ -38,13 +53,15 @@ Page({ wx.showToast({ title: '请先写下反馈内容', icon: 'none' }); return; } + if (form.type === 'privacy' && !form.contact.trim()) { + wx.showToast({ title: '请填写可联系你的方式', icon: 'none' }); + return; + } this.setData({ submitting: true }); try { - await createFeedback(form); - wx.showToast({ title: '已收到反馈', icon: 'success' }); - - setTimeout(() => { + const feedback = await createFeedback(form); + const finish = () => { const pages = typeof getCurrentPages === 'function' ? getCurrentPages() : []; if (pages.length > 1) { wx.navigateBack(); @@ -52,11 +69,24 @@ Page({ } this.setData({ submitting: false }); wx.switchTab({ url: '/pages/me/me' }); - }, 450); + }; + if (feedback.delivery === 'cloud') { + wx.showModal({ + title: form.type === 'privacy' ? '申请已送达' : '反馈已送达', + content: `回执编号:${feedback.id}`, + showCancel: false, + confirmText: '知道了', + success: finish, + fail: finish + }); + return; + } + wx.showToast({ title: '仅保存在本机,未发送', icon: 'none' }); + setTimeout(finish, 800); } catch (error) { console.error('[feedback] submit failed', error); this.setData({ submitting: false }); - wx.showToast({ title: '反馈提交失败', icon: 'none' }); + wx.showToast({ title: '未送达,请稍后重试', icon: 'none' }); } } }); diff --git a/pages/feedback/feedback.wxml b/pages/feedback/feedback.wxml index 445cb61..ecbb1dd 100644 --- a/pages/feedback/feedback.wxml +++ b/pages/feedback/feedback.wxml @@ -14,14 +14,14 @@ 内容 -