{"_id":"@aafe/ai-test","_rev":"2-9a0914172848dffeaa66e4ff2f6a417a","name":"@aafe/ai-test","dist-tags":{"latest":"0.0.2"},"versions":{"0.0.1":{"name":"@aafe/ai-test","version":"0.0.1","_id":"@aafe/ai-test@0.0.1","maintainers":[{"name":"lixintao","email":"li18566230175@gmail.com"}],"bin":{"uitest":"dist/uitest.js"},"dist":{"shasum":"0b74d1d59b7e1ef48a7c17e6bfdae56ff0b06257","tarball":"https://registry.npmjs.org/@aafe/ai-test/-/ai-test-0.0.1.tgz","fileCount":36,"integrity":"sha512-3kslN8JJmsFKuCv25dQJvWmFJ8LYTNTC20+2J0aquGRoh0rN3R9itG6rUvvj0QPCHGdFsQSBiBt6GaPdW50xqA==","signatures":[{"sig":"MEQCIDgT9OZoFUDbr+pWiTJMNn2FvTY39EPYrMn66uZmrRTwAiBvkGf0KK9A3ON6M5vQ1jQpbiYE/ApKk6RtHX89ZlXyKA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":8254815},"type":"module","engines":{"node":">=18"},"gitHead":"0f9ab11594b2ef61087ef7b37c0700e39e0b34a5","scripts":{"test":"node scripts/run-tests.mjs","build":"node scripts/build.mjs","prepack":"node scripts/build.mjs >/dev/null","typecheck":"tsc --noEmit","test:package":"node scripts/test-package-consumer.mjs","install:browser":"playwright install chromium"},"_npmUser":{"name":"lixintao","email":"li18566230175@gmail.com"},"_npmVersion":"10.8.2","description":"通用开发验证与 AI UI 测试工具 —— 提供 development-test、test-review、ai-ui-test Skills 及确定性浏览器 CLI","directories":{},"_nodeVersion":"20.20.1","dependencies":{"ajv":"^8.17.1","yaml":"^2.6.1","pngjs":"^7.0.0","pixelmatch":"^6.0.0","playwright":"^1.48.0","@playwright/test":"^1.62.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.23.12","esbuild":"^0.28.1","typescript":"^5.8.0","@types/node":"^20.14.0"},"optionalDependencies":{"@midscene/web":"^0.16.10"},"_npmOperationalInternal":{"tmp":"tmp/ai-test_0.0.1_1787122319803_0.6409441476566315","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@aafe/ai-test","version":"0.0.2","description":"通用开发验证与 AI UI 测试工具 —— 提供 development-test、test-review、ai-ui-test Skills 及确定性浏览器 CLI","type":"module","bin":{"uitest":"dist/uitest.js"},"publishConfig":{"access":"public"},"engines":{"node":">=18"},"scripts":{"typecheck":"tsc --noEmit","build":"node scripts/build.mjs","prepack":"node scripts/build.mjs >/dev/null","test":"node scripts/run-tests.mjs","test:package":"node scripts/test-package-consumer.mjs","install:browser":"playwright install chromium"},"dependencies":{"@playwright/test":"^1.62.1","ajv":"^8.17.1","pixelmatch":"^6.0.0","playwright":"^1.48.0","pngjs":"^7.0.0","yaml":"^2.6.1"},"devDependencies":{"@types/node":"^20.14.0","esbuild":"^0.28.1","tsx":"^4.23.12","typescript":"^5.8.0"},"optionalDependencies":{"@midscene/web":"^0.16.10"},"_id":"@aafe/ai-test@0.0.2","gitHead":"0f9ab11594b2ef61087ef7b37c0700e39e0b34a5","_nodeVersion":"20.20.1","_npmVersion":"10.8.2","dist":{"integrity":"sha512-sRCkx4YNUx7VRp1AW/9LAusw9ynY+G4v7bFXxi38voqZ+h/oW+aR9EXhbkl38xR/yr4cs1sKJuRxQAw90CFfVA==","shasum":"536dcae5271c75649fffcd55f95badc5d4b4fe14","tarball":"https://registry.npmjs.org/@aafe/ai-test/-/ai-test-0.0.2.tgz","fileCount":36,"unpackedSize":8274107,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQC6g7xJKTjODElIxY2kRhJBQ1fWXWQFdJQLJ58JoArI+QIhAOPKa06kCg8BQvGkNPXYpn1KQJhiR9JKKQSAivjgx6nV"}]},"_npmUser":{"name":"lixintao","email":"li18566230175@gmail.com"},"directories":{},"maintainers":[{"name":"lixintao","email":"li18566230175@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ai-test_0.0.2_1787128015635_0.0734292121715836"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-19T06:51:59.380Z","modified":"2026-08-19T08:26:56.091Z","0.0.1":"2026-08-19T06:51:59.974Z","0.0.2":"2026-08-19T08:26:55.861Z"},"description":"通用开发验证与 AI UI 测试工具 —— 提供 development-test、test-review、ai-ui-test Skills 及确定性浏览器 CLI","maintainers":[{"name":"lixintao","email":"li18566230175@gmail.com"}],"readme":"# uitest —— 通用 AI UI 测试工具\n\n跨前端项目复用的 AI UI 自动化测试能力。**Agent 基于需求和代码影响分析直接编写 YAML Case → dist 入口在真浏览器里执行 → 断言裁决锁在 Case 与确定性证据，模型不能为变绿改断言**。\n\n- 用例真相源：由 Agent 直接维护的 Midscene 原生 YAML **受控超集**（不自造跨引擎 DSL）。\n- 引擎按用例类型分流：结构化证据类走**纯 Playwright**（零 token），UI 流程类走 **Midscene 即时操作**。\n- 断言由 Node 侧枚举 check 裁决；`aiAssert` 视觉判断只能 `policy: auxiliary`。\n- 结果四态：`passed | failed | blocked | uncertain`；环境/依赖/模型未就绪一律 `blocked`，绝不误判 `failed`。\n\n## 职责边界\n\n- **Skill / LLM**：负责理解需求、分析受影响入口、选择已有 Case、直接编写或修改 YAML，并解释报告中的四态结果。\n- **dist 入口**：只负责 `preflight`、`init` / `doctor` / `config`、`case validate`、`smoke`、`run` / `run-all`、`inventory` / `from-changes` / `from-pr`、确定性断言和证据、Figma visual-diff、Midscene `explore` / 语义执行、`report`。每个命令对应 `dist/` 下一个独立预编译 JS 入口（如 `dist/run.js`、`dist/from-changes.js`、`dist/from-pr.js`、`dist/inventory.js`）。\n- **测试闭环分工**：`development-test` 指导主 Agent 选择、编写、执行、诊断、修复和重跑逻辑单元、组件/集成与浏览器 E2E 测试；浏览器层委托 `ai-ui-test`；独立测试审查者只是复杂或高风险场景的可选输入。\n- **探索不裁决**：`explore` 只返回候选定位、轨迹、截图和语义证据，不输出 `passed` / `failed`；只有 `run` / `run-all` 中的确定性断言报告可以裁决 Case。\n- **Figma 边界**：Figma 能力只服务 `visual-diff` / 设计还原度基线，不生成或修改业务代码。\n\n## 架构分层\n\n工具按职责分四层，各层边界清晰、可独立演进：\n\n| 层 | 职责 | 关键模块 | 对外暴露 |\n| --- | --- | --- | --- |\n| **用例层** | 用例真相源、Schema 校验 | `src/case/` — `validate.ts`、`load.ts`、`types.ts` | `case validate/ls/show` |\n| **引擎层** | 浏览器驱动、证据采集、即时操作 | `src/engine/` — `router.ts`（分流）、`playwright/`、`midscene/`、`shared-steps.ts` | `run/run-all`（内部调度） |\n| **断言层** | 枚举 check 裁决、四态判定 | `src/assert/` — `registry.ts`、`evaluate.ts`、`visual-diff.ts`、`style-equals.ts` | 断言结果嵌入 `run` 报告 |\n| **闭环层** | 冒烟、报告、探索入口 | `src/commands/` — `smoke.ts`、`report.ts`、`explore.ts` | `smoke/report/explore` |\n\n**数据流向**：用例层定义 YAML 真相源 → 引擎层按 `engine` 字段分流执行 → 断言层基于枚举 check 产出四态裁决 → 命令层提供冒烟、回归、报告与探索入口；CI 门禁由 `case validate --strict` + `run-all` 组合完成。`case validate`（含 `--ci`）只证明结构合法，`smoke` 只证明入口冒烟，二者都不代表 E2E passed；只有真实 `run` / `run-all` 报告中的 `passed` 才表示 E2E 通过。\n\n**引擎分工**：结构化证据类用例走纯 Playwright（零 token）；UI 流程类用例走 Midscene（AI 即时操作）；`engine` 缺省时由 `router.ts` 自动分流。\n\n**非侵入设计**：`init` 缺省跳过已存在的骨架文件（`--apply` 强制覆写）；`run` 缺省有头弹窗（`--headless` 强制无头，CI 自动降级）；所有命令尊重 `--project` 与 `--json` 全局参数。\n\n当前命令、Case schema、报告、Figma visual-diff 和 CI 契约以本文为准。\n\n## 安装\n\n框架仓库内开发推荐按锁文件安装后通过 dist 预编译入口使用：\n\n```bash\ncd /path/to/AITest\nnpm ci\nnpm run build          # esbuild 编译 src/entry/*.ts → dist/*.js\nnpx playwright install chromium   # 纯 Playwright 用例需要\n```\n\n作为 npm 包消费时：\n\n```bash\nnpm install --save-dev @aafe/ai-test\nnpx uitest init\nnpx skills add ./node_modules/@aafe/ai-test\n```\n\n包提供 `uitest` 可执行入口（`bin` → `dist/uitest.js`），子命令名与 dist 脚本一一对应：\n`npx uitest init` 等价于 `node node_modules/@aafe/ai-test/dist/init.js`，`npx uitest from-pr --pr <url>`\n等价于 `node node_modules/@aafe/ai-test/dist/from-pr.js --pr <url>`。README 里的两段式写法\n（`case ls`、`report open`、`auth export`）也被接受。`npx uitest --help` 列出全部子命令。\n\n`npx skills add` 从发布包的 `skills/development-test/`、\n`skills/test-review/` 和 `skills/ai-ui-test/` 安装三个通用 Agent Skills。`init` 除 E2E\n骨架外，还会写入薄 Cursor Rule / Skill 适配层（`.cursor/rules/uitest-from-pr.mdc`、\n`.cursor/skills/ai-ui-test/SKILL.md`），以便在 Agent 中说「分析此PR <PR URL>影响并生成测试用例」\n时触发 `npx uitest from-pr`；`init` 不写入 MCP 或 `.codebuddy/` 配置。单测环境（Vitest）\n由 `development-test` Skill 的 LLM 自检流程按需配置。采用 `.ai/` canonical 的项目应把\n通用 Skill 包装成项目适配层后再用自己的同步机制分发。\n\n发布前黑盒验证：\n\n```bash\nnpm run test:package\nUITEST_VERIFY_SKILLS_CLI=1 \\\nUITEST_SKILLS_REGISTRY=https://registry.npmjs.org \\\nnpm run test:package\n```\n\n第一条不调用外部 Skills CLI，验证 tgz 安装、dist 入口和三个 Skill 发布资产；第二条额外调用标准\nSkills CLI 验证真实发现。企业 npm 镜像未代理 `skills` 包时需显式指定可用 registry。\n\n## 项目快速接入\n\n```bash\nnode dist/init.js       # 作为 npm 包安装时：npx uitest init\nnode dist/doctor.js\nnode dist/validate.js --strict\nnode dist/run.js <case-id>\n```\n\n接入门禁与场景路由（skills 接入方式下每次触发 E2E 的第一条命令）：\n\n```bash\nnode dist/preflight.js --json\nnode dist/preflight.js --auto-init --json\n```\n\n`preflight` 回报接入方式（`npm` / `skills`）、初始化检查项和建议场景。退出码 0=就绪，3=缺 `project.yaml` / `cases/` / `.AITestEnv` / baseUrl。`--auto-init` 只补确定性骨架，不下载浏览器、也不预填被测地址：`init` 写入的 `UITEST_BASE_URL` 为空值，未填真实地址前门禁保持 blocked；历史项目里残留的 `http://localhost:8080` 占位值同样按未配置处理，确实跑在该地址时改由 shell/CI 注入以示确认。三种分析场景：\n\n| 场景 | 适用 | 命令 |\n| --- | --- | --- |\n| 存量功能分析 | 新接入项目铺底 | `node dist/inventory.js --json` |\n| 当前变更 | 改完还没提交 / 未开 PR | `node dist/from-changes.js --json` |\n| 指定 PR | 多 commit 大任务上线前验证 | `node dist/from-pr.js --pr <url> --json` |\n\n分析当前工作区变更：\n\n```bash\nnode dist/from-changes.js --json\nnode dist/from-changes.js --staged --json\nnode dist/from-changes.js --base origin/master --json\n```\n\n影响包落在 `.ui-ai/changes/latest.json`，含 `layers`（最小充分测试层）与 `verification`（新功能 / 影响范围 / 既有功能回归 / 人工确认四类清单）。`layers.primary: unit` 时不该升 E2E，交由 `development-test` 走单测链路。\n\n按 GitHub 或工蜂 PR 补前端 E2E：\n\n```bash\nnode dist/from-pr.js --pr https://github.com/<owner>/<repo>/pull/<n> --json\nnode dist/from-pr.js --pr https://<host>/<group>/<repo>/merge_requests/<n> --json\n```\n\n内核只产出 `.ui-ai/pr-impact/` 影响包；Agent 按影响包与源码编写 `tests/ui-ai/cases/` 后执行 `validate` → `smoke` → `run`。`--run` 只复跑已命中的既有 Case。\n\n### PR 访问令牌\n\n访问私有仓库需要令牌。**把它写进本地配置 `.AITestEnv` 即可，配置一次长期复用**：\n\n```bash\n# .AITestEnv（init 已生成，且已加入 .gitignore）\nGITHUB_TOKEN=<token>         # GitHub；也接受 GH_TOKEN\nGIT_PRIVATE_TOKEN=<token>    # 工蜂；也接受 PRIVATE_TOKEN / GITLAB_TOKEN\n```\n\n优先级为 `--token-stdin` > shell 环境变量 > `.AITestEnv`，因此 CI 用 Secret 注入同名环境变量即可覆盖本地值，无需改文件：\n\n```bash\nexport GITHUB_TOKEN=<token>                                    # 临时覆盖\nprintf %s \"$GITHUB_TOKEN\" | node dist/from-pr.js --pr <url> --token-stdin --json   # 不落盘的一次性用法\n```\n\n令牌可以留在本机，但**绝不能进入 GitHub / 工蜂**，为此有四道约束：\n\n1. `.AITestEnv` 由 init 写入 `.gitignore`；`.AITestEnv.example` 只含空值，可安全提交。\n2. `.AITestEnv` 一旦被 git 跟踪，`.gitignore` 即失效——`preflight` 会以必需项 `aitest-env-untracked` 报错，`from-pr` / `inventory --pr` 直接拒绝执行，并给出 `git rm --cached .AITestEnv` 的修复命令。\n3. **不支持 `--token <值>`**，传入直接 `blocked`：命令行明文会被 `ps`（同机其他用户可见）、shell 历史、CI 日志和 Agent 对话记录留存。\n4. 令牌读入后登记为脱敏密钥，其字面量在 stdout、影响包、报告和报错中一律替换为 `***`；`ghp_` / `github_pat_` / `glpat-` 等平台格式即使未登记也会被抹掉。\n\nAgent 侧的额外约束（不索要明文、不拼进命令、不写入用例与报告、不回显）见 `skills/ai-ui-test/pr-to-case.md`。\n\n存量项目一次性全量冒烟：\n\n```bash\nnode dist/inventory.js --json\nnode dist/inventory.js --update --json\n```\n\n首次扫描知识库与路由并落盘 `SMOKE-*.yaml`，写入 `project.yaml` 的 `inventory.initialized`。之后必须 `--update`（可叠加 `--pr`）。\n\n入口会从当前目录向上查找 `tests/ui-ai/project.yaml`。找不到时会明确提示先执行\n`init`，不会静默执行框架仓库中的示例 Case。`case validate` 等离线\n校验可在未启动被测环境时执行；运行浏览器命令前必须提供 `baseUrl`，或设置\n`baseUrlEnv` 指向的环境变量，缺失时会明确报错而不会回退 demo。\n\n纯格式或 schema 修改可以只执行 `validate`，但必须报告”浏览器 E2E 未执行”。需求行为 Case 的完成门禁是 `validate → smoke → run`；`run --dry-run` 只生成解析/执行计划，报告为 `uncertain` 且 `e2eExecuted: false`，不能作为 E2E 通过证据。本地默认有头与 CI 自动无头只是浏览器展示方式不同，两者在不带 `--dry-run` 时都是真实 `run`。\n\n### 运行产物目录\n\n接入项目生成的 Case 属于项目测试代码，默认放在\n`<projectRoot>/tests/ui-ai/cases/`。运行时截图、差异图、trace、视频和报告统一放在\n`<projectRoot>/.ui-ai/reports/<reportId>/`；探索模式截图放在\n`<projectRoot>/.ui-ai/explore/`。`init` 会确保项目 `.gitignore` 包含\n`.ui-ai/`，这些临时产物不应提交。\n\n本地密钥与 baseUrl 等真实值写在项目根 `.AITestEnv`（`init` 生成并加入\n`.gitignore`，**勿提交**）；可提交的空值模板为 `.AITestEnv.example`。入口会为每个\nresolved context 创建独立的只读环境快照（shell / CI 环境变量优先），不会把\n`.AITestEnv` 永久写入 `process.env`。业务项目通用 `.env` 不会被读取。CI 仍用 Secret / 流水线\n变量注入，不要依赖仓库内的真实密钥文件。\n\n用于正式视觉回归的 Figma 基线属于测试输入，默认放在\n`<projectRoot>/tests/ui-ai/figma-baselines/`，应按团队基线评审流程提交。\n\n### 仓库内部测试 fixture\n\n`fixtures/demo-project/` 仅用于框架自身回归测试，不包含在 npm 发布包中，也不会\n作为消费项目的运行时 fallback。它覆盖以下场景：\n\n`fixtures/demo-project/demo/` 内置场景页，并配有零 token 的 `engine: playwright` 用例：\n\n| 页面 | 场景 | 用例 | file:// 可跑 |\n| --- | --- | --- | --- |\n| `test.html` | 多实例隔离 / hook 值比对 | `MI-DEMO-001`、`MI-DEMO-HOOK-001` | ✅ |\n| `async-load.html` | 异步延迟渲染，`wait visible` 必须真等 | `DEMO-ASYNC-001` | ✅ |\n| `form.html` | 表单 `input`/`tap` + 提交结果裁决 | `DEMO-FORM-001` | ✅ |\n| `console-error.html` | 控制台错误（`?fail=console\\|pageerror` 触发） | `DEMO-CONSOLE-001` | ✅ |\n| `network-error.html` | HTTP 错误（`?fail=404\\|500` 触发） | `DEMO-NETWORK-001` | ❌ 需 HTTP |\n| `api-assert.html` | `waitApi` + `api-equals` 接口 JSON 断言 | `DEMO-API-001` | ❌ 需 HTTP |\n\n网络场景必须走 HTTP（`file://` 下浏览器直接拦截 fetch，不产生 HTTP 响应事件）：\n\n```bash\nnode scripts/serve-demo.mjs                 # 仅绑 127.0.0.1，静态根 = demo/\nUITEST_DEMO_BASE_URL=http://127.0.0.1:4321/ node dist/run-all.js\n```\n\n`console-error.html` / `network-error.html` 的 `?fail=` 开关用于**反向验证**「断言真的能抓错」：把用例 `entry.path` 临时加上开关重跑，应判 `failed` 而非 `passed`。\n\n## 命令面\n\n| 命令 | 作用 |\n| --- | --- |\n| `init` | 在当前目录生成 `tests/ui-ai/` 骨架 |\n| `doctor` | 环境体检：node / playwright / @midscene/web / 模型密钥 / CDP 探活 / 项目配置 |\n| `status` / `config` | 用例分流概览 / 解析后配置（脱敏，只显示 env 名） |\n| `case validate [id]` | 只做 Schema + 领域结构校验（缺省校验全部）；`--strict`（`--ci` 为其别名）严格模式（warn 也判 fail），不执行浏览器、不产生 E2E passed |\n| `case ls` / `case show <id>` | 列出 / 查看用例 |\n| `smoke <entry>` | 零作者入口冒烟：导航并检查错误遮罩 + console + HTTP；无需写 Case，但不产生 E2E passed |\n| `run <id>` | 真实执行用例并产生 E2E 四态报告（`--mode ai\\|cached`、`--timeout`；**默认有头弹窗**，CI/无显示器自动降级无头）；`--dry-run` 例外，仅解析计划且不得 passed |\n| `run-all` | 批量执行（按 `budget.concurrency` 并发，任何非 passed 结果均非零退出，避免 blocked/uncertain 被 CI 误放行；同样默认有头，CI 环境自动降级无头，也可 `--headless` 显式无头） |\n| `report open <reportId>` | 打印报告 HTML/JSON 路径 |\n| `auth export [--cdp][--out]` | 导出作者态已登录 CDP 会话为 `storageState`（缺省落 `fixtures/auth/storageState.json`，自动 gitignore） |\n| `baseline check/update/figma` | Visual-diff 基线检查、更新和 Figma 基线拉取 |\n| `explore <entry> --intent <text>` | 使用 Midscene 对页面做窄职责语义定位/探测，输出候选定位、轨迹、截图/证据；不写 Case、不修改断言、不输出 passed/failed |\n| `preflight` | 检测接入方式（npm / skills）、校验初始化配置并路由分析场景；`--auto-init` 缺骨架时自动补 `init`（不下载浏览器），`--scenario` 显式指定场景。退出码 0=就绪 / 3=blocked |\n| `from-changes` | 按本地 git 变更（工作区 / `--staged` / `--base <ref>`）做前端影响分析并落盘 `.ui-ai/changes/`；`--run` 只复跑已命中的既有 Case |\n| `from-pr --pr <url>` | 按 GitHub PR 或工蜂 MR 链接拉取变更，结合知识库做前端影响分析并落盘 `.ui-ai/pr-impact/`；`--run` 只复跑已命中的既有 Case，新 YAML 由 Agent 编写后再 `validate` / `run` |\n| `inventory` | 存量项目全量冒烟：扫描知识库/路由并落盘 `SMOKE-*.yaml`，在 `project.yaml` 标记 `inventory.initialized`；再次执行需 `--update`（可叠加 `--pr`） |\n\nCI 门禁不再是独立命令：离线结构校验用 `validate --strict`（`--ci` 为其别名），浏览器裁决用 `run-all`（任何非 passed 结果均非零退出）。\n\n作者态探测能力已收敛为 `explore`。底层仍复用 CDP attach、SoM 截图、即时证据与 Midscene `aiLocate`，但 `attach` / `som` / `snapshot` / `eval` 不再作为零散命令公开。`explore` 只辅助 Agent 编写 YAML，不能替代 Case 裁决。\n\n所有入口支持 `--project <dir>`（指定 `tests/ui-ai`）；支持 `--json` 的入口会输出结构化结果。`nextActions` 只在当前命令确有后续建议时出现，不是所有 JSON 输出的固定字段。\n\n## 用例格式（Midscene 原生超集）\n\n见 `fixtures/demo-project/tests/ui-ai/cases/`：\n\n- 结构化证据类（`engine: playwright`）：`steps` 用 `navigate/wait/waitApi/tap/input`，断言用枚举 check（`api-equals`/`console-no-errors`/`network-no-http-errors`/`url-matches`/`dom-exists`/`dom-absent`/`style-equals`/`visual-diff`/`design-review`…）。\n- UI 流程类（`engine: midscene`）：`steps` 用 `aiTap/aiInput/aiWaitFor/aiLocate`，强证据断言裁决，`visual-*` 仅 `policy: auxiliary`。\n- `engine` 缺省/`auto` 时自动分流：含即时操作 → midscene，否则 → playwright。\n- 显式 `engine: playwright` 不得在 `steps` 中出现任何 `ai*` 步骤；`engine: midscene` 的主步骤可混合结构化动作和即时操作。\n\n`validate` 会递归检查所有可执行和裁决字段（入口、步骤、断言的\n`selector/hook/path/expect/baseline/figma/mask` 等）：`REPLACE_*`、`TODO`、\n`待替换`、`PLACEHOLDER` 等人工模板出现在这些字段时直接报错，但不会扫描\n`title`、`description`、`sources` 正文。环境变量运行时取值为空仍按 `blocked`\n处理，不属于静态语法错误。\n\n`run` 会在浏览器与导航前再次检查残留占位符，防止绕过 validate。\n\n`waitApi` + `api-equals`（看板/接口 JSON 确定性通道）：\n\n```yaml\nsteps:\n  - action: tap\n    target: '#load-btn'\n  - action: waitApi\n    target: '/api/ok.json'          # URL 子串或 /regex/\nassertions:\n  - id: assert-api\n    check: api-equals\n    target: '/api/ok.json'\n    path: data.panels.0.id          # 可选深路径\n    expect: panel-1\n```\n\n仅当用例含 `waitApi` 或 `api-equals` 时才缓冲响应 body（同 host、JSON、≤512KB）；证据不足 → `uncertain`，绝不假通过。\n\n## 项目接入\n\n`init` 生成 `tests/ui-ai/project.yaml`：\n\n```yaml\nbaseUrlEnv: UITEST_BASE_URL     # 只声明 env 名，不写真实地址/密钥\nallowedHosts: []                # host 白名单，空=不限制\nmodel:\n  apiKeyEnv: OPENAI_API_KEY\n  baseUrlEnv: OPENAI_BASE_URL\n  modelNameEnv: MIDSCENE_MODEL_NAME\nbudget:\n  concurrency: 2\n  tokenBudgetPerCase: 100000\nauth:\n  mode: none                          # 公开页面默认 none；需登录改 manual\n  # storageStatePath: fixtures/auth/storageState.json  # mode=manual 时取消注释\n```\n\n认证能力仅支持手工 Playwright `storageState`：先运行 `auth export`，再配置\n`auth.storageStatePath`。`auth.mode: auto`、登录页/按钮 selector 和\n`storageStateTTL` 尚未实现，配置解析会明确拒绝并给出迁移提示，不会静默假支持。\n`auth.mode: none` 会完全禁用登录态解析与注入，不能同时配置任一 `storageStatePath`。\n\nrequired precondition 是运行前的零副作用门：\n\n- `auth` / `storageState`：检查 `storageStatePath` 已配置、文件存在且为合法 Playwright storage state JSON。\n- `environment`：`name` 必须是环境变量名；从当前项目私有环境快照读取且必须非空。\n- `required: false` 只保留说明，不阻断运行；`environment` 保持既有执行器检查语义。\n\n以上检查都在浏览器启动和导航之前完成。\n\n本地填写真实值时编辑项目根 `.AITestEnv`（已 gitignore），例如：\n\n```bash\nUITEST_BASE_URL=http://localhost:8080\nOPENAI_API_KEY=sk-...\nOPENAI_BASE_URL=https://...\nMIDSCENE_MODEL_NAME=...\n```\n\nCI 挂载时仍从 Secret 注入同名环境变量，无需提交 `.AITestEnv`。\n\nMidscene 0.16 的 OpenAI-compatible 路径必须同时具备 API key、endpoint 和模型名。\n`apiKeyEnv`、`baseUrlEnv`、`modelNameEnv` 可改成项目自己的变量名；运行前会在不覆盖既有\n`OPENAI_API_KEY`、`OPENAI_BASE_URL`、`MIDSCENE_MODEL_NAME` 的前提下作用域化映射给 SDK。\nMidscene 0.16 尚无 per-agent model config，因此 run/explore 会用进程内异步互斥覆盖完整\nSDK/agent 调用；canonical 模型变量、`MIDSCENE_CACHE` 与 `MIDSCENE_RUN_DIR` 均只在该\n作用域内设置，并在 `finally` 恢复原环境，避免并发项目串扰。\n旧项目仍可直接写 `model.modelName`。`config --json` 只显示变量名和 set/unset，\n`doctor --json` 分别显示 `playwrightReady` 与 `midsceneReady`；仅 Playwright\n就绪仍可通过总体体检，但 Midscene 用例与 `explore` 会明确返回 `blocked`。\n\n## CI 挂载\n\n见 `ci/uitest-ci.example.yml`（GitHub Actions 风格，可移植到蓝盾）：核心是安装依赖 + `playwright install` + `validate --strict --json` + `run-all --json`，产物归档 `.ui-ai/reports/`。CI 环境（`CI` 环境变量为真）下 `run-all` 会被自动裁决为无头，无需显式 `--headless`。\n\n**CI 门禁 pre-check**：在 `run-all` 之前加 `validate --strict`（离线、不启动浏览器），提前拦截用例结构错误：\n\n```yaml\n- run: node dist/validate.js --strict --json  # 离线全量校验（零浏览器依赖）\n- run: node dist/run-all.js --json            # 全量执行（需浏览器）\n```\n\n`validate --ci` 是 `--strict` 的别名，语义化标注用于流水线；warn 级别问题也判 fail，防止低质量用例混入。成本约束：结构化类用例零 token 兜底核心回归；UI 流程类用例按 `tokenBudgetPerCase` 与 `concurrency` 控量，缓存优先依赖 Midscene 内建两级缓存（`--mode cached` 做纯回归提速验证）。\n\n## 与已有资产的关系\n\n- `tools/casepilot`：Schema/断言/报告的迁移来源；其 Python browser-use runner 已归档，不进本方案运行时。\n- Playwright MCP / playwright-cli：只用于**作者态探路与诊断**，不当 Case 运行时引擎。\n- 通用开发测试入口位于 `skills/development-test/SKILL.md`，独立对抗测试审查位于\n  `skills/test-review/SKILL.md`，浏览器专项位于 `skills/ai-ui-test/SKILL.md`；三者随 npm 包发布并可用标准 Skills 安装器发现。\n- `init` 写入薄 Cursor Rule / Skill 适配层，不写 MCP，也不猜测其他工具专属配置。\n\n## 测试分层与协作 SOP\n\n- **分层**：逻辑/边界（纯函数、状态机、协议解析、边界条件）归 Vitest；场景渲染、跨模块集成、视觉还原归本工具的浏览器 E2E。两层各自证明不同风险，不互相替代，也不复制同一断言制造覆盖感——详见 `skills/development-test/test-selection.md`。引擎选择（结构化证据用 Playwright、复杂交互用 Midscene、设计还原用 `visual-diff` + Figma 基线、无稳定定位先 `explore`）见上文“用例格式”与 `skills/ai-ui-test/SKILL.md` 的“引擎选择”一节；Midscene 与视觉还原是两件不同的事，不合并处理。\n- **单测按需接入**：`init` 只负责 E2E 骨架初始化，不再涉及 Vitest 脚手架。单测环境（Vitest）由 `development-test` Skill 的 LLM 自检流程按需检查和配置：检查项目是否已有 vitest 依赖和配置文件，缺失时按内联参考模板生成并向用户确认安装。详见 `skills/development-test/SKILL.md` 的”Vitest 环境就绪性检查”。\n- **协作 SOP**：`development-test` 是开发完成后的统一测试入口，负责测试选择、编写、执行、失败分类与修复闭环；高风险或跨模块场景可选调用 `test-review` 做只读独立对抗审查；浏览器 E2E 委托 `ai-ui-test` 完成影响分析、Case 编写、执行与四态解释。三者随本包发布，详见下文“Skill 安装”与各 Skill 的 `SKILL.md`。\n- **升级链**：获取必要证据或裁决遇阻时按固定顺序升级，不得跳级伪装已获取——①确定性自动获取 → ②`explore`/Midscene 窄职责取证 → ③申请最小 testability hook 或用户授权旁路 → ④问人。只在两个窄触发点升级到问人：(a) 缺少必要且不可自动获取的证据、直接影响裁决正确性；(b) 对抗审查指出需求或验收口径存在真歧义。有人在场直接问；无人值守（CI、批处理）时一律 fail-safe——保持 `uncertain`/`NEEDS_CONFIRMATION`，附带可执行的 `nextAction`，以非零退出码入队，不阻塞、不伪造证据、不假通过。\n\n## Skill 协作（AI Agent SOP）\n\n发布包包含三个互补 Skill：\n\n- `development-test`：开发完成后的统一入口，负责测试选择、测试代码、执行、\n  失败分类、产品/测试修复、复审和最多两轮重跑。\n- `test-review`：复杂或高风险变更的只读独立对抗审查，输出正确性条件、测试矩阵、\n  风险反例和人工确认项；不写代码、不执行命令。\n- `ai-ui-test`：浏览器专项，负责 UI 影响分析、YAML Case、Playwright、\n  Midscene 探索、Figma visual-diff 和四态结果解释。\n\n统一报告可在内部将测试层映射为 L1/L2/L3，但面向用户仍使用“逻辑单元测试”、\n“组件/集成测试”和“浏览器 E2E”等明确测试名称。\n\n宿主项目只保留薄 `test-agent` 接线：由独立子 Agent 加载\n`node_modules/@aafe/ai-test/skills/test-review/SKILL.md`，并将需求、diff、审查结论和约束作为输入；\n审查结论交回 `development-test` 继续测试闭环。\n\n浏览器分支的执行顺序：\n\n1. Agent 先根据需求、代码 diff、路由、sources、tags 和现有 Case 做影响分析。\n2. Agent 直接新增或修改 YAML Case，并用 `validate <id> --ci` 校验。\n3. `smoke <entry> --json` → 对受影响入口做零作者冒烟。\n4. `run <id> --json` / `run-all --json` → 执行确定性回归并读取四态裁决。\n5. `explore <entry> --intent \"<目标>\" --json` → 仅在需要作者态探路时生成候选定位和证据，再由 Agent/人工确认是否更新 Case。\n\nAgent 读 `SKILL.md` 后应优先使用保留主链完成验证；新增或修改 Case 由 Agent 基于需求、README/schema 和现有 Case 人工可评审地编辑 YAML，再用 `validate` 校验。\n\n> **历史命令**：早期版本的 `leads`/`dev-done`/`solidify`/`diagnose`/`heal`/`audit`/`watch`/`ci` 等命令已移除，由 `validate` + `run/run-all` + `explore` + `report open` 组合替代。\n","readmeFilename":"README.md"}