Skip to content

feat(ui-evidence): add source-bound real-browser verification - #113

Merged
Qiyuanqiii merged 16 commits into
mainfrom
codex/issue-102-ui-evidence
Aug 20, 2026
Merged

Qiyuanqiii merged 16 commits into
mainfrom
codex/issue-102-ui-evidence

Conversation

@Qiyuanqiii

@Qiyuanqiii Qiyuanqiii commented Aug 19, 2026 •

Copy link
Copy Markdown
Member

摘要

  • 新增严格版本化的 ui-evidence.v1:将 Run/Mission/Session/Workspace、commit、dirty/index/worktree digest、build/start recipe、固定浏览器身份、loopback route、fixture/seed、呈现矩阵、交互步骤、失败策略和不可变 Artifact 绑定为一份可审计清单。
  • 新增 Run-owned 真实浏览器执行链:由 command-runtime.v2 启动当前源码的应用,拒绝预先存在的 listener;从固定受信位置重新验证 Edge 版本、publisher 与 executable SHA-256,以 disposable Profile、受限 CDP、Safe Web/WFP 和创建时 Job Object 完成真实导航、交互、断言及采集。
  • 新增 source checkpoint 与 fail-closed 生命周期:在 build 前、readiness 后、浏览器断言后和全部进程/端口/Profile 清理后重新证明精确源码;console/page/request/HTTP 异常、源码漂移、产物不完整或 cleanup 不确定均不能得到 passed。
  • 新增 schema v119 的 Attempt/step/artifact/operation 持久化、认证 HTTP/OpenAPI、只读且哈希验证的 CLI 导出、Desktop/React 证据查看与显式启动/取消,并把内置 run-verify 从 1.0.0 精确归档后升级为 1.1.0。
  • Windows CI 新增 clean-checkout 真实 Edge 矩阵与行为回归:覆盖 desktop/mobile、light/dark、en-US/zh-CN、full/reduced motion,并用“页面正常构建但 click handler 缺失”的 route 证明真实交互证据能发现源码/build 检查无法单独证明的错误。

Closes #102.

协议、源码绑定与状态语义

ui-evidence.v1 的 manifest 在运行前一次性持久化,随后不可变。它同时绑定:

  • Run、Mission、Session、Workspace、Attempt 与 root fingerprint;
  • Git/non-Git source kind、commit、branch、dirty digest、原始 index digest 和确定性 worktree manifest digest;
  • 可选 build 与必需 start 的规范化 command-runtime.v2 配方,包括固定 executable/argv、Workspace 相对 cwd、environment digest、timeout、network=disabled 与 credentials=none;
  • Edge product/channel/version/executable SHA-256、restricted-cdp-ui-evidence.v1、headless 和 temporary-profile 声明;
  • literal 127.0.0.1 origin、route、readiness、viewport/DPR、locale/theme/reduced-motion、fixture/seed/page state digest;
  • 有序 navigate/click/type/assert/capture steps、mask selectors,以及强制 console/page/request/HTTP failure policy。

Application 在 build 前、应用 readiness 后、浏览器断言后及 owned application/browser tree 清理完成后重新捕获 source checkpoint。tracked、未忽略的 untracked、index、commit、branch 或 root 任一漂移都会 fail closed;最后一次复核刻意位于 cleanup 之后,避免应用在最后断言与 terminal receipt 之间改写源码。

Attempt 只允许 not_run|running|passed|failed|cancelled|timed_out|interrupted。passed 是唯一绿色状态;not_run、build 成功、mock render、缺少强制产物或清理未证实都保持非通过。失败阶段固定为 build|launch|readiness|navigation|selector|assertion|console|network|capture|cleanup,CLI、OpenAPI、Desktop 和 Skill 使用同一字段与语义。

运行所有权、浏览器与网络边界

Desktop 默认只读。启动 UI evidence 必须同时具备当前 running Run、Code/Local/Deliver/root、active execution lease、full_access、permission control、danger-full-access、Run execution、restricted browser CDP 和显式 --enable-ui-evidence;启动 flag 只打开进程内 capability,不创建 permission、approval 或 lease。

readiness 端口在任何启动前探测;已有 listener 返回 launch/preexisting_service,不会被收养、停止或用作当前提交的证据。应用由 Run-owned command runtime 管理;浏览器从固定安装位置重新验证身份,以新的 Profile 启动。每次 start/read/wait 都重查当前 lease。取消、timeout、撤权或 Desktop shutdown 后,cleanup-only binding 只携带本 Attempt 的 durable Job/operation/Run/lease identity,只能回收精确 owner 的进程树,不能恢复启动权、收养历史 PID/端口/Profile 或影响其他 Job。

Profile 仅在浏览器树、network guard 和端口回收完成后进入 exact-owner quarantine;Windows 短暂 sharing lock 使用 5 秒有界重试,超限仍是 cleanup failure。启动 reconciliation 只把遗留 running Attempt 收敛为 interrupted,不会从 SQLite 历史恢复进程 authority。

浏览器只允许精确 literal loopback origin,所有 request/redirect 重新经过 TargetScope。UI 专用 CDP allowlist 不包含 Runtime.evaluate、cookie API、response body、request mutation/replay 或 Fetch.fulfillRequest。越界 URL 只持久化 [blocked-url];不读取 request headers、cookie 或 body。普通 command runtime 的 network=disabled 是宿主执行策略,不被描述成通用 packet-level OS containment;真正浏览器流量由 UI-evidence Safe Web/WFP 路径独立约束。

Windows 托管 Edge 根因与生产修复

托管 Windows 首轮失败不是 Edge 安装、publisher、Profile ACL、Job Object、standard-user token、RemoteDebuggingAllowed policy 或进程存活问题。有限诊断证明主进程与 Job 持续存活、Profile 正常初始化;同一临时标准用户使用等价参数直接启动可以发布 DevTools endpoint,只有生产 adapter 的旧环境块失败。

根因是旧实现把 USERPROFILE、APPDATA、LOCALAPPDATA 全部改写为浏览器 Profile 下的任意子目录。Chromium 的 Windows PathService 因此无法从实际 launch token 解析 Local AppData,把远程调试使用的 user-data directory 视为未知/默认目录并主动拒绝。最终修复先取得并验证启动 authority,再通过 Windows CreateEnvironmentBlock 的 Go 封装从该 token 创建全新环境,且明确不继承调用者进程环境;随后只保留固定 structural allowlist,要求三个 known-folder 值均为绝对路径,并覆盖 COMSPEC、PATH、PATHEXT、SystemRoot/WINDIR。HOME、TEMP、TMP 与 --user-data-dir 仍指向 disposable Profile,调用者 secret 与任意 PATH 不会进入浏览器。

Edge 还会先创建零字节 DevToolsActivePort,再写入两行 endpoint。读取器现在只把“owned Profile 内、direct、regular、非 reparse、大小恰为 0”的短暂窗口视为 pending,并继续受 45 秒 deadline 与进程存活约束;indirection、oversize、非空 malformed payload 仍立即 fail closed。CI 同时改为临时标准用户、随机密码、唯一 direct temp root、受限 ACL、production process adapter,并在 finally 中按 SID 清除 Profile、ACL、账户和临时目录。

证据、脱敏、限额与清理

V1 每次执行必须同时采集 PNG screenshot、DOM、accessibility tree、console/page diagnostics、network/HTTP metadata 和 performance metrics;video 字段保留但当前必须为 false,因此不会以像素快照单独替代行为、可访问性或运行时健康验证。

每个 Artifact 绑定 kind、MIME、bytes、SHA-256、source commit、Run/Attempt、source step、capture time、viewport、screenshot dimensions、redaction、retention_policy=run_history 与 untrusted=true。截图像素面和 PNG dimensions 在 domain、SQLite trigger 与 React parser 三层绑定到 viewport × DPR;在完整 PNG 解码/分配前先检查 header dimensions。上限为单 Artifact 32 MiB、单 Attempt 128 MiB、仓库 2 GiB,CI 上传副本保留 5 天。

文本产物统一修复 UTF-8、去控制字符并做 Secret redaction。Network 仅保留再次通过 scope 的脱敏 URL、method、resource type、status、MIME 和失败摘要。动态或可能含敏感数据的 screenshot 区域必须显式列入 mask;任一 mask selector 未匹配即失败。下载响应使用 no-store、ETag、内容 SHA-256 与 untrusted header;React 在创建 Blob 前复核 MIME/长度/hash,CLI 在 0600 独占创建输出文件前完成同样复核。

HTTP、CLI、Desktop 与 Skill

认证 HTTP/OpenAPI 新增:

GET  /api/v1/runs/{run_id}/ui-evidence
POST /api/v1/runs/{run_id}/ui-evidence
GET  /api/v1/ui-evidence/{attempt_id}
GET  /api/v1/ui-evidence/{attempt_id}/artifacts/{artifact_id}
POST /api/v1/ui-evidence/{attempt_id}/cancel

GET/download 使用 read bearer;start/cancel 使用独立 control bearer、严格 JSON、有界 body、Run URL ownership 和 confirm=true。相同 operation_key + request fingerprint 幂等返回原 Attempt,不同载荷复用 key 冲突。

CLI 有意只提供 list|show|artifact,不复制执行 authority;artifact export 是 exclusive create + hash verify。Desktop Run workspace 新增双语 UI evidence 页签,capability 关闭后仍能只读查看历史;启动要求编辑完整 JSON 并显式勾选审阅确认,只有 passed 使用成功样式,not_run 保持中性。React 还独立复核 Attempt/step/artifact chronology、尺寸和下载内容。

内置 run-verify@1.1.0 将 manifest/receipt、focused-checks 与 PR Verification 字段对齐;原 1.0.0 的 SKILL.md 和 manifest 逐字节归档,升级不会改写历史 Skill 身份。

生成后的 OpenAPI 为 128 paths / 143 operations / 350 schemas;OpenAPI SHA-256 为 016a7de607badbc240c35d0ebeb33d576ed89f4d17a26f27a6a273db44a42ffc,TypeScript binding SHA-256 为 652a5fbc747b0ad1910b1fc6a12cc1bba4ddbc63a90fbc6d97cb0a0e02259808。

主线兼容性

本分支已 rebase 到包含 PR #112 / Issue #103 的当前 main 9fc824c。上游批次交付已经使用 schema v118 与 ADR 0119,因此本 PR 顺延为 schema v119 与 ADR 0120,不改写迁移历史。

  • v119 从 v118 迁移,新增 UI-evidence manifest/attempt/step/artifact/operation 表、索引、配额及不可变/状态 trigger;完整 v1→v119 migration/recovery fixture 通过。
  • 复用现有 Run-owned command-runtime.v2、execution lease、permission、browser runtime、Safe Web/WFP、Job Object、Workspace identity 与 Artifact 约束,不建立第二套进程或浏览器 authority。
  • run-verify 精确归档 1.0.0 并升级 1.1.0;旧 Skill 身份、既有 command runtime、batch delivery、workspace checkpoints 与 Desktop compatibility identifiers 保持兼容。

验收条件对应

  • clean checkout/fixed commit 按记录配方真实启动应用,由独立临时 Profile 的 Edge 完成导航、click/type、断言和 PNG 截图。
  • 每份证据绑定 commit、dirty/index/worktree digest、build/start recipe、工具/浏览器版本、viewport/DPR、route、fixture/seed 和 source step。
  • console error、page error、failed request 和非预期 HTTP status 均 fail closed,并只保存有界脱敏诊断。
  • PNG、DOM/a11y、console/network/performance 产物均有 hash、来源、尺寸/大小和 retention 限制;可选 video 在 V1 明确为 false,不会伪报已采集。
  • success/timeout/cancel/restart/撤权路径验证应用与浏览器进程树、临时 Profile、DevTools/readiness 端口和 network guard 清理。
  • 不读取、连接或污染用户日常 Chrome/Edge Profile、cookie、扩展或已登录会话。
  • Windows 本地真实 Edge 与 CI headless 矩阵覆盖不同 viewport/theme/locale/reduced-motion;Desktop 启动能力保持显式 opt-in。
  • Skill、CLI、OpenAPI、Desktop/React 与 README/guide 使用同一 Attempt/Artifact 状态语义,not_run 不显示为通过。
  • 缺失 click handler 的真实 regression route 被浏览器交互断言捕获,而页面仍可正常构建和呈现。

本地验证

  • go test -count=1 -timeout 25m ./...:在最终分支 commit 4a80cd7b93a212c2b8ab83539b29fe33e18279b5 全仓通过;internal/application 645.479s、internal/httpapi 220.330s、internal/store 完整 v1→v119 migration/recovery 1377.572s。
  • go test -race -count=1 -timeout 15m -run '(UIEvidence|SchemaV11(8|9)|CleanupOnly)' ./internal/application ./internal/browserruntime ./internal/httpapi ./internal/store ./internal/uievidence ./internal/desktop:定向 race 全通过。
  • schema v118/v119、HTTP/OpenAPI/runtime capability/UI evidence、Desktop 与 cmd/cyberagent-desktop 的 ordinary/secure-tag 定向回归通过;Windows Desktop 使用完整 desktop,wv2runtime.error tags。
  • go vet ./...、go mod verify、go mod tidy -diff、Linux amd64/CGO-off go build ./...。
  • staticcheck -checks='SA*,S1*,QF*' 覆盖全部受影响 Go 包及 Desktop tags,无新增 finding。
  • OpenAPI golden、npm run check:api 和连续生成无漂移;OpenAPI/TypeScript hash 如上。
  • npm run typecheck、完整 npm test(62 files / 266 tests)、npm run build。
  • npm audit --audit-level=high:0 vulnerabilities。
  • cargo fmt --check、cargo test --locked(7 + 2 tests)、cargo clippy --locked --all-targets -- -D warnings。
  • CI YAML 解析、git diff --check、本机绝对路径/身份、凭据形态和构建产物扫描。
  • ./scripts/build-desktop.ps1 -SkipFrontend -VerifyReproducible 在最终分支 commit 4a80cd7b93a212c2b8ab83539b29fe33e18279b5 完成双构建;EXE SHA-256 ebffbb8ff33061623bfd6616ab60be4bd669afb450eb79915690ca32f01fd6be,reproducible=true、release_ready=false、无 installer/registry writes,Profile control 默认关闭。

全仓 staticcheck 仍有 5 项与本 PR 无关的既存 finding:internal/fileedit SA9003、internal/projectconfig SA4005,以及 internal/sandbox 的 SA1019/SA4011/S1023;受影响包扫描为绿色,未把仓库既存债务描述成零 finding。

govulncheck ./... 在本机 Go 1.26.5 如实报告 5 项可达标准库问题:GO-2026-6218、GO-2026-6090、GO-2026-6089、GO-2026-5972、GO-2026-5026,均由 Go 1.26.6 修复;另有 imported/required 但未调用的 advisory。本 PR 未新增模块依赖,不能把该本机扫描写成 zero finding;远端固定 Go 1.25 当前补丁版的结果由 CI 给出。

最终真实 Edge 收据

最终 clean 分支 commit 4a80cd7b93a212c2b8ab83539b29fe33e18279b5 上运行:

CYBERAGENT_UI_EVIDENCE_SMOKE=1
go test -v -timeout 3m -count=1 ./internal/browserruntime -run '^TestInstalledEdgeUIEvidenceHeadlessMatrixAndRegression$'
  • receipt protocol:ui-evidence-ci-smoke.v1
  • receipt SHA-256:8a19544bbf17725574efaf949209b33b796c472d5078917f7b81ba0bc1d9cddf
  • source:上述 commit;dirty digest e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855;clean_checkout=true
  • Edge:151.0.4129.93;executable SHA-256 486c0e70f7c66a3288f3b00acf25452b69e08c2cb1f361937d8f6e720ee01ece
  • desktop-light-en.png:1440×900,14,000 bytes,SHA-256 f215d70b144e3116838e3bc6fb579e42385927d03ac316cee19d2522ac3be337
  • mobile-dark-zh-reduced.png:390×844 @2x → 780×1688,31,269 bytes,SHA-256 deb11ce9f55258066d7902bd7857d873007e484380454bb3332231b186c9fbf3
  • regression-detected.png:1440×900,13,467 bytes,SHA-256 d8e58eac08a8dc1e81e8799721e29f462b2137d67b54e1990186268bb1d5723d
  • regression_caught=true;browser_tree_reaped=true;browser_port_released=true;profile_removed=true;fixture_server_reaped=true;CI retention 5 days。

同一固定 fixture 连续运行的三张 PNG 逐字节一致。首次真实运行暴露的 Windows Profile database sharing-lock 已通过 exact-owner、5 秒有界 quarantine cleanup 修复并由重跑验证。

远端 CI

  • CI run 32320191954:Go control plane、TypeScript console、Rust analyzer fixtures、Windows Desktop、macOS Desktop、Windows 2022 standard-user real Edge 共 6 个作业全部通过。
  • Desktop release run 32320191934:dependency/license 与 reproducible verified Portable ZIP 通过。
  • Publish verified GitHub Release 按 PR/Draft 规则跳过,没有发布未审阅产物。

托管真实 Edge 作业在 GitHub 的 synthetic merge checkout 6f1da0fe2b1b702909198bedaf01b80f13b27ac4 上生成并上传 ui-evidence-edge-smoke(Artifact ID 9389521356,retention 5 days):

  • receipt SHA-256:c1ebac20fa6bdd90d3aebcd26aab5bffb8b5a6957675728622e93b2b833ec21a
  • clean_checkout=true;dirty digest e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
  • Edge 151.0.4129.59;executable SHA-256 b573f5254db288cd586e3a7afa8a6ef93e749c7a08f5f8fb862c3a75f5065902;adapter windows_browser_job.v2
  • desktop:1440×900,12,069 bytes,SHA-256 67131360b3c35696d81a5f4166cd42a4b55baf40246b048c45d24b705fa24763
  • mobile:780×1688,25,214 bytes,SHA-256 c3ea2d16a1bebaeb015242fea237212ba0d0e4579d0bb89a38667fdf42728e5f
  • regression:1440×900,11,609 bytes,SHA-256 f39395f1b378cf2bf0406db0949899510e5a6c86e5a66ae3ef0e741fb894afdc
  • regression_caught=true、browser_tree_reaped=true、browser_port_released=true、profile_removed=true、fixture_server_reaped=true

PR 继续保持 Draft,留给维护者完成人工 Desktop UX 审阅。

安全审计

  • 最终 canonical security diff scan 精确覆盖 9fc824c...4a80cd7 的 62/62 个 source inventory 项,9 个高风险 surface 全部 no_issue_found,无 exclusion、deferred 或 open question,0 candidate / 0 reportable finding。审查包含最终 token environment allowlist、standard-user launch、zero-byte endpoint publication、production-adapter hosted smoke 和 cleanup delta;报告已 sealed,scan ID 35c14640-fa37-47ee-81f2-4479635fc664。
  • 审查 source TOCTOU、pre-existing listener、Run/lease/permission 撤权、cleanup-only identity、Profile quarantine、CDP allowlist、redirect scope、diagnostic redaction、artifact quota/dimensions、download hash、idempotency、restart reconciliation 与 not_run false-success。
  • 未提交 credential、cookie、用户 Profile、原始 typed value、request header/body、local database、临时 evidence、构建产物、本机绝对路径或本地身份。
  • README、usage、architecture、HTTP API、Desktop 测试矩阵、project memory/status/progress、独立运行手册和双语 ADR 0120 同步描述保证、失败语义与残余边界。

安全扫描的动态限制如实保留:真实运行证据来自 Windows;宿主 command runtime 的 stripped/offline policy 不是 packet-level sandbox;安全结论只覆盖本 PR diff,不把不可信浏览器产物当作源码外安全属性的证明。

非目标与仍需人工证据

  • 不接管用户浏览器,不复用登录会话/扩展/cookie,不开放公网或认证流程。
  • 不开放 Full Debug CDP、任意 JavaScript evaluation、cookie/response-body/request mutation,也不让页面内容获得 host authority。
  • V1 固定 video=false;当前强制证据是 PNG + DOM + accessibility + console/page + network/HTTP + performance。GIF/video 属于可选后续能力,不会伪报已采集。
  • 不自动接受视觉基线;动态或敏感区域必须显式 mask,baseline 更新仍需人工审阅。
  • 合并前仍建议人工确认 Desktop 的 manifest review、start/cancel、not_run 中性展示、step timeline、Artifact 下载验证及失败说明。尚未取得的人工交互证据不会写成已完成。

Audit

  • No credentials or local runtime data are included.
  • Policy, workspace, sandbox, browser, process, network, persistence and cleanup boundaries were reviewed.
  • README, API/architecture guides, ADR and project memory/status/progress were updated.

@Qiyuanqiii
Qiyuanqiii marked this pull request as ready for review August 20, 2026 06:07
@Qiyuanqiii
Qiyuanqiii merged commit 4fa37c5 into main Aug 20, 2026
9 checks passed
@Qiyuanqiii
Qiyuanqiii deleted the codex/issue-102-ui-evidence branch August 20, 2026 06:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(ui-evidence): 真实浏览器运行时与可追溯 UI 验证

1 participant