@autobest-ui/agent 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +182 -0
- package/bin/sync-assets.mjs +126 -0
- package/bin/sync-assets.test.mjs +64 -0
- package/mcp/azurepr-mcp-bridge/README.md +37 -0
- package/mcp/azurepr-mcp-bridge/azure-devops.js +327 -0
- package/mcp/azurepr-mcp-bridge/config.toml.example +7 -0
- package/mcp/azurepr-mcp-bridge/index.js +65 -0
- package/mcp/azurepr-mcp-bridge/index.test.js +116 -0
- package/mcp/azurepr-mcp-bridge/package.json +22 -0
- package/mcp/rag-mcp-bridge/README.md +42 -0
- package/mcp/rag-mcp-bridge/codex-system-prompt.md +20 -0
- package/mcp/rag-mcp-bridge/config.toml.example +12 -0
- package/mcp/rag-mcp-bridge/index.js +361 -0
- package/mcp/rag-mcp-bridge/index.test.js +56 -0
- package/mcp/rag-mcp-bridge/package.json +21 -0
- package/package.json +44 -0
- package/plugins/autobest-delivery/.codex-plugin/plugin.json +25 -0
- package/plugins/autobest-delivery/.mcp.json +11 -0
- package/plugins/autobest-delivery/README.md +164 -0
- package/plugins/autobest-delivery/assets/delivery-report-template.xlsx +0 -0
- package/plugins/autobest-delivery/mcp-server/npm-shrinkwrap.json +3511 -0
- package/plugins/autobest-delivery/mcp-server/package.json +23 -0
- package/plugins/autobest-delivery/mcp-server/src/paths.mjs +43 -0
- package/plugins/autobest-delivery/mcp-server/src/report.mjs +605 -0
- package/plugins/autobest-delivery/mcp-server/src/runner.mjs +489 -0
- package/plugins/autobest-delivery/mcp-server/src/server.mjs +199 -0
- package/plugins/autobest-delivery/mcp-server/tests/fixture-server.mjs +36 -0
- package/plugins/autobest-delivery/mcp-server/tests/fixtures/basic.feature.mjs +68 -0
- package/plugins/autobest-delivery/mcp-server/tests/mcp-smoke.test.mjs +83 -0
- package/plugins/autobest-delivery/mcp-server/tests/report.test.mjs +254 -0
- package/plugins/autobest-delivery/mcp-server/tests/runner.test.mjs +354 -0
- package/plugins/autobest-delivery/scripts/export-delivery-report.mjs +41 -0
- package/plugins/autobest-delivery/scripts/setup.mjs +295 -0
- package/plugins/autobest-delivery/scripts/setup.test.mjs +145 -0
- package/plugins/autobest-delivery/scripts/start-mcp.mjs +7 -0
- package/plugins/autobest-delivery/skills/code-audit/SKILL.md +24 -0
- package/plugins/autobest-delivery/skills/code-audit/agents/openai.yaml +7 -0
- package/plugins/autobest-delivery/skills/code-craft/SKILL.md +27 -0
- package/plugins/autobest-delivery/skills/code-craft/agents/openai.yaml +7 -0
- package/plugins/autobest-delivery/skills/delivery-loop/SKILL.md +43 -0
- package/plugins/autobest-delivery/skills/delivery-loop/agents/openai.yaml +7 -0
- package/plugins/autobest-delivery/skills/delivery-loop/references/delivery-contract.md +235 -0
- package/plugins/autobest-delivery/skills/e2e-gen-spec/SKILL.md +35 -0
- package/plugins/autobest-delivery/skills/e2e-gen-spec/agents/openai.yaml +7 -0
- package/plugins/autobest-delivery/skills/e2e-ui-checker/SKILL.md +30 -0
- package/plugins/autobest-delivery/skills/e2e-ui-checker/agents/openai.yaml +7 -0
- package/plugins/autobest-delivery/skills/export-report/SKILL.md +66 -0
- package/plugins/autobest-delivery/skills/export-report/agents/openai.yaml +8 -0
- package/plugins/autobest-delivery/skills/ui-structure-guard/SKILL.md +24 -0
- package/plugins/autobest-delivery/skills/ui-structure-guard/agents/openai.yaml +7 -0
- package/skills/README.md +38 -0
- package/skills/common/figma-ui-capture/SKILL.md +197 -0
- package/skills/common/figma-ui-capture/agents/openai.yaml +4 -0
- package/skills/common/ui-prd-scope/SKILL.md +67 -0
- package/skills/common/ui-prd-scope/agents/openai.yaml +4 -0
- package/skills/common/ui-prd-scope/references/scope-schema.md +158 -0
- package/skills/common/ui-prd-scope/scripts/validate-scope-bundle.mjs +302 -0
- package/skills/react/react-code-standards/SKILL.md +78 -0
- package/skills/react/react-code-standards/agents/openai.yaml +4 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { execFile } from 'node:child_process';
|
|
3
|
+
import { chmod, mkdir, mkdtemp, readFile, writeFile } from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import test from 'node:test';
|
|
7
|
+
import { promisify } from 'node:util';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
|
|
10
|
+
const execFileAsync = promisify(execFile);
|
|
11
|
+
const setupPath = fileURLToPath(new URL('./setup.mjs', import.meta.url));
|
|
12
|
+
|
|
13
|
+
async function writeExecutable(filePath, contents) {
|
|
14
|
+
await writeFile(filePath, contents, 'utf8');
|
|
15
|
+
await chmod(filePath, 0o755);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
test('npx 安装器可迁移、重复更新并安全卸载插件', async t => {
|
|
19
|
+
const temporaryRoot = await mkdtemp(path.join(os.tmpdir(), 'autobest-delivery-setup-'));
|
|
20
|
+
const agentHome = path.join(temporaryRoot, 'agent-home');
|
|
21
|
+
const fakeBin = path.join(temporaryRoot, 'bin');
|
|
22
|
+
const codexStatePath = path.join(temporaryRoot, 'codex-state.json');
|
|
23
|
+
const codexLogPath = path.join(temporaryRoot, 'codex.log');
|
|
24
|
+
const oldMarketplaceRoot = path.join(temporaryRoot, 'old-marketplace');
|
|
25
|
+
await mkdir(fakeBin, { recursive: true });
|
|
26
|
+
await writeFile(codexStatePath, JSON.stringify({
|
|
27
|
+
marketplaces: [{ name: 'autobest-team', root: oldMarketplaceRoot }],
|
|
28
|
+
installed: [{ pluginId: 'autobest-delivery@autobest-team' }]
|
|
29
|
+
}), 'utf8');
|
|
30
|
+
|
|
31
|
+
const fakeNpmPath = path.join(fakeBin, 'fake-npm');
|
|
32
|
+
await writeExecutable(fakeNpmPath, `#!/usr/bin/env node
|
|
33
|
+
import { chmod, mkdir, writeFile } from 'node:fs/promises';
|
|
34
|
+
import path from 'node:path';
|
|
35
|
+
const binDir = path.join(process.cwd(), 'node_modules', '.bin');
|
|
36
|
+
await mkdir(binDir, { recursive: true });
|
|
37
|
+
const playwright = path.join(binDir, 'playwright');
|
|
38
|
+
await writeFile(playwright, \`#!/usr/bin/env node
|
|
39
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
40
|
+
import path from 'node:path';
|
|
41
|
+
await mkdir(process.env.PLAYWRIGHT_BROWSERS_PATH, { recursive: true });
|
|
42
|
+
await writeFile(path.join(process.env.PLAYWRIGHT_BROWSERS_PATH, 'chromium.ready'), 'ready');
|
|
43
|
+
\`);
|
|
44
|
+
await chmod(playwright, 0o755);
|
|
45
|
+
`);
|
|
46
|
+
|
|
47
|
+
const fakeCodexPath = path.join(fakeBin, 'fake-codex');
|
|
48
|
+
await writeExecutable(fakeCodexPath, `#!/usr/bin/env node
|
|
49
|
+
import { appendFile, readFile, writeFile } from 'node:fs/promises';
|
|
50
|
+
import path from 'node:path';
|
|
51
|
+
const args = process.argv.slice(2);
|
|
52
|
+
const state = JSON.parse(await readFile(process.env.FAKE_CODEX_STATE, 'utf8'));
|
|
53
|
+
await appendFile(process.env.FAKE_CODEX_LOG, JSON.stringify(args) + '\\n');
|
|
54
|
+
if (args.join(' ') === 'plugin marketplace list --json') {
|
|
55
|
+
process.stdout.write(JSON.stringify({ marketplaces: state.marketplaces }));
|
|
56
|
+
process.exit(0);
|
|
57
|
+
}
|
|
58
|
+
if (args.join(' ') === 'plugin list --json') {
|
|
59
|
+
process.stdout.write(JSON.stringify({ installed: state.installed, available: [] }));
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
if (args[0] === 'plugin' && args[1] === 'remove') {
|
|
63
|
+
state.installed = state.installed.filter(item => item.pluginId !== args[2]);
|
|
64
|
+
} else if (args[0] === 'plugin' && args[1] === 'marketplace' && args[2] === 'remove') {
|
|
65
|
+
state.marketplaces = state.marketplaces.filter(item => item.name !== args[3]);
|
|
66
|
+
} else if (args[0] === 'plugin' && args[1] === 'marketplace' && args[2] === 'add') {
|
|
67
|
+
state.marketplaces.push({ name: 'autobest-team', root: path.resolve(args[3]) });
|
|
68
|
+
} else if (args[0] === 'plugin' && args[1] === 'add') {
|
|
69
|
+
state.installed.push({ pluginId: args[2] });
|
|
70
|
+
} else {
|
|
71
|
+
process.stderr.write('unexpected fake codex command: ' + args.join(' '));
|
|
72
|
+
process.exit(2);
|
|
73
|
+
}
|
|
74
|
+
await writeFile(process.env.FAKE_CODEX_STATE, JSON.stringify(state));
|
|
75
|
+
process.stdout.write('{}');
|
|
76
|
+
`);
|
|
77
|
+
|
|
78
|
+
const env = {
|
|
79
|
+
...process.env,
|
|
80
|
+
AUTOBEST_AGENT_HOME: agentHome,
|
|
81
|
+
AUTOBEST_AGENT_NPM_COMMAND: fakeNpmPath,
|
|
82
|
+
AUTOBEST_AGENT_CODEX_COMMAND: fakeCodexPath,
|
|
83
|
+
FAKE_CODEX_STATE: codexStatePath,
|
|
84
|
+
FAKE_CODEX_LOG: codexLogPath
|
|
85
|
+
};
|
|
86
|
+
const marketplaceRoot = path.join(agentHome, 'marketplaces', 'autobest-team');
|
|
87
|
+
const pluginRoot = path.join(marketplaceRoot, 'plugins', 'autobest-delivery');
|
|
88
|
+
|
|
89
|
+
await execFileAsync(process.execPath, [setupPath], { env });
|
|
90
|
+
|
|
91
|
+
const marketplace = JSON.parse(await readFile(
|
|
92
|
+
path.join(marketplaceRoot, '.agents', 'plugins', 'marketplace.json'),
|
|
93
|
+
'utf8'
|
|
94
|
+
));
|
|
95
|
+
assert.equal(marketplace.name, 'autobest-team');
|
|
96
|
+
assert.equal(marketplace.plugins[0].source.path, './plugins/autobest-delivery');
|
|
97
|
+
const manifest = JSON.parse(await readFile(
|
|
98
|
+
path.join(pluginRoot, '.codex-plugin', 'plugin.json'),
|
|
99
|
+
'utf8'
|
|
100
|
+
));
|
|
101
|
+
assert.equal(manifest.version, '1.0.0+codex.npm');
|
|
102
|
+
assert.equal(
|
|
103
|
+
await readFile(path.join(pluginRoot, 'mcp-server', '.runtime', 'ms-playwright', 'chromium.ready'), 'utf8'),
|
|
104
|
+
'ready'
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
let state = JSON.parse(await readFile(codexStatePath, 'utf8'));
|
|
108
|
+
assert.deepEqual(state.marketplaces, [{ name: 'autobest-team', root: marketplaceRoot }]);
|
|
109
|
+
assert.deepEqual(state.installed, [{ pluginId: 'autobest-delivery@autobest-team' }]);
|
|
110
|
+
|
|
111
|
+
await writeFile(
|
|
112
|
+
path.join(pluginRoot, 'mcp-server', '.runtime', 'ms-playwright', 'cached-browser-marker'),
|
|
113
|
+
'cached'
|
|
114
|
+
);
|
|
115
|
+
await execFileAsync(process.execPath, [setupPath, 'install'], { env });
|
|
116
|
+
state = JSON.parse(await readFile(codexStatePath, 'utf8'));
|
|
117
|
+
assert.deepEqual(state.marketplaces, [{ name: 'autobest-team', root: marketplaceRoot }]);
|
|
118
|
+
assert.deepEqual(state.installed, [{ pluginId: 'autobest-delivery@autobest-team' }]);
|
|
119
|
+
assert.equal(
|
|
120
|
+
await readFile(
|
|
121
|
+
path.join(pluginRoot, 'mcp-server', '.runtime', 'ms-playwright', 'cached-browser-marker'),
|
|
122
|
+
'utf8'
|
|
123
|
+
),
|
|
124
|
+
'cached'
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const commands = (await readFile(codexLogPath, 'utf8'))
|
|
128
|
+
.trim()
|
|
129
|
+
.split('\n')
|
|
130
|
+
.map(line => JSON.parse(line));
|
|
131
|
+
assert.equal(
|
|
132
|
+
commands.filter(args => args.join(' ') === 'plugin marketplace add ' + marketplaceRoot + ' --json').length,
|
|
133
|
+
1
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
await execFileAsync(process.execPath, [setupPath, 'uninstall'], { env });
|
|
137
|
+
state = JSON.parse(await readFile(codexStatePath, 'utf8'));
|
|
138
|
+
assert.deepEqual(state, { marketplaces: [], installed: [] });
|
|
139
|
+
await assert.rejects(readFile(path.join(marketplaceRoot, '.agents', 'plugins', 'marketplace.json')));
|
|
140
|
+
|
|
141
|
+
await t.test('help 不触发安装', async () => {
|
|
142
|
+
const result = await execFileAsync(process.execPath, [setupPath, '--help'], { env });
|
|
143
|
+
assert.match(result.stdout, /autobest-delivery-setup uninstall/);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-audit
|
|
3
|
+
description: 在独立验收者通过后,从固定审查点检查交付改动是否符合仓库规范和功能规格。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 代码审计
|
|
7
|
+
|
|
8
|
+
本 Skill 只执行审计,不修改代码、不执行 E2E,也不提交。开始前完整阅读[共享交付契约](../delivery-loop/references/delivery-contract.md)。
|
|
9
|
+
|
|
10
|
+
## 输入
|
|
11
|
+
|
|
12
|
+
- 可解析的固定审查点。
|
|
13
|
+
- 功能规格及同目录的 scope。
|
|
14
|
+
- 默认审查 `worktree`,也可审查用户明确指定的 `committed` 改动。
|
|
15
|
+
|
|
16
|
+
## 流程
|
|
17
|
+
|
|
18
|
+
1. 只冻结一次审查范围。审查工作树时纳入与任务相关的暂存、未暂存及新增文件,保留并排除无关的既有改动。
|
|
19
|
+
2. 阅读适用的仓库规则、spec、scope、提交列表、改动文件和完整 diff。
|
|
20
|
+
3. 并行启动两个独立审查者:Standards 轴检查已记录的规范、兼容性、缺陷和重大风险;Spec 轴检查遗漏、错误行为和未经支持的范围扩张。两者都必须引用文件位置和约束依据。
|
|
21
|
+
4. 分别汇总两个审查轴,不允许其中一个掩盖另一个,并标出阻断交付的问题。
|
|
22
|
+
5. 按契约写入并复读 `featureDir/review/code-audit-result.json` 和 `.md`。审计 finding、依据、建议、摘要和全部 Markdown 内容必须使用简体中文;JSON 字段名、机器状态、路径、命令和代码标识符保持原始格式,英文原始错误需附中文解释。
|
|
23
|
+
|
|
24
|
+
审查范围为空或不可读,或者无法归属的重叠工作树改动阻碍判断时,结果必须是 `blocked`,不得判为通过。只有两个审查轴都覆盖全部任务文件且 `blockingFindings` 为空时,才能报告 `passed`。
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-craft
|
|
3
|
+
description: 根据功能 spec 和同目录 scope 实现交付需求,或修复独立验收者和代码审计者提供的结构化失败项。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 业务代码实现
|
|
7
|
+
|
|
8
|
+
作为实现者(Maker)完成业务实现。最终需求来自 `spec.md`;同目录的 `scope.md` 提供运行上下文、需求来源和本地设计资产。开始前完整阅读[共享交付契约](../delivery-loop/references/delivery-contract.md)。
|
|
9
|
+
|
|
10
|
+
## 输入
|
|
11
|
+
|
|
12
|
+
- 必需的功能 spec 和同目录 scope。
|
|
13
|
+
- 仅在修复回合读取:最新验收者 `failed` 结果、测试编写者的 `implementation` 阻断项,或代码审计阻断结果。
|
|
14
|
+
|
|
15
|
+
输入缺失或冲突,或者工作需要扩大用户授权范围时,必须按事实返回 `blocked`。
|
|
16
|
+
|
|
17
|
+
## 流程
|
|
18
|
+
|
|
19
|
+
1. 阅读仓库规则、spec、scope、目标模块、相邻实现,以及仅与改动 UI 映射的视觉基准。修复回合中,把每个失败项追溯到业务代码中的原因。
|
|
20
|
+
2. 实现 spec,或修复已提供失败项的直接原因。保留已经通过的行为和用户既有改动。涉及 React 或 CSS 布局时使用 `$ui-structure-guard`。
|
|
21
|
+
3. 执行仓库最小充分的 lint、类型检查、构建和现有测试反馈。仓库没有测试框架时不得自行引入。
|
|
22
|
+
4. 可通过隔离运行器执行冻结场景以获得开发反馈,但实现者不得宣称验收通过,也不得修改冻结输入或其他角色的报告。
|
|
23
|
+
5. 按共享契约写入并复读 `featureDir/delivery/code-craft-result.json`。
|
|
24
|
+
|
|
25
|
+
## 边界
|
|
26
|
+
|
|
27
|
+
不执行最终验收、代码审计、提交、推送、发布、依赖规避或范围外重构。只返回 `implemented` 或 `blocked`,并记录实际改动文件、验证结果和阻断项。
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: delivery-loop
|
|
3
|
+
description: 围绕功能 spec 调度相互隔离的实现者、E2E 测试编写者、可见 UI 验收者和代码审计者;Blocked 时等待人工决策,直到交付通过或用户终止。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 交付闭环
|
|
7
|
+
|
|
8
|
+
以选定的 `spec.md` 和同目录 `scope.md` 为入口协调独立角色。spec 决定最终预期;scope 提供需求来源、运行事实和视觉资产。开始前完整阅读[交付契约](references/delivery-contract.md)。
|
|
9
|
+
|
|
10
|
+
## 输入
|
|
11
|
+
|
|
12
|
+
- 必需的功能 `spec.md`,并定位同目录的 `scope.md`。
|
|
13
|
+
- 可选的实现者最大回合数,默认 `3`,包含首次实现。
|
|
14
|
+
- 可选的审计固定点,默认为启动时的 `HEAD`。
|
|
15
|
+
|
|
16
|
+
只有交付通过且用户明确要求时,才允许提交或推送。
|
|
17
|
+
|
|
18
|
+
## 角色隔离
|
|
19
|
+
|
|
20
|
+
每个实现者、测试编写者、验收者和代码审计者回合都必须使用全新的独立子代理。实现者使用 `$code-craft`,测试编写者使用 `$e2e-gen-spec`,验收者使用 `$e2e-ui-checker`,代码审计者使用 `$code-audit`。角色不可用时不得由编排者自行审查替代。
|
|
21
|
+
|
|
22
|
+
## 流程
|
|
23
|
+
|
|
24
|
+
1. **预检:**阅读 spec、scope、仓库规则、视觉映射、固定审查点和当前工作树。在启动实现者前调用 `autobest-delivery` MCP 的 `check_environment` 工具。若环境未就绪,保留工具证据、保持实现者回合数为零并进入 `awaiting-decision`,由用户选择重试、跳过环境验收并记录 waiver,或停止。按契约自行解析可发现的 URL、命令、路由变体、数据准备和定位器,不为这些可发现信息询问用户。
|
|
25
|
+
2. **状态:**将 `delivery/delivery-state.json` 写为 `running`,记录当前阶段、回合数、固定审查点、既有改动和当前产物。每次状态流转都更新该文件。
|
|
26
|
+
3. **实现者:**启动全新的 `$code-craft` 子代理。首回合只传入 spec 和 scope;修复回合只额外传入最新验收失败结果、实现阻断项或代码审计阻断结果。启动时增加实现者回合计数。
|
|
27
|
+
4. **测试编写者:**缺少 `e2e/e2e.feature.mjs` 时,启动全新的 `$e2e-gen-spec` 子代理。旧 `.spec.ts` 不是标准输入,应保持不变。测试编写者必须使用隔离 scene,并在冻结前通过同一运行器 preflight;场景代码、定位器、弹窗、fixture 或等待条件造成的 preflight Blocked 由测试编写者在草稿阶段自行修正并重跑。报告 `ready` 后才冻结 `.mjs`。环境、spec 或实现阻断进入 `awaiting-decision`,由用户选择补充输入、重试、分类、记录 waiver 或停止。
|
|
28
|
+
5. **验收者:**启动全新的 `$e2e-ui-checker` 子代理,并传入 spec、scope、冻结场景和迭代编号。验收者必须通过隔离 MCP 运行器以 headed 模式执行全部场景,然后只审查 spec 已映射的视觉基准。`passed` 时继续;`failed` 且仍有回合时返回实现者。
|
|
29
|
+
6. **Blocked 决策:**验收者返回 `awaiting-decision` 时,先确认运行器已在阻断 scene 后继续尝试其余独立 scene,再将交付状态写为同名状态,记录该验收代理,保留可见浏览器与证据,并把契约定义的六个决策及其 effect 交给用户。必须明确 `skip` 是接受阻断 scene 的缺失证据并继续审计,不是从异常语句下一行恢复。收到选择后,把决议回传给同一个验收代理,由它调用 `resolve_blocked_run`;验证 `human-decision.json` 后再按其 `effect` 继续。不得自行修改冻结场景、替用户接受风险或把 Blocked 当作终止;只有 `stop` 是终止决策。
|
|
30
|
+
7. **代码审计:**验收通过,或用户 `accept`/`skip` 形成 waiver 后,从固定审查点启动全新的 `$code-audit` 子代理。存在阻断项且仍有回合时返回实现者,修复后重新执行完整验收。没有阻断项时完成交付。
|
|
31
|
+
8. **完成:**标准场景、最新验收、最新代码审计、人工决议、状态与产物必须一致且不存在未授权提交。完整验收通过写入 `passed`;存在 `accept`/`skip` waiver 且审计通过写入 `passed-with-waivers`;只有用户明确选择 `stop` 才写入终止态 `blocked`。Maker 回合耗尽时先进入 `awaiting-decision`,不得替用户停止。
|
|
32
|
+
|
|
33
|
+
## 控制规则
|
|
34
|
+
|
|
35
|
+
- 运行器或验收工具故障不消耗新的实现者回合,也不得交给实现者修复。
|
|
36
|
+
- `blocked` 是运行器证据状态,不是编排终止指令;编排器必须进入 `awaiting-decision` 并等待用户。
|
|
37
|
+
- 只有用户选择 `test_defect` 后,才能启动全新的测试编写者修订并重新冻结场景;重试不得修改冻结输入。
|
|
38
|
+
- 上一条只约束已经冻结的场景;首次冻结前的 preflight 草稿由测试编写者自行修正机械缺陷。
|
|
39
|
+
- 可见 Blocked 会话过期后按 `retry` 处理,不得推断用户已接受或跳过。
|
|
40
|
+
- `resolve_blocked_run` 必须由创建 `blockedSessionId` 的同一个验收代理调用;返回 `not-found` 时记录会话失效并按 `retry` 处理。
|
|
41
|
+
- 任何业务代码修复都会使之前的验收证据失效。
|
|
42
|
+
- 实现者和验收者将 spec、scope、视觉基准、冻结场景和既有角色产物视为只读。
|
|
43
|
+
- 只管理和清理由本流程或当前角色启动的进程。
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# 交付契约
|
|
2
|
+
|
|
3
|
+
本契约是插件内各交付 Skill 与 `autobest-delivery` MCP 运行器之间产物交接的唯一事实来源。除非另有说明,所有路径都相对于选定的功能目录。
|
|
4
|
+
|
|
5
|
+
## 产物所有权
|
|
6
|
+
|
|
7
|
+
| 产物 | 所有者 | 其他角色权限 |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| `spec.md` | 产品输入 | 只读 |
|
|
10
|
+
| `scope.md` | 范围输入 | 只读 |
|
|
11
|
+
| 已映射视觉基准 | 产品或设计输入 | 只读 |
|
|
12
|
+
| 业务源码 | 实现者 | 测试编写者、验收者和代码审计者只读 |
|
|
13
|
+
| `e2e/e2e.feature.mjs` | 测试编写者 | 创建后冻结 |
|
|
14
|
+
| `e2e/author-result.json` | 测试编写者 | 只读 |
|
|
15
|
+
| `e2e/runs/iteration-NN/runner-result.json` | 隔离运行器 | 只读 |
|
|
16
|
+
| `e2e/runs/iteration-NN/human-decision.json` | 隔离运行器 | 只读 |
|
|
17
|
+
| `checker/checker-result.json` 和 `.md` | 验收者 | 只读 |
|
|
18
|
+
| `review/code-audit-result.json` 和 `.md` | 代码审计者 | 只读 |
|
|
19
|
+
| `delivery/delivery-state.json` | 编排者 | 只读 |
|
|
20
|
+
| `delivery/code-craft-result.json` | 实现者 | 只读 |
|
|
21
|
+
|
|
22
|
+
旧 `e2e.feature.spec.ts` 文件不是本契约的可执行输入。保持这些文件不变;缺少标准 `.mjs` 场景时生成新场景。
|
|
23
|
+
|
|
24
|
+
## 人读输出语言
|
|
25
|
+
|
|
26
|
+
角色自行撰写的 Markdown、结论、摘要、检查说明、期望、实际结果、阻断原因和审计 finding 一律使用简体中文。`checker-result.md`、`code-audit-result.md` 等人读报告不得使用英文标题、英文叙述或 `Passed` / `Failed` 等英文展示状态。
|
|
27
|
+
|
|
28
|
+
JSON 字段名、状态枚举、检查 ID、scene、`reportGroup`、文件路径、命令、代码标识符和协议值保持原始机器格式。浏览器、控制台、网络和工具返回的英文原始错误必须原样保留,但角色应在相邻字段用简体中文解释其含义,不得篡改证据。
|
|
29
|
+
|
|
30
|
+
## 运行器接口
|
|
31
|
+
|
|
32
|
+
任何实现者回合开始前,都必须调用插件 MCP 工具 `check_environment`。就绪结果证明插件自带的 Node、Playwright 和 Chromium 可以启动。不得检查或依赖目标仓库中的 Playwright 包。
|
|
33
|
+
|
|
34
|
+
使用 `run_feature_e2e` 执行冻结场景:
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"workspaceRoot": "/absolute/workspace",
|
|
39
|
+
"scenarioPath": ".scratch/feature/e2e/e2e.feature.mjs",
|
|
40
|
+
"baseUrl": "http://localhost:6060",
|
|
41
|
+
"outputDir": ".scratch/feature/e2e/runs/iteration-01",
|
|
42
|
+
"iteration": 1,
|
|
43
|
+
"timeoutMs": 300000,
|
|
44
|
+
"headed": true,
|
|
45
|
+
"keepBrowserOpenOnBlock": true,
|
|
46
|
+
"blockedSessionTtlMs": 1800000
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
运行器会验证场景路径和输出路径都位于 `workspaceRoot` 内,执行前后校验冻结文件的 SHA-256。完整执行后清理浏览器;headed 运行 Blocked 且要求保留现场时返回 `blockedSessionId`,浏览器保持在阻断页面,直到人工决议或会话过期。运行器不会向目标仓库安装内容,也不会编辑目标仓库。
|
|
51
|
+
|
|
52
|
+
## Blocked 人工决议
|
|
53
|
+
|
|
54
|
+
`blocked` 只表示运行证据不完整。编排器必须写入 `awaiting-decision` 并把可见浏览器、阻断证据和以下选择交给用户,不得自行终止或修改测试:
|
|
55
|
+
|
|
56
|
+
| decision | effect | 含义 |
|
|
57
|
+
| --- | --- | --- |
|
|
58
|
+
| `retry` | `retry-checker` | 关闭当前会话,以未修改的冻结场景启动全新验收者;用于服务、网络、加载时序等瞬时问题 |
|
|
59
|
+
| `accept` | `continue-with-waiver` | 用户已在可见浏览器中人工确认阻断点满足预期,记录人工确认 waiver 后继续审计 |
|
|
60
|
+
| `skip` | `continue-with-waiver` | 用户不确认阻断点是否正确,只接受该阻断 scene 证据缺失的风险并继续审计;不会从抛异常的语句恢复执行 |
|
|
61
|
+
| `implementation_defect` | `maker` | 用户确认是业务缺陷;在 Maker 回合允许时交给全新实现者 |
|
|
62
|
+
| `test_defect` | `test-author` | 用户确认是场景缺陷;全新测试编写者可修订并重新冻结场景 |
|
|
63
|
+
| `stop` | `terminal-blocked` | 用户明确终止交付闭环 |
|
|
64
|
+
|
|
65
|
+
`blockedSessionId` 只在创建它的 MCP 进程内有效。编排器必须保留并记录原验收代理;收到选择后,把决议回传给同一个验收代理调用 `resolve_blocked_run`:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"sessionId": "runner 返回的 UUID",
|
|
70
|
+
"decision": "retry",
|
|
71
|
+
"note": "用户给出的判断依据"
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
工具将决议写入本轮输出目录的 `human-decision.json`,关闭浏览器,并返回下一步 `effect`。工具返回 `not-found` 表示原 MCP 会话已退出或过期,编排器按 `retry` 处理。会话过期自动记录 `expired`,其效果等同重新验收;过期不能推断为接受或跳过。`accept` 与 `skip` 不会补全缺失证据,最终交付只能是 `passed-with-waivers`,不能写成 `passed`。运行器无法恢复已经抛出异常的 JavaScript 调用栈,因此 `skip` 不是“从失败语句的下一行继续”;后续覆盖依靠下述独立 scene 在形成 Blocked 决议前继续执行。
|
|
76
|
+
|
|
77
|
+
## 冻结场景
|
|
78
|
+
|
|
79
|
+
`e2e/e2e.feature.mjs` 不包含 import,只导出 `metadata` 和一个默认异步函数:
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
export const metadata = {
|
|
83
|
+
reportSchemaVersion: 1,
|
|
84
|
+
scenes: ['initial'],
|
|
85
|
+
viewports: [{ name: 'desktop', width: 1400, height: 1000 }],
|
|
86
|
+
visualMappings: []
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export default async function run({
|
|
90
|
+
scene
|
|
91
|
+
}) {
|
|
92
|
+
await scene('initial', async ({
|
|
93
|
+
page,
|
|
94
|
+
expect,
|
|
95
|
+
check,
|
|
96
|
+
route,
|
|
97
|
+
capture,
|
|
98
|
+
artifact,
|
|
99
|
+
baseUrl,
|
|
100
|
+
parseUrl
|
|
101
|
+
}) => {});
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
场景不得使用 `require`、`process`、`__dirname`、包导入或文件系统写入,也不得额外创建 browser、page 或 context。所有能力都由运行器注入。每个 `metadata.scenes` 项必须通过 `scene(id, callback)` 恰好执行一次;运行器为每个 scene 创建独立 page,scene 内未捕获的定位器、弹窗、fixture 或准备步骤错误只阻断该 scene,记录后继续执行后续 scene,并在全部独立 scene 尝试完毕后返回 `blocked`。scene 必须自包含其页面导航、路由 fixture 和必要状态,不能依赖前一个 scene 的 page 或临时状态。
|
|
106
|
+
|
|
107
|
+
新建或修订场景必须声明 `metadata.reportSchemaVersion: 1`;运行器据此校验通用报告分组字段,未声明版本的历史冻结场景继续兼容。每条原子运行预期通过 `check(definition, assertion)` 记录。新建或修订的检查定义必须同时包含 `reportModule`、`reportGroup`、`reportTitle` 和 `reportMethod`:`reportModule` 是人读的页面或模块名称,`reportGroup` 是当前功能目录内稳定且唯一的功能组 ID,`reportTitle` 是该组在报告中的简短中文标题,`reportMethod` 是该组件或完整功能合并后的中文检查方式。属于同一 UI 组件、同一页面位置或同一完整用户操作流的文案、样式、布局、响应式行为和相关功能检查共用同一组及同一套模块、标题和检查方式;不同组件、独立业务能力、状态转换或风险边界不得为了减少行数而合并。分组只表达产品功能语义,不表达 E2E scene 或执行顺序。可能失败的用户操作和等待应放在所属的 `check` 或 `scene` 内,不能作为未归属的顶层 await。
|
|
108
|
+
|
|
109
|
+
spec 声明的每个视觉对比都必须在 `metadata.visualMappings` 中出现且只出现一次,并包含检查定义、场景、截图文件名、定位器说明、仓库相对路径基准、`reportDevice`(`desktop` 或 `mobile`)以及同样的 `reportModule`、`reportGroup`、`reportTitle`。视觉映射与它证明的运行检查必须复用同一功能组。运行检查与视觉映射必须完整且不重复地划分所有原子 spec。
|
|
110
|
+
|
|
111
|
+
旧版直接使用顶层 `page` 的默认函数仍可执行,但它不具备 scene 级故障隔离;测试编写者创建或修订场景时必须使用 `scene()`。
|
|
112
|
+
|
|
113
|
+
## 冻结前 Preflight
|
|
114
|
+
|
|
115
|
+
测试编写者拥有场景草稿,必须在写入 `ready` 和冻结 SHA-256 前通过同一 `run_feature_e2e` 运行器执行 preflight。preflight 使用独立输出目录和 `keepBrowserOpenOnBlock: false`,只验证场景机械可执行性、scene 覆盖、运行检查可达性、fixture、弹窗和定位器,不给出最终视觉结论。
|
|
116
|
+
|
|
117
|
+
- `passed` 或证据完整的 `failed` 证明测试代码可以完整执行;业务断言失败留给独立验收者判定。
|
|
118
|
+
- `blocked` 且证据指向场景代码、定位器、弹窗处理、fixture 或等待条件时,测试编写者在冻结前自行修正并重跑,不需要人工 `test_defect` 决议。
|
|
119
|
+
- 环境、spec 或实现导致无法完成 preflight 时,按对应类型报告阻断,不得通过放宽断言伪造可执行。
|
|
120
|
+
- `author-result.json` 记录最终 preflight 的 runner 路径、场景 SHA-256、已执行 scene 和检查覆盖;只有所有声明 scene 均已尝试且预期检查可达时才能写为 `ready`。
|
|
121
|
+
|
|
122
|
+
## 审计视口
|
|
123
|
+
|
|
124
|
+
- 移动端审计使用 `375px` 宽度,匹配 Figma 移动端画布并满足 `<= 767px`。
|
|
125
|
+
- 桌面端审计使用 `1400px` 宽度,满足 `> 767px`。
|
|
126
|
+
|
|
127
|
+
测试编写者为每个视口冻结一个稳定高度,验收者必须复用完全相同的值。
|
|
128
|
+
|
|
129
|
+
## 语义视觉比较
|
|
130
|
+
|
|
131
|
+
视觉比较判断映射组件的 UI 效果,不比较业务数据内容。测试编写者必须让截图定位器与 Figma 基准指向同一语义节点;例如 `375px` 页面画布内宽 `343px` 的内容节点应截取该内容节点,不能用整个页面或外层根节点与 `343px` 基准直接比较。
|
|
132
|
+
|
|
133
|
+
- 忽略业务文字的字面值、字符串长度、字形宽度,以及仅由样例文字差异造成的换行、截断或省略位置差异。文案正确性通过独立功能检查断言。
|
|
134
|
+
- 忽略业务图片的具体主体和资源内容。比较图片容器的尺寸、比例、位置、裁切或适配方式、边框和圆角。
|
|
135
|
+
- 比较组件结构、容器几何、间距、对齐、排列、颜色、背景、边框、圆角、阴影、控件状态,以及字体族、字号、字重、行高和文字颜色。
|
|
136
|
+
- `1px` 几何容差只适用于视口、截图语义节点和组件状态一致的可比元素。基准与实际的画布或捕获边界不一致时,先将其归类为视觉映射缺陷,不得据此判定业务实现失败。
|
|
137
|
+
|
|
138
|
+
fixture 应提供足以触发目标布局和状态的代表性数据,不需要复刻基准中的名称、文字长度或图片主体。文字长度、业务图片或列表内容本身属于 spec 功能预期时,使用运行检查验证,不纳入截图视觉结论。
|
|
139
|
+
|
|
140
|
+
## 执行事实
|
|
141
|
+
|
|
142
|
+
产品预期只来自 `spec.md`。URL、启动命令、数据准备和定位器应从 spec、同目录 scope、仓库规则、路由、链接构建器、fixture 和运行探测中解析。通配路由必须生成候选项,并通过解码状态确认。当仓库事实或受控 fixture 可以推导具体值时,spec 缺少字面值不构成阻断。
|
|
143
|
+
|
|
144
|
+
## 运行器结果
|
|
145
|
+
|
|
146
|
+
`e2e/runs/iteration-NN/runner-result.json` 包含:
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"status": "passed",
|
|
151
|
+
"iteration": 1,
|
|
152
|
+
"scenarioPath": ".scratch/feature/e2e/e2e.feature.mjs",
|
|
153
|
+
"scenarioSha256": "64 个小写十六进制字符",
|
|
154
|
+
"metadata": {},
|
|
155
|
+
"checks": [],
|
|
156
|
+
"captures": [],
|
|
157
|
+
"tracePath": ".scratch/feature/e2e/runs/iteration-01/trace.zip",
|
|
158
|
+
"consoleErrors": [],
|
|
159
|
+
"networkFailures": [],
|
|
160
|
+
"blockers": [],
|
|
161
|
+
"browserMode": "headed",
|
|
162
|
+
"blockedSessionId": null,
|
|
163
|
+
"blockedSessionExpiresAt": null,
|
|
164
|
+
"decisionPath": null
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`passed` 表示所有运行检查均通过。`failed` 表示执行完整结束,但至少一个业务断言失败。`blocked` 表示至少一个 scene、运行器、浏览器、超时、路径策略或证据链阻止了完整证据;使用 `scene()` 时其余独立 scene 会继续执行,全部尝试后再触发人工决议。
|
|
169
|
+
|
|
170
|
+
## 角色结果
|
|
171
|
+
|
|
172
|
+
实现者结果:
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
{
|
|
176
|
+
"status": "implemented",
|
|
177
|
+
"iteration": 1,
|
|
178
|
+
"modifiedFiles": [],
|
|
179
|
+
"verification": [],
|
|
180
|
+
"blockers": []
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
测试编写者结果:
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"status": "ready",
|
|
189
|
+
"testPath": ".scratch/feature/e2e/e2e.feature.mjs",
|
|
190
|
+
"scenes": [],
|
|
191
|
+
"scenarioSha256": "64 个小写十六进制字符",
|
|
192
|
+
"preflightRunnerResult": ".scratch/feature/e2e/preflight/attempt-01/runner-result.json",
|
|
193
|
+
"preflightChecks": [],
|
|
194
|
+
"blockers": []
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
验收者结果:
|
|
199
|
+
|
|
200
|
+
```json
|
|
201
|
+
{
|
|
202
|
+
"status": "passed",
|
|
203
|
+
"iteration": 1,
|
|
204
|
+
"runnerResult": ".scratch/feature/e2e/runs/iteration-01/runner-result.json",
|
|
205
|
+
"checks": [],
|
|
206
|
+
"blockers": []
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
运行器 Blocked 时,验收者使用 `status: "awaiting-decision"`,并在结果中记录 `blockedSessionId`、过期时间、runner 证据和阻断项;验收者不得代替用户选择决议。
|
|
211
|
+
|
|
212
|
+
每个验收检查项包含 `id`、`specSnippet`、`scene`、`reportModule`、`reportGroup`、`reportTitle`、`reportMethod`、`errorType`(`功能缺陷` 或 `UI视觉缺陷`)、`expect`、`actual`、可选的 `evidencePath` 和 `isPass`。Checker 合并 Runner 检查时必须原样保留四个报告字段;同一 `reportGroup` 的 `reportModule`、`reportTitle` 和 `reportMethod` 必须一致。每条原子 spec 检查只出现一次。证据不完整时验收者状态为 `awaiting-decision`;证据完整但存在失败检查时为 `failed`。
|
|
213
|
+
|
|
214
|
+
`checker-result.md` 固定使用中文标题 `E2E UI 验收结果`、`证据摘要`、`视觉结果`、`运行结果` 和 `证据路径`;表头和展示状态也使用中文。机器状态可在中文结论旁以反引号保留。
|
|
215
|
+
|
|
216
|
+
## Excel 报告分组
|
|
217
|
+
|
|
218
|
+
Excel 只包含一个名为 `自测报告` 的工作表,每行代表一个真实 UI 组件或完整业务功能。不得显示 story ID、scene 名、历史场景或原子明细工作表。桌面端和移动端代表截图位于同一功能行。新证据直接使用报告字段聚合;历史证据缺少这些字段时,导出 Skill 必须阅读 `spec.md` 和验收证据,生成 `report/report-groups.json` 语义分组清单。清单必须绑定当前 spec SHA-256、Runner iteration 和冻结场景 SHA-256,并完整且不重复地覆盖全部 story;导出器不得以 scene、story 顺序或截图名称猜测业务分组。
|
|
219
|
+
|
|
220
|
+
代码审计结果:
|
|
221
|
+
|
|
222
|
+
```json
|
|
223
|
+
{
|
|
224
|
+
"status": "passed",
|
|
225
|
+
"fixedPoint": "commit",
|
|
226
|
+
"target": "worktree",
|
|
227
|
+
"standardsFindings": [],
|
|
228
|
+
"specFindings": [],
|
|
229
|
+
"blockingFindings": []
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## 编排状态
|
|
234
|
+
|
|
235
|
+
`delivery/delivery-state.json` 记录 `status`、`phase`、`makerTurns`、`maxMakerTurns`、`fixedPoint`、`preExistingChanges`、最新产物路径,以及等待决议时的验收代理、`blockedSessionId`、过期时间和 waiver。`status` 可为 `running`、`awaiting-decision`、`passed`、`passed-with-waivers` 或 `blocked`。该文件只记录结论,不得覆盖角色自行拥有的结果。
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: e2e-gen-spec
|
|
3
|
+
description: 在首版实现可运行后,为 Autobest Delivery 运行器编写并冻结无依赖的功能 E2E 场景。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# E2E 测试编写者
|
|
7
|
+
|
|
8
|
+
本 Skill 只生成验收资产。开始前完整阅读[共享交付契约](../delivery-loop/references/delivery-contract.md)。
|
|
9
|
+
|
|
10
|
+
## 输入
|
|
11
|
+
|
|
12
|
+
- `featureDir/spec.md` 及同目录的 `scope.md`。
|
|
13
|
+
- 仓库规则、路由、fixture、运行事实,以及 spec 已映射的本地视觉基准。
|
|
14
|
+
- 可运行的首版实现。
|
|
15
|
+
- 仅在修订回合读取 `test_defect` 人工决议及其指向的 Blocked runner/checker 证据。
|
|
16
|
+
|
|
17
|
+
产品预期只由 spec 决定。按契约解析具体执行事实。存在通配路由,或者 spec 未直接写出命令、视口或定位器,并不自动构成阻断。
|
|
18
|
+
|
|
19
|
+
## 流程
|
|
20
|
+
|
|
21
|
+
1. 将每条原子 spec 预期准确映射到一个运行检查或一个视觉映射。为每项分配通用的 `reportModule`、`reportGroup`、`reportTitle`、`reportMethod`,让同一 UI 组件、页面位置或完整用户操作流的文案、样式、布局、响应式行为和相关功能检查在报告中合并;同组的模块、标题和检查方式必须完全一致。不同组件、独立业务能力、状态转换或风险边界保持不同组。分组只表达当前 spec 的功能语义,不得按 scene 或执行顺序分组,不得写死特定页面族词汇或依赖导出器猜测业务语义。覆盖所有必需页面变体、状态转换、空态或错误态、导航结果和响应式变体。
|
|
22
|
+
2. 解析并探测具体 URL、启动行为、受控 API 数据、持久化状态和面向用户的稳定定位器。通过解码后的状态或受控 fixture 确认通配路由变体。
|
|
23
|
+
3. 首次编写只写入 `featureDir/e2e/e2e.feature.mjs`,保持旧 `.spec.ts` 不变。该文件不得包含 import,只导出 `metadata` 和一个默认异步函数,并且只能使用运行器注入的 `scene`、`page`、`expect`、`check`、`route`、`capture`、`artifact`、`baseUrl` 和 `parseUrl`。新建或修订场景在 metadata 中声明 `reportSchemaVersion: 1`,使运行器校验所有检查和视觉映射的通用报告分组字段。每个 `metadata.scenes` 项通过 `scene(id, callback)` 恰好执行一次;每个 scene 自包含页面导航、API fixture 和必要状态。冻结后只有用户通过 `resolve_blocked_run` 明确选择 `test_defect` 才能修订;修订结果必须记录旧、新 SHA-256 和决议证据路径。
|
|
24
|
+
4. 在 metadata 中冻结桌面宽度 `1400px`、移动端宽度 `375px` 和明确稳定的高度。spec 声明的每个组件视觉对比都要映射场景、截图文件名、定位器说明、仓库相对路径基准、`reportDevice`(`desktop` 或 `mobile`)、`reportModule`、`reportGroup`、`reportTitle`,以及完整的验收检查定义。视觉映射与它证明的运行检查复用同一功能组。截图定位器必须与 Figma 基准指向同一语义节点;fixture 只需提供能触发目标布局和状态的代表性数据,不复刻文字长度、业务文案或图片主体,视觉比较边界遵循共享契约。
|
|
25
|
+
5. 通过 `check(definition, assertion)` 记录功能和 DOM 预期;定义中的 `reportModule`、`reportTitle` 和 `reportMethod` 使用简体中文,`reportGroup` 使用稳定的 ASCII ID。把可能失败的点击、选择、弹窗、等待和状态准备放在所属的 `check` 或 `scene` 内,不在两个检查之间留下未归属的可失败 await。`capture` 仅用于已映射的组件根节点,`artifact` 用于结构化 DOM 测量。不得创建其他浏览器、context 或 page,不得直接写文件、使用 Node 全局变量、导入包、截取未映射的整页截图或自行给出视觉结论。
|
|
26
|
+
6. 使用 `node --check` 检查草稿语法后,启动必需服务并调用同一隔离运行器执行冻结前 preflight,输出到 `e2e/preflight/attempt-NN`,使用 `headed: false`、`keepBrowserOpenOnBlock: false`。`blocked` 若源于场景代码、定位器、弹窗处理、fixture 或等待条件,在冻结前自行修正并重跑;不得放宽 spec 断言。环境、spec 或实现阻断按真实类型报告。
|
|
27
|
+
7. 只有 preflight 返回 `passed`,或返回证据完整且所有声明 scene 和预期检查均已到达的 `failed`,才能冻结当前 SHA-256。核对 metadata、检查项覆盖和路径所有权,将最终 preflight 路径、SHA-256、scene 和检查覆盖写入 `e2e/author-result.json` 并标记 `ready`。preflight 只证明测试代码可执行,不替代独立最终验收或视觉审查。
|
|
28
|
+
|
|
29
|
+
## 阻断条件
|
|
30
|
+
|
|
31
|
+
只有记录已检查的来源和失败探测后才能返回 `blocked`。阻断类型为 `implementation`、`spec` 或 `environment`。不得创建占位场景。
|
|
32
|
+
|
|
33
|
+
## 完成条件
|
|
34
|
+
|
|
35
|
+
只有每条原子 spec 都有唯一归属、每个语义路由变体都可控、视觉映射准确、文件无依赖且语法有效,并且冻结前 preflight 已证明所有 scene 均被尝试、预期检查可达时,场景才算完成。
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: e2e-ui-checker
|
|
3
|
+
description: 使用 Autobest Delivery MCP 运行器在可见浏览器中独立执行冻结场景,Blocked 时保留现场供人工决策,并审查已映射的本地 UI 基准。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# E2E UI 验收者
|
|
7
|
+
|
|
8
|
+
开始前完整阅读[共享交付契约](../delivery-loop/references/delivery-contract.md)。本 Skill 只执行检查并写入审计产物。
|
|
9
|
+
|
|
10
|
+
## 输入
|
|
11
|
+
|
|
12
|
+
- `featureDir/spec.md`、同目录 `scope.md`,以及冻结的 `featureDir/e2e/e2e.feature.mjs`。
|
|
13
|
+
- spec 已映射的本地视觉基准。
|
|
14
|
+
- 迭代编号;单独使用时默认为 `1`。
|
|
15
|
+
- 可发现的开发启动命令和基础 URL。
|
|
16
|
+
|
|
17
|
+
## 流程
|
|
18
|
+
|
|
19
|
+
1. 建立原子 spec 检查清单,确认冻结运行检查和 metadata 视觉映射完整且不重复地划分了全部检查项。`reportSchemaVersion: 1` 的场景须核对每个运行检查包含 `reportModule`、`reportGroup`、`reportTitle`、`reportMethod`,同组字段一致且表达真实组件或完整功能,而不是 E2E scene;同时核对视觉映射的同组字段和 `reportDevice` 正确区分桌面端与移动端。历史场景没有报告 schema 时不据此阻断。
|
|
20
|
+
2. 调用 `autobest-delivery` MCP 的 `check_environment` 工具并传入 `headed: true`。环境未就绪时,以返回的版本和错误证据形成 `tool` 阻断;不得降级使用通用 Playwright MCP 或目标仓库依赖。
|
|
21
|
+
3. 只解析并启动仓库已声明且本次必需的开发服务,记录其 PID,并确认基础 URL。复用冻结的 `1400px` 和 `375px` 视口及受控路由或 API 状态。
|
|
22
|
+
4. 调用 `run_feature_e2e`,传入工作区绝对路径、标准场景路径、基础 URL、`featureDir/e2e/runs/iteration-NN`、迭代编号、有界超时、`headed: true` 和 `keepBrowserOpenOnBlock: true`。将运行器返回结果视为不可修改的证据。
|
|
23
|
+
5. 运行器返回 `blocked` 时,确认 `blockedSessionId`、过期时间、当前页面和 Trace,并核对每个独立 scene 的已执行检查、缺失检查和 blocker;scene 级阻断不应阻止其余独立 scene 留下证据。验收结果写为 `awaiting-decision` 并交回编排器;保留本验收代理供决议 follow-up 使用。编排器回传用户选择后,由同一个验收代理调用 `resolve_blocked_run` 并复读决议产物。明确说明 `skip` 只接受阻断 scene 的缺失证据,不会从异常语句恢复执行。不得自行修改测试、选择决议或给出验收结论。返回 `failed` 时取得功能失败项。运行器返回 `passed` 或 `failed` 时,检查每个已映射组件的实际截图、基准截图和已记录的叶子 DOM 测量。只比较映射指定的组件、状态和设备,并按共享契约执行语义视觉比较:忽略业务文字的内容和长度及业务图片主体,检查结构、几何、间距、对齐、响应式排列和视觉样式。文案正确性由运行检查判定。默认 `1px` 几何容差仅用于视口、语义节点和组件状态一致的可比元素;捕获边界不一致应判为视觉映射缺陷,不得判为实现缺陷。
|
|
24
|
+
6. 合并运行检查和视觉检查,确保每条原子 spec 只出现一次。证据完整但存在失败项时为 `failed`;全部通过时为 `passed`;缺失任一必要证据时为 `awaiting-decision`。
|
|
25
|
+
7. 写入并复读 `checker/checker-result.json` 和 `.md`,确保两份报告结论一致,且所有证据路径均为仓库相对路径。合并 Runner 检查时原样保留 `reportModule`、`reportGroup`、`reportTitle` 和 `reportMethod`。角色撰写的检查说明、期望、实际结果和全部 Markdown 内容必须使用简体中文;Markdown 使用共享契约规定的中文标题、中文表头和中文展示状态。JSON 的字段名、机器状态、ID、scene 和路径保持协议格式,英文原始错误原样引用并附中文解释。
|
|
26
|
+
8. 普通完成时关闭浏览器资源;存在 `blockedSessionId` 时保留浏览器和当前验收者启动的开发服务,直到编排器完成决议。决议后只清理由本轮启动的资源;会话过期也必须清理。
|
|
27
|
+
|
|
28
|
+
## 边界
|
|
29
|
+
|
|
30
|
+
spec 定义预期,scope 提供执行上下文,基准只约束已映射的视觉改动,运行器产物提供实际行为证据。不得修改业务代码、依赖、配置、冻结输入或之前角色的产物。不得调用实现者,也不得在最终答复中提出代码修复方案。
|