# DSH Visual Acceptance V0.1 闭环迭代方案 > 日期:2026-08-26 > 文档性质:基于当前首个可用切片、对抗式审查和外部竞品调研形成的实施方案;不是已实现能力声明。 > 建议版本名:`0.1.1-closed-loop-beta` ## 1. 决策摘要 下一阶段不扩展视觉 Provider、参考图比较、OCR、像素 Diff 或更多浏览器检查项。 唯一主线是把已验证的确定性浏览器证据底座,补成一次**人工可控的闭环验收**: ```text 声明 Target/Matrix → Run 收集确定性事实与截图 → 用户把 Finding 或截图立为正式 Issue → 用户作出 Decision → Agent 获得只读修改包并完成修改 → 原 Matrix Retest → 系统给出候选复验关系,用户确认结果 ``` 当前首切片不是“毫无价值的报告器”:它已经提供可重放 Matrix、状态到达证据、浏览器事实、工作区证据存储与生命周期围栏。但它也尚不是完整的验收台,因为用户不能在界面内完成 Issue、Decision、交接与复验。 本方案的目标不是证明产品已经有护城河,而是用最低成本验证一个更严格的假设:**当真实项目确实发生修改时,用户是否愿意复用原 Matrix、保存 Decision,并完成第二次验收。** ## 2. 证据基线与问题校正 | 已验证事实 | 当前状态 | 对迭代的含义 | |---|---|---| | `conversation.view`、本地 Target/Matrix、Chrome CDP、Run/截图存储、取消与关闭释放 | 已在真实 DSH Web 首切片实测 | 不重做 Runner 与入口,直接在其上增加闭环数据与 UI | | Issue 指纹、Decision 保留、`active → unresolved → resolved → regressed` | 仅受控 Fixture 的 Core Spike | 先接入真实 Run;不得把 Spike 写成现有用户能力 | | Provider CLI 探测与契约 | 未验证真实图片推理质量 | 本阶段不接 Provider,不让 AI 判断干扰闭环验证 | | Desktop 深浅色、升级兼容 | 未完成自动化验收 | 闭环 Beta 仍须保留 DSH Adapter 关闭与兼容回归策略 | | 真实项目、第二轮复验、主观判断准确率 | 尚无数据 | 这是 Beta 的核心验证对象,而非发布宣传素材 | ### 2.1 对当前复验 Core 的关键修正 现有 `src/core/retest.mjs` 在本轮没有 Finding 时,会把旧 Issue 直接标记为 `resolved`。这只适用于“同一 Checkpoint 已到达、同一规则已执行、且确实没有再次命中”的受控情形。 以下情况**不得**自动写为已解决: - Checkpoint 为 `unreached`、Run 失败或被取消; - 该规则本轮没有执行,或执行版本不兼容; - 用户修改了 Matrix、视口、主题、路径或 Ready 契约; - 手工视觉 Issue 本来就没有可自动检测的规则; - 指纹匹配不确定,可能是 DOM 改动造成的合并/拆分。 因此,系统的职责是产生“待确认的复验关系”,不是在证据不足时替用户宣布修复成功。 ## 3. 产品边界:本 Beta 做什么、不做什么 ### 做什么 1. 将确定性异常信号转换为可审核的 `Finding Candidate`; 2. 允许用户从 Candidate 或截图创建正式 Issue,也允许创建带截图证据的手工视觉 Issue; 3. 为正式 Issue 保存不可被后续 Run 覆盖的人工 Decision 历史; 4. 仅对 `approved-fix` Issue 生成只读修改包; 5. 用原始 Matrix 创建 Retest,并展示 `new / still-detected / possibly-resolved / not-verifiable`; 6. 让用户确认、合并、拆分或否决自动关联; 7. 将每一步保存在工作区本地,能够在 DSH UI Adapter 暂停时保留数据。 ### 不做 - 不接入 Provider 实际推理、VLM 审美判定、OCR、参考图 Diff 或 Pixel Diff; - 不自动把浏览器信号当作正式 Issue; - 不自动把 Retest 中未出现的 Issue 写成“已解决”; - 不直接改项目文件、不自动向 Agent 注入修改指令; - 不增加第二套 Harness UI、Codex Skill、MCP 或云端协作服务; - 不以总分、通过率或“AI 置信度”替代具体证据和人工判断。 这不是放弃这些能力,而是防止它们掩盖目前最需要验证的闭环使用行为。 ## 4. 闭环 Beta 的产品设计 ### 4.1 核心对象与权限 | 对象 | 谁创建/修改 | 必备内容 | 不能做什么 | |---|---|---|---| | Runtime Evidence | Runner | 原始浏览器事实、环境、截图引用 | 不能表达产品或审美结论 | | Finding Candidate | 规则或用户 | 来源、规则、Checkpoint、证据引用、候选指纹 | 不是正式 Issue,不代表必须修改 | | Issue | 用户明确“立为 Issue”或手工创建 | 标题、影响、范围、证据、生命周期 | 不等于 AI 或规则最终结论 | | Decision Event | 用户 | `approved-fix` / `accepted-risk` / `dismissed` / `deferred`、原因、时间 | 后续 Run 不得覆盖 | | Change Package | 系统按已批准 Issue 生成 | 允许修改、禁止修改、证据、原 Matrix、复验要求 | 只读,不执行修改 | | Retest Relation | 系统候选 + 用户确认 | 基线 Run、本轮 Run、匹配理由、验证状态 | 不可在 `unreached` 时声称已解决 | ### 4.2 用户流程 1. 用户像现在一样创建 Run,结果页仍首先显示“已检查 / Ready / 异常信号”,不把信号叫作 Issue。 2. 每个异常信号旁增加“查看证据”和“立为 Issue”;用户也可在截图上选择 Checkpoint 后“新增手工 Issue”。 3. 创建 Issue 时必须确认:检查对象、影响、处理人,以及是否需要 Agent 修改。系统预填证据和范围,用户补充简短标题/影响。 4. Issue 列表按 `待决定 / 已批准修改 / 已接受风险 / 已驳回 / 延后` 展示。Decision 是追加事件,不覆盖历史。 5. 用户只对 `approved-fix` 点击“生成修改包”。首版只提供可复制 Markdown 与 JSON 文件;用户自行发送给当前 Agent。直接会话注入另行做 DSH 契约 Spike 后再决定。 6. 修改完成后,用户从该修改包或基线 Run 点击“按原条件复验”。系统锁定原 Matrix;若需要改 Matrix,必须新建普通 Run,不能冒充 Retest。 7. Retest 结果按 Issue 显示:仍检出、疑似已解决、回归、新问题、未验证。用户对“疑似已解决”和关联不确定项作最终确认。 ### 4.3 结果语义 | Retest 前提 | 系统显示 | 是否可自动更新 lifecycle | |---|---|---| | 同 Checkpoint `reached`,同规则执行,稳定指纹再次命中 | `still-detected` | 可以候选为 `unresolved`,Decision 保留 | | 同 Checkpoint `reached`,同规则执行,无匹配 Finding | `possibly-resolved` | 否;等待用户确认后才写 `resolved` | | 旧 Issue 已确认 `resolved`,同范围再次命中 | `regressed` | 可以候选为 `regressed`,保留原 Decision 与审计记录 | | Checkpoint `unreached`、Run 失败/取消、规则缺失或环境不兼容 | `not-verifiable` | 否;不得出现“已解决” | | 指纹相似但不确定,或用户手工视觉 Issue | `needs-human-review` | 否;允许合并/拆分/否决 | | 不属于基线 Issue 的新 Finding | `new-candidate` | 仅作为候选,用户可立为新 Issue | ## 5. 数据与存储方案 ### 5.1 Schema 演进 保留已经存在的 `runs//manifest.json` 和截图目录;新增项目级数据,不回写或破坏旧 Run: ```text .dsh-visual-acceptance/ ├── schema.json ├── project.json ├── issues.json ├── runs/ │ └── /manifest.json ├── change-packages/ │ └── -.md └── screenshots/ ``` 建议引入独立的版本化 Envelope: ```json { "schemaVersion": "0.1.1-closed-loop", "projectId": "ap-...", "issues": [], "updatedAt": "2026-08-26T00:00:00.000Z" } ``` 旧 `0.1.0-slice` Run 可只读展示;首次创建 Issue 时初始化项目级文件。迁移不得删除旧文件,失败时保留原文件并显式报错。 ### 5.2 Issue 的最小结构 ```ts type Issue = { issueId: string projectId: string source: 'deterministic-finding' | 'manual-visual' scope: { targetKey: string checkpointKey: string path: string state: string viewport: { width: number; height: number } theme: 'light' | 'dark' readyContract: { selector: string; text?: string } } title: string impact: string evidenceRefs: Array<{ runId: string; checkpointId: string; kind: 'screenshot' | 'browser-fact' }> detector?: { rule: string; version: string; fingerprint: string } lifecycle: 'active' | 'unresolved' | 'resolved' | 'regressed' decision: 'pending' | 'approved-fix' | 'accepted-risk' | 'dismissed' | 'deferred' decisionHistory: DecisionEvent[] verificationHistory: VerificationEvent[] } ``` 指纹必须加入稳定的 `projectId`、`targetKey`、规范化 `checkpointKey`、规则名和锚点;不能仅依赖当前的 `page/state/viewport/theme/anchor`。规则版本也应记录,以免规则变更被误解为页面修复。 ### 5.3 Finding 与 Issue 分离 `run-executor` 继续输出浏览器事实;新增纯函数将 `console / request / image / font / overflow / unreached` 映射为 Finding Candidate。Candidate 仅保留本次 Run 的事实和候选指纹。 正式 Issue 必须由用户明确创建。这样可以同时避免两类误导: - “2 个字体信号”被自动写成“2 个产品问题”; - 用户在截图中发现的布局/业务状态问题,因为没有机器规则而无法进入闭环。 ## 6. 技术实施拆分 ### M0:闭环数据契约与复验安全(先做) 目的:让后续 UI 不会建立在错误的“自动已解决”逻辑上。 | 改动 | 推荐位置 | 验收 | |---|---|---| | 提取 `FindingCandidate` 及稳定 scope/fingerprint 契约 | `src/core/findings.mjs`、`src/core/issue-identity.mjs` | 同一输入稳定;项目/Target 不同不碰撞 | | 将 Retest 结果改为候选关系而非直接改写 Issue | `src/core/retest.mjs` | `unreached`、取消、规则缺失均不能返回 `resolved` | | 新增项目级 `issues.json` 与原子写入/读取/迁移 | `src/storage/issue-store.mjs` | 旧 Run 可读;目录/文件权限继续为 `0700/0600` | | 定义 Change Package 序列化 | `src/core/change-package.mjs` | 只含获批 Issue;不含 Cookie、Header、Body、表单值 | | 完善单元测试 | `tests/retest.test.mjs` 等 | 覆盖同问题、回归、指纹歧义、`unreached`、手工 Issue | ### M1:Issue 与 Decision 的最小 UI/API 目的:让用户在同一会话中从证据走到明确决定。 新增 Host API(路径可按现有 `/visual-acceptance` 前缀实现): | 方法 | 路径 | 作用 | |---|---|---| | GET | `/issues` | 列出当前工作区 Issue 与最新状态 | | POST | `/issues` | 从 Finding Candidate 或手工表单创建 Issue | | GET | `/issues/:issueId` | 读取 Issue、证据与历史 | | POST | `/issues/:issueId/decision` | 追加 Decision Event | | POST | `/issues/:issueId/relations` | 人工确认/合并/拆分/非同一问题 | | POST | `/issues/:issueId/change-package` | 仅对 `approved-fix` 生成本地 Markdown/JSON | Client 改动集中在 `src/dsh/client/client.js`: - Run 结果保留现有摘要,在事实条与截图旁放置“立为 Issue”; - 增加轻量 Issue 侧栏/详情区,而非新建页面; - Decision 使用完整文字和原因输入,不能用颜色或单个图标表达; - “生成修改包”默认禁用,直到用户明确选择 `approved-fix`; - 对未实现的直接 Agent 交接只显示“复制修改包”,不得使用“已发送给 Agent”的文案; - 390px 下 Issue 详情改为结果区内顺序展开,保留截图、Decision 与主操作可达。 ### M2:原 Matrix Retest 与人工确认 目的:完成产品最小闭环,而不是增加报告视觉复杂度。 | 改动 | 推荐位置 | 关键规则 | |---|---|---| | 基线 Run 的不可变 Matrix 快照 | `run-store.mjs` | Retest 从基线复制 Matrix;改变范围只能新建 Run | | 创建 Retest | Host `/runs/:runId/retest` | 保存 `parentRunId`、基线环境与本轮环境,不复用旧截图 | | 产生复验候选 | `retest.mjs` | 先检查 checkpoint 可达与规则执行,再做指纹关联 | | Retest 对比 UI | `client.js` | 显示两轮证据并标注“候选/已确认”,不使用自动通过 | | 用户确认 | Issue API + `issue-store.mjs` | 只在用户确认后写 `resolved`;历史 Decision 永不被覆盖 | ### M3:真实项目试点与宿主兼容 目的:验证闭环是否值得继续投资,及时处理 DSH 风险。 - 用 5 个真实本地 Web 项目做试点;每个项目至少有一次真实修改和一次按原 Matrix 的 Retest; - 每轮记录 DSH 版本、Chrome 版本、运行环境、项目类型和 Matrix; - 每个 DSH 升级先跑 Host/Client 契约与一个真实 Web 冒烟 Run;不兼容时关闭 Adapter,保留 `.dsh-visual-acceptance` 本地数据; - Desktop 只补齐深浅色、关闭/重开、390px 与无横向溢出,不在试点期扩张 UI 视觉范围; - 试点结束后再判断是否接 Provider、参考图或通用 CLI。 ## 7. 验收与测试矩阵 ### 7.1 必须自动化的测试 | 类别 | 用例 | |---|---| | Schema/迁移 | 旧 Run 只读、首次创建 Issue、原子写入中断、非法 ID/路径被拒绝 | | 权限与隐私 | 项目级目录 `0700`、JSON/修改包 `0600`、包内无 Header/Body/Cookie/输入值 | | Finding | console/request/image/font/overflow/unreached 分别生成正确 Candidate;无信号不虚构 Issue | | Decision | Decision 追加而非覆盖;非 `approved-fix` 不生成修改包 | | Retest | 同问题仍在、已确认解决后回归、指纹不确定、手工 Issue、`unreached`、取消、规则缺失 | | Host API | loopback、workspace、session、路径围栏、错误码、关闭释放 | ### 7.2 必须人工验收的界面状态 - 空 Issue 列表、Run 进行中、Candidate 已创建、Decision 保存失败、Retest 运行中、`not-verifiable`; - 1440px 与 DSH 390px 壳层:不出现横向溢出,主操作和证据均可到达; - Light/Dark、键盘焦点、缩放 200%、长标题/长路径; - 创建 Decision 后刷新/重开 DSH,历史仍存在; - 关闭插件/取消 Retest 后,Chrome、路由和临时目录正确释放。 ### 7.3 试点指标与停止条件 以下是待验证门槛,不是现有数据或对外承诺: | 指标 | 记录方式 | 继续条件 | |---|---|---| | 闭环完成率 | 有真实修改的试点中,完成同 Matrix Retest 的项目数 / 合格试点项目数 | 5 个中至少 3 个完成 | | Matrix 复用率 | Retest 是否直接复用基线 Matrix | 不低于 80%,否则检查 Matrix 设计是否不适用 | | Decision 保留正确性 | Retest 后人工抽查历史 Decision | 100% 保留 | | 假性解决 | `unreached`/失败/取消后被标为 resolved 的数量 | 必须为 0 | | 自动关联可纠正性 | 用户能完成合并/拆分/否决的比例 | 100% 可操作 | | 用户价值信号 | 用户是否用修改包推动实际修改,及是否减少截图/聊天来回 | 记录定性反馈和耗时,不虚构节省比例 | 停止或转向应基于“发生真实修改后仍不愿复验”的行为,而不是单纯看用户是否只生成一次报告。一次性发布前验收本身可能是合理场景,不能被误判为产品失败。 ## 8. DSH 与竞品策略 ### 8.1 宿主策略 DSH 官方仍处于 Developer Preview,并明确提示会有兼容性破坏;这证明 Adapter 风险存在,但不能据此推导“生态为零”或“插件价值归零”。当前官方仓库的公开 Star/Fork 规模说明有外部关注,但不能替代活跃用户、留存或付费意愿数据。[DSH 官方介绍](https://deepseek.com/harness/en/) [GitHub 仓库](https://github.com/deepseek-ai/deepseek-harness) 采取以下对冲,而不是现在做第二套产品: 1. Core、Schema、存储和 Change Package 不导入 DSH 类型; 2. DSH Adapter 只保留 `conversation.view`、Host API 与生命周期职责; 3. 将 Change Package 的 Markdown/JSON 视为未来 CLI 的稳定边界; 4. 仅当试点证明闭环被使用、且 DSH 维护成本连续两个版本阻塞核心迭代时,再做无 UI 的 CLI 导出/导入 Spike; 5. 不在此阶段承诺 Codex Skill 或跨 Harness UI。 ### 8.2 竞品策略 Playwright 已提供截图与视觉比较,Stagehand 也兼容 Playwright 并提供浏览器自动化能力;因此“截图/浏览器控制”不是差异化叙事。[Playwright visual comparisons](https://playwright.dev/docs/next/test-snapshots) [Stagehand introduction](https://docs.stagehand.dev/v2/first-steps/introduction) 本项目应验证的差异化是更窄的:**同一会话、同一工作区内,对 AI 修改产物进行范围声明、证据保留、人工授权、受限交接和同条件复验。** 若成熟工具原生提供该完整链路,或试点没有复验使用行为,应停止将其包装成独立产品。 CDP 不需要立刻替换为 Playwright;应在 M3 后基于两项证据决定:维护 CDP 子集的真实成本,以及接入 Playwright 能否减少兼容/测试成本而不破坏本地生命周期和安全边界。 ## 9. 实施顺序与完成定义 ```text M0 复验安全与存储契约 → M1 Issue / Decision / 修改包 → M2 原 Matrix Retest / 确认关系 → M3 5 项真实项目试点与兼容验证 → Go: Provider 或 CLI Spike → No-go: 收缩为 CLI/JSON 证据工具,或停止扩展 ``` `0.1.1-closed-loop-beta` 只有同时满足以下条件才可称为“闭环 Beta”: 1. 用户可以从 Run 证据或手工截图创建正式 Issue; 2. Decision 有历史,且只允许用户修改; 3. `approved-fix` 才能生成只读修改包; 4. Retest 固定重放原 Matrix; 5. `unreached`、失败、取消或不确定关联绝不自动宣告已解决; 6. 用户可确认/否决解决、回归、合并和拆分; 7. 所有数据仍为工作区本地,DSH 关闭后可保留并可再次读取; 8. 自动化与人工验收覆盖本方案第 7 节。 在这些条件满足前,README、演示和发布说明应继续使用“首个可用切片”或“闭环 Beta 开发中”,不能称“完整视觉验收闭环已交付”。