groundwork-verify
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesegroundwork-verify
groundwork-verify
Generates Cucumber.js step definitions that test the system as a black box through its external interface.
Step definitions live in — a self-contained layer with its own , independent of the project's stack.
docs/specspackage.json生成Cucumber.js步骤定义,通过系统的外部接口以黑盒方式测试系统。
步骤定义存储在目录中——这是一个独立的层,拥有自己的,与项目的技术栈无关。
docs/specspackage.jsonCONSTRAINT — Epistemic perimeter
约束——认知边界
This skill NEVER reads files outside docs/specs/.
No access to src/, app/, lib/, or any application directory.
If information is missing, ASK the user — never infer it from code.This is non-negotiable. The skill must have exactly the same information as the Gherkin — no more, no less.
Reading application code introduces cognitive bias: the skill learns patterns from the implementation (including bugs) and replicates them in step definitions. Tests end up validating the implementation instead of the contract.
The constraint prevents the bias vector — it doesn't matter if the dev knows the code. What matters is that the skill doesn't use it as source of truth.
Clarification: Information provided verbally by the dev (e.g. "the endpoint changed to POST /refunds") is legitimate input. You MAY update with information the dev tells you directly — the constraint forbids reading application files, not listening to the dev. The interview mechanism works the same way: the dev describes the interface, you write it down.
SPEC-INTERFACE.md本工具绝不会读取docs/specs/目录以外的文件。
无法访问src/、app/、lib/或任何应用程序目录。
如果信息缺失,询问用户——绝不要从代码中推断。这一点是不可协商的。本工具拥有的信息必须与Gherkin文件完全一致——不多不少。
读取应用程序代码会引入认知偏差:工具会从实现(包括bug)中学习模式,并在步骤定义中复制这些模式。最终测试验证的是实现而非契约。
该约束可防止偏差影响——开发人员是否了解代码并不重要。重要的是工具不会将代码作为事实来源。
说明:开发人员口头提供的信息(例如“端点已更改为POST /refunds”)是合法输入。你可以将开发人员直接告知的信息更新到中——该约束禁止读取应用程序文件,但不禁止听取开发人员的说明。这与访谈机制的工作方式相同:开发人员描述接口,你将其记录下来。
SPEC-INTERFACE.mdWhen to use
适用场景
- After implementation completes a feature and you need to verify it matches the spec
- User asks to generate tests from files
.feature - User asks "does the code do what we specified?"
- New scenarios were added via and step definitions need updating
/groundwork extend
- 功能实现完成后,需要验证其是否符合规格要求时
- 用户要求从文件生成测试时
.feature - 用户询问“代码是否符合我们的规格要求?”时
- 通过添加了新场景,需要更新步骤定义时
/groundwork extend
When NOT to use
不适用场景
- User wants to define or design a feature → use groundwork-discovery
- User wants unit tests or internal code tests — this skill is black-box only
- No files exist yet
.feature
- 用户希望定义或设计功能 → 使用groundwork-discovery
- 用户需要单元测试或内部代码测试——本工具仅支持黑盒测试
- 尚未存在文件时
.feature
Quick reference
快速参考
| Command | Purpose |
|---|---|
| Generate/update step definitions for one feature |
| Generate/update step definitions for all features |
| Interface type | Tooling | Assertions via |
|---|---|---|
| | HTTP status, JSON body |
| | stdout, stderr, exit code |
| Playwright | Page elements, navigation |
| 命令 | 用途 |
|---|---|
| 为单个功能生成/更新步骤定义 |
| 为所有功能生成/更新步骤定义 |
| 接口类型 | 工具链 | 断言方式 |
|---|---|---|
| | HTTP状态码、JSON响应体 |
| | 标准输出、标准错误、退出码 |
| Playwright | 页面元素、导航行为 |
First action
首次操作
Read if it exists.
Then read existing step definitions in to understand the style already in use.
docs/specs/CONVENTIONS.mddocs/specs/step-definitions/Do NOT read any file outside .
docs/specs/如果存在,请先读取该文件。
然后读取中的现有步骤定义,以了解已使用的风格。
docs/specs/CONVENTIONS.mddocs/specs/step-definitions/绝对不要读取docs/specs/以外的任何文件。
SPEC-INTERFACE.md — Interface contract
SPEC-INTERFACE.md —— 接口契约
SPEC-INTERFACE.mddocs/specs/It is written by the dev, not inferred from code. This is the critical point: describing the system explicitly breaks implicit bias — the dev reasons about the contract, not the implementation.
SPEC-INTERFACE.mddocs/specs/该文件由开发人员编写,而非从代码中推断。这是关键要点:明确描述系统可打破隐性偏差——开发人员是基于契约而非实现进行思考。
Structure
结构
markdown
undefinedmarkdown
undefinedInterface Contract
接口契约
Type
类型
rest | web | rest+web | cli
rest | web | rest+web | cli
Base URLs
基础URL
Auth
认证方式
type: bearer | cookie | none
token_env: TEST_AUTH_TOKEN # env var name, not the value
type: bearer | cookie | none
token_env: TEST_AUTH_TOKEN # 环境变量名称,而非值
Endpoint map
端点映射
Format: METHOD /path → expected response (success | error)
格式: METHOD /path → 预期响应(success | error)
POST /auth/login → 200 {token} | 401
GET /users/:id → 200 {user} | 404
POST /orders → 201 {order} | 422
POST /auth/login → 200 {token} | 401
GET /users/:id → 200 {user} | 404
POST /orders → 201 {order} | 422
Web entry points
Web入口点
Format: <feature> → <path>
格式: <feature> → <路径>
login → /login
dashboard → /dashboard
checkout → /checkout
login → /login
dashboard → /dashboard
checkout → /checkout
Notes
说明
Any contextual information useful for tests, not inferable from scenarios
任何对测试有用、无法从场景中推断的上下文信息
The **Endpoint map** section is the most important: it explicitly maps scenarios → interface, forcing the dev to declare the contract instead of letting the skill deduce it from code.
---
**端点映射**部分是最重要的:它显式地将场景映射到接口,迫使开发人员声明契约,而非让工具从代码中推导。
---Architecture
架构
Prerequisite
前提条件
The system under test exposes a standard external interface. Step definitions never touch internal code — they interact exclusively through:
- REST/GraphQL — endpoints
- CLI — command-line process
- Web — browser UI
被测系统暴露标准的外部接口。步骤定义绝不会触及内部代码——它们仅通过以下方式与系统交互:
- REST/GraphQL —— 端点
- CLI —— 命令行进程
- Web —— 浏览器UI
Fixed toolchain
固定工具链
Cucumber.js for all cases. Playwright added if any feature uses the interface type.
webdocs/
specs/
package.json ← cucumber.js (+ playwright if needed)
cucumber.js ← cucumber config
CONVENTIONS.md
SPEC-INTERFACE.md ← interface contract (only source of system info)
features/
<domain>/<feature>.feature
step-definitions/
<domain>/<feature>.steps.js
support/
hooks.js ← Before/After setup and teardown
world.js ← shared state across steps
.env.test.example ← required env vars, no secrets所有场景均使用Cucumber.js。如果任何功能使用接口类型,则添加Playwright。
webdocs/
specs/
package.json ← cucumber.js(如需则包含playwright)
cucumber.js ← cucumber配置文件
CONVENTIONS.md
SPEC-INTERFACE.md ← 接口契约(系统信息的唯一来源)
features/
<领域>/<feature>.feature
step-definitions/
<领域>/<feature>.steps.js
support/
hooks.js ← Before/After 初始化与清理
world.js ← 步骤间的共享状态
.env.test.example ← 必填环境变量,不含敏感信息Interface → tooling map
接口→工具链映射
| Interface type | HTTP client | Notes |
|---|---|---|
| | JSON request/response assertions |
| | spawn process, assert stdout/exit code |
| Playwright | headless browser, behavioural assertions |
A single feature can mix and steps if the interface type is .
restwebrest+web| 接口类型 | HTTP客户端 | 说明 |
|---|---|---|
| | JSON请求/响应断言 |
| | 启动进程,断言标准输出/退出码 |
| Playwright | 无头浏览器,行为断言 |
如果接口类型为,单个功能可以混合和步骤。
rest+webrestwebCommand
命令
/groundwork verify [feature-name]
/groundwork verify [feature-name]/groundwork verify [feature-name]
/groundwork verify [feature-name]Generate or update step definitions. Without argument, processes all files.
.feature生成或更新步骤定义。不带参数时,处理所有文件。
.featurePhase 1 — Interface discovery
阶段1 —— 接口发现
SPEC-INTERFACE.md exists?
├── YES → read the file
│ ↓
│ all necessary information present?
│ ├── YES → proceed to Phase 2
│ └── NO → ask only the missing gaps, update the file
│
└── NO → enter interview mode
generate SPEC-INTERFACE.md at the end
proceed to Phase 2SPEC-INTERFACE.md是否存在?
├── 是 → 读取该文件
│ ↓
│ 是否包含所有必要信息?
│ ├── 是 → 进入阶段2
│ └── 否 → 仅询问缺失的信息,更新文件
│
└── 否 → 进入访谈模式
最终生成SPEC-INTERFACE.md
进入阶段2Interview (only if the file doesn't exist or is incomplete)
访谈(仅当文件不存在或不完整时)
Ask questions in order, one at a time:
- "Interface type: REST API, Web UI, or both?"
- "Base URL of the system in test environment?"
- "How does it authenticate? Bearer token, session cookie, or none?"
- "For each scenario in the , which endpoint or page is involved?"
.feature
Question 4 is the core anti-bias mechanism: the dev manually maps scenarios → interface, without the skill looking at code.
按顺序逐个提问:
- “接口类型:REST API、Web UI,还是两者兼具?”
- “测试环境中系统的基础URL是什么?”
- “系统如何进行认证?Bearer令牌、会话Cookie,还是无需认证?”
- “对于文件中的每个场景,涉及哪个端点或页面?”
.feature
问题4是核心的反偏差机制:开发人员手动将场景映射到接口,工具不会查看代码。
Incremental update
增量更新
If exists but has gaps (e.g. a new scenario references an unmapped endpoint):
SPEC-INTERFACE.md- Ask only for the missing information
- Update the file with append, without touching existing sections
- Explicitly report what was added
如果存在但存在缺失(例如新场景引用了未映射的端点):
SPEC-INTERFACE.md- 仅询问缺失的信息
- 通过追加方式更新文件,不修改现有章节
- 明确报告添加的内容
Phase 2 — Gap analysis
阶段2 —— 差距分析
Compare files against existing step definitions in :
.featuredocs/specs/step-definitions/| Status | Action |
|---|---|
| Missing — scenario with no step definition | Generate |
| Broken — step definition referencing a changed endpoint or selector | Regenerate |
| Obsolete — step definition with no matching scenario | Report to user, do not delete |
Only generate what is missing or broken. Never rewrite working step definitions.
Report the gap analysis before generating:
Found 3 scenarios without step definitions:
- auth/login.feature: "login with expired token"
- payments/checkout.feature: "checkout with empty cart" (new)
- payments/checkout.feature: "duplicate order submission" (new)
Generating...将文件与中的现有步骤定义进行比较:
.featuredocs/specs/step-definitions/| 状态 | 操作 |
|---|---|
| 缺失 —— 场景无对应步骤定义 | 生成步骤定义 |
| 损坏 —— 步骤定义引用了已更改的端点或选择器 | 重新生成步骤定义 |
| 过时 —— 步骤定义无匹配场景 | 向用户报告,不删除 |
仅生成缺失或损坏的步骤定义。绝不重写可用的步骤定义。
在生成前报告差距分析结果:
发现3个无对应步骤定义的场景:
- auth/login.feature: "使用过期令牌登录"
- payments/checkout.feature: "空购物车结账"(新增)
- payments/checkout.feature: "重复提交订单"(新增)
正在生成...Phase 3 — Generate step definitions
阶段3 —— 生成步骤定义
Use information from (base URLs, endpoints, auth type) to generate step definitions. Never look at application code to determine URLs, selectors, or configuration.
SPEC-INTERFACE.mdREST pattern:
javascript
// Scenario: <scenario name from .feature>
Given('a registered user exists with email {string}', async function(email) {
// TODO: adjust to your seed/fixture mechanism
this.user = await createTestUser({ email });
});
When('they attempt login with incorrect credentials', async function() {
// URL from SPEC-INTERFACE.md endpoint map
this.response = await fetch(`${process.env.BASE_URL}/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: this.user.email, password: 'wrong' })
});
});
Then('the operation fails with an authentication error', async function() {
assert.strictEqual(this.response.status, 401);
});Web pattern:
javascript
// Scenario: <scenario name from .feature>
Given('a registered user exists', async function() {
// TODO: adjust to your seed/fixture mechanism
this.user = await createTestUser();
});
When('they attempt login with incorrect credentials', async function() {
// Path from SPEC-INTERFACE.md web entry points
await this.page.goto(`${process.env.WEB_URL}/login`);
await this.page.fill('[data-testid="login-email"]', this.user.email);
await this.page.fill('[data-testid="login-password"]', 'wrong');
await this.page.click('[data-testid="login-submit"]');
});
Then('the operation fails with an authentication error', async function() {
await expect(this.page.locator('[data-testid="login-error"]')).toBeVisible();
});Selector naming convention: Use (e.g. , ). Choose names that describe the UI concept from the scenario, not the implementation. These selectors are declared in the step definitions first — the dev adds them to the markup after.
data-testid="<page>-<element>"login-emailcheckout-submitCLI pattern:
javascript
// Scenario: <scenario name from .feature>
When('they run the export command', async function() {
this.cliOutput = await exec(`node cli.js export --user=${this.user.id}`);
});
Then('the export succeeds', async function() {
assert.strictEqual(this.cliOutput.exitCode, 0);
});Rules for all patterns:
- Add comment at the top referencing the
// Scenario: <name>origin.feature - Use URLs and paths from — comment the source (e.g.
SPEC-INTERFACE.md)// URL from SPEC-INTERFACE.md endpoint map - Mark with anything requiring manual configuration (seed mechanisms, selectors)
// TODO: - For scenarios, generate a stub with
@pending— do not skip silentlypending() - For scenarios, generate nothing
@out-of-scope
使用中的信息(基础URL、端点、认证类型)生成步骤定义。绝不要查看应用程序代码来确定URL、选择器或配置。
SPEC-INTERFACE.mdREST模式:
javascript
// Scenario: <来自.feature的场景名称>
Given('a registered user exists with email {string}', async function(email) {
// TODO: 根据你的数据初始化机制调整
this.user = await createTestUser({ email });
});
When('they attempt login with incorrect credentials', async function() {
// URL来自SPEC-INTERFACE.md的端点映射
this.response = await fetch(`${process.env.BASE_URL}/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: this.user.email, password: 'wrong' })
});
});
Then('the operation fails with an authentication error', async function() {
assert.strictEqual(this.response.status, 401);
});Web模式:
javascript
// Scenario: <来自.feature的场景名称>
Given('a registered user exists', async function() {
// TODO: 根据你的数据初始化机制调整
this.user = await createTestUser();
});
When('they attempt login with incorrect credentials', async function() {
// 路径来自SPEC-INTERFACE.md的Web入口点
await this.page.goto(`${process.env.WEB_URL}/login`);
await this.page.fill('[data-testid="login-email"]', this.user.email);
await this.page.fill('[data-testid="login-password"]', 'wrong');
await this.page.click('[data-testid="login-submit"]');
});
Then('the operation fails with an authentication error', async function() {
await expect(this.page.locator('[data-testid="login-error"]')).toBeVisible();
});选择器命名规范: 使用(例如、)。选择能够从场景描述UI概念的名称,而非基于实现的名称。这些选择器先在步骤定义中声明——开发人员随后将其添加到标记中。
data-testid="<页面>-<元素>"login-emailcheckout-submitCLI模式:
javascript
// Scenario: <来自.feature的场景名称>
When('they run the export command', async function() {
this.cliOutput = await exec(`node cli.js export --user=${this.user.id}`);
});
Then('the export succeeds', async function() {
assert.strictEqual(this.cliOutput.exitCode, 0);
});所有模式的规则:
- 在顶部添加注释,引用对应的
// Scenario: <名称>来源.feature - 使用中的URL和路径——添加来源注释(例如
SPEC-INTERFACE.md)// URL来自SPEC-INTERFACE.md的端点映射 - 对需要手动配置的内容(数据初始化机制、选择器)标记
// TODO: - 对于场景,生成包含
@pending的存根——不要静默跳过pending() - 对于场景,不生成任何内容
@out-of-scope
Phase 4 — Setup (first run only)
阶段4 —— 初始化配置(仅首次运行)
If does not exist:
docs/specs/package.json- Generate :
docs/specs/package.json
json
{
"name": "specs",
"private": true,
"scripts": {
"test": "cucumber-js",
"test:tag": "cucumber-js --tags"
},
"dependencies": {
"@cucumber/cucumber": "^10.0.0",
"node-fetch": "^3.0.0"
}
}Add and if any interface type exists.
"@playwright/test": "^1.0.0""playwright": "^1.0.0"web- Generate :
docs/specs/cucumber.js
javascript
module.exports = {
default: {
paths: ['features/**/*.feature'],
require: ['step-definitions/**/*.steps.js', 'support/**/*.js'],
format: ['progress-bar', 'html:reports/cucumber-report.html'],
parallel: 2
}
}-
Generatewith shared state (page instance for Playwright, auth tokens, last response)
docs/specs/support/world.js -
Generatewith
docs/specs/support/hooks.js/Beforelifecycle hooks for setup and teardownAfter -
Generatebased on
docs/specs/.env.test.example:SPEC-INTERFACE.md
BASE_URL=http://localhost:3000如果不存在:
docs/specs/package.json- 生成:
docs/specs/package.json
json
{
"name": "specs",
"private": true,
"scripts": {
"test": "cucumber-js",
"test:tag": "cucumber-js --tags"
},
"dependencies": {
"@cucumber/cucumber": "^10.0.0",
"node-fetch": "^3.0.0"
}
}如果接口类型包含,添加和。
web"@playwright/test": "^1.0.0""playwright": "^1.0.0"- 生成:
docs/specs/cucumber.js
javascript
module.exports = {
default: {
paths: ['features/**/*.feature'],
require: ['step-definitions/**/*.steps.js', 'support/**/*.js'],
format: ['progress-bar', 'html:reports/cucumber-report.html'],
parallel: 2
}
}-
生成,包含共享状态(Playwright页面实例、认证令牌、最后一次响应)
docs/specs/support/world.js -
生成,包含
docs/specs/support/hooks.js/Before生命周期钩子用于初始化和清理After -
根据生成
SPEC-INTERFACE.md:docs/specs/.env.test.example
BASE_URL=http://localhost:3000Add other required env vars from SPEC-INTERFACE.md here
在此添加SPEC-INTERFACE.md中要求的其他环境变量
6. Add to the project root `package.json` (if it exists):
```json
"test:bdd": "cd docs/specs && npm test"- Add to
docs/specs/.env.test.gitignore
6. 如果项目根目录的`package.json`存在,添加以下内容:
```json
"test:bdd": "cd docs/specs && npm test"- 将添加到
docs/specs/.env.test.gitignore
Drift handling
偏差处理
SPEC-INTERFACE.mdMitigation strategy: CI failure is the drift signal.
CI red for 404 / element not found
→ dev updates SPEC-INTERFACE.md
→ /groundwork verify <feature> (regenerates step definitions)
→ CI greenDrift is detected at runtime, not compile time. This is an acceptable trade-off: the cost of a false negative in CI is much lower than the cost of tests that validate bugs.
如果API发生变化但未更新文件,可能会过时。本工具无法自动检测到这一点——因为这需要读取应用程序代码,违反约束。
SPEC-INTERFACE.md缓解策略:CI失败是偏差的信号。
CI因404/元素未找到而失败
→ 开发人员更新SPEC-INTERFACE.md
→ /groundwork verify <feature> (重新生成步骤定义)
→ CI恢复正常偏差在运行时而非编译时被检测到。这是可接受的权衡:CI中假阴性的成本远低于验证bug的测试成本。
Output summary
输出摘要
After generation, always report:
Generated:
docs/specs/step-definitions/auth/login.steps.js (3 scenarios)
docs/specs/step-definitions/payments/checkout.steps.js (2 scenarios)
Stubs (pending):
docs/specs/step-definitions/auth/login.steps.js → "login with biometric" (@pending)
Skipped:
payments/checkout.feature → "guest checkout" (@out-of-scope)
Obsolete (no matching scenario — not deleted):
docs/specs/step-definitions/auth/register.steps.js → "register with invite code"
Next steps:
1. Copy docs/specs/.env.test.example → docs/specs/.env.test and fill in values
2. Review // TODO: comments in generated files
3. Add the required selectors (see prompt below)
4. cd docs/specs && npm install && npm test生成完成后,始终报告以下内容:
已生成:
docs/specs/step-definitions/auth/login.steps.js (3个场景)
docs/specs/step-definitions/payments/checkout.steps.js (2个场景)
存根(待实现):
docs/specs/step-definitions/auth/login.steps.js → "使用生物特征登录" (@pending)
已跳过:
payments/checkout.feature → "访客结账" (@out-of-scope)
过时内容(无匹配场景——未删除):
docs/specs/step-definitions/auth/register.steps.js → "使用邀请码注册"
后续步骤:
1. 复制docs/specs/.env.test.example → docs/specs/.env.test并填写值
2. 查看生成文件中的// TODO:注释
3. 添加所需的选择器(见下方提示)
4. cd docs/specs && npm install && npm testSelector prompt (only for web
/ rest+web
features)
webrest+web选择器提示(仅适用于web
/rest+web
功能)
webrest+webAfter the output summary, if any step definition uses Playwright selectors, generate a ready-to-copy prompt listing every the step definitions expect. The dev pastes this prompt into their coding agent to add the selectors to the markup.
data-testidFormat:
---
The following data-testid attributes are required by the BDD step definitions.
Add them to the corresponding UI elements in the application code.
Page: /login
- data-testid="login-email" → email input field
- data-testid="login-password" → password input field
- data-testid="login-submit" → submit button
- data-testid="login-error" → error message container (visible on auth failure)
Page: /checkout
- data-testid="checkout-card" → credit card input
- data-testid="checkout-submit" → place order button
- data-testid="checkout-success" → order confirmation message
---Rules for the selector prompt:
- Group by page (from web entry points)
SPEC-INTERFACE.md - Each line: the exact value + a short description of the expected element
data-testid - Include selectors for assertions too (error messages, success states, content checks)
- Only list selectors that appear in the generated step definitions — no extras
输出摘要后,如果任何步骤定义使用了Playwright选择器,生成可直接复制的提示,列出步骤定义所需的所有。开发人员可将此提示粘贴到其编码工具中,以将选择器添加到标记中。
data-testid格式:
---
以下data-testid属性是BDD步骤定义所需的。
请将它们添加到应用程序代码中对应的UI元素上。
页面:/login
- data-testid="login-email" → 邮箱输入框
- data-testid="login-password" → 密码输入框
- data-testid="login-submit" → 提交按钮
- data-testid="login-error" → 错误消息容器(认证失败时可见)
页面:/checkout
- data-testid="checkout-card" → 信用卡输入框
- data-testid="checkout-submit" → 下单按钮
- data-testid="checkout-success" → 订单确认消息
---选择器提示规则:
- 按页面分组(来自SPEC-INTERFACE.md的Web入口点)
- 每行:精确的值 + 对预期元素的简短描述
data-testid - 包含断言所需的选择器(错误消息、成功状态、内容检查)
- 仅列出生成的步骤定义中出现的选择器——不添加额外内容
Common mistakes
常见错误
| Mistake | Fix |
|---|---|
| Reading application code (src/, app/, lib/) to determine URLs or config | NEVER. Use only |
| Rewriting working step definitions | Always run gap analysis first — only generate what is missing or broken |
Assuming the interface type without checking | Read the file or ask the user — never infer from code |
| Importing or calling internal project code in step definitions | Step definitions are black-box — interact only through the external interface |
Skipping | Generate a stub with |
| Deleting obsolete step definitions without asking | Report obsolete definitions to the user — never delete automatically |
Forgetting | Mark seed mechanisms, selectors, and URLs that need manual adjustment |
Inferring endpoints from code when | Ask the user to update |
| 错误 | 修复方案 |
|---|---|
| 读取应用程序代码(src/、app/、lib/)以确定URL或配置 | 绝对不要。仅使用 |
| 重写可用的步骤定义 | 始终先进行差距分析——仅生成缺失或损坏的内容 |
未检查 | 读取文件或询问用户——绝不要从代码中推断 |
| 在步骤定义中导入或调用项目内部代码 | 步骤定义是黑盒——仅通过外部接口交互 |
静默跳过 | 生成包含 |
| 未询问用户就删除过时的步骤定义 | 向用户报告过时的定义——绝不自动删除 |
忘记对依赖配置的代码标记 | 标记数据初始化机制、选择器和需要手动调整的URL |
当 | 要求用户更新 |