Files
get-shit-done/docs/zh-CN/references/verification-patterns.md
lone 1155c7564e docs: add Chinese (zh-CN) documentation
- Add language switch link to root README
- Translate README.md to docs/zh-CN/README.md
- Translate USER-GUIDE.md to docs/zh-CN/USER-GUIDE.md
- Translate all 13 reference documents to docs/zh-CN/references/

Key terminology mappings:
- context engineering → 上下文工程
- spec-driven development → 规格驱动开发
- context rot → 上下文衰减
- phase → 阶段
- milestone → 里程碑
- roadmap → 路线图

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 10:13:55 +08:00

16 KiB
Raw Blame History

验证模式

如何验证不同类型的工件是真实实现,而非存根或占位符。

<core_principle> 存在 ≠ 实现

文件存在并不意味着功能有效。验证必须检查:

  1. 存在 - 文件在预期路径
  2. 实质性 - 内容是真实实现,非占位符
  3. 已连接 - 已连接到系统的其他部分
  4. 功能性 - 调用时实际工作

级别 1-3 可以编程检查。级别 4 通常需要人工验证。 </core_principle>

<stub_detection>

通用存根模式

这些模式表明占位符代码,无论文件类型:

基于注释的存根:

# 存根注释的 Grep 模式
grep -E "(TODO|FIXME|XXX|HACK|PLACEHOLDER)" "$file"
grep -E "implement|add later|coming soon|will be" "$file" -i
grep -E "// \.\.\.|/\* \.\.\. \*/|# \.\.\." "$file"

输出中的占位符文本:

# UI 占位符模式
grep -E "placeholder|lorem ipsum|coming soon|under construction" "$file" -i
grep -E "sample|example|test data|dummy" "$file" -i
grep -E "\[.*\]|<.*>|\{.*\}" "$file"  # 模板括号未移除

空或琐碎实现:

# 什么都不做的函数
grep -E "return null|return undefined|return \{\}|return \[\]" "$file"
grep -E "pass$|\.\.\.|\bnothing\b" "$file"
grep -E "console\.(log|warn|error).*only" "$file"  # 仅日志函数

预期动态但硬编码的值:

# 硬编码 ID、计数或内容
grep -E "id.*=.*['\"].*['\"]" "$file"  # 硬编码字符串 ID
grep -E "count.*=.*\d+|length.*=.*\d+" "$file"  # 硬编码计数
grep -E "\\\$\d+\.\d{2}|\d+ items" "$file"  # 硬编码显示值

</stub_detection>

<react_components>

React/Next.js 组件

存在检查:

# 文件存在且导出组件
[ -f "$component_path" ] && grep -E "export (default |)function|export const.*=.*\(" "$component_path"

实质性检查:

# 返回实际 JSX非占位符
grep -E "return.*<" "$component_path" | grep -v "return.*null" | grep -v "placeholder" -i

# 有有意义的内容(不仅仅是包装 div
grep -E "<[A-Z][a-zA-Z]+|className=|onClick=|onChange=" "$component_path"

# 使用 props 或 state非静态
grep -E "props\.|useState|useEffect|useContext|\{.*\}" "$component_path"

React 特有的存根模式:

// 危险信号 - 这些是存根:
return <div>Component</div>
return <div>Placeholder</div>
return <div>{/* TODO */}</div>
return <p>Coming soon</p>
return null
return <></>

// 也是存根 - 空处理器:
onClick={() => {}}
onChange={() => console.log('clicked')}
onSubmit={(e) => e.preventDefault()}  // 仅阻止默认,什么都不做

连接检查:

# 组件导入它需要的东西
grep -E "^import.*from" "$component_path"

# Props 实际被使用(不仅仅是接收)
# 查找解构或 props.X 用法
grep -E "\{ .* \}.*props|\bprops\.[a-zA-Z]+" "$component_path"

# API 调用存在(对于数据获取组件)
grep -E "fetch\(|axios\.|useSWR|useQuery|getServerSideProps|getStaticProps" "$component_path"

功能验证(需要人工):

  • 组件是否渲染可见内容?
  • 交互元素是否响应点击?
  • 数据是否加载并显示?
  • 错误状态是否适当显示?

</react_components>

<api_routes>

API 路由Next.js App Router / Express 等)

存在检查:

# 路由文件存在
[ -f "$route_path" ]

# 导出 HTTP 方法处理器Next.js App Router
grep -E "export (async )?(function|const) (GET|POST|PUT|PATCH|DELETE)" "$route_path"

# 或 Express 风格处理器
grep -E "\.(get|post|put|patch|delete)\(" "$route_path"

实质性检查:

# 有实际逻辑,不仅仅是 return 语句
wc -l "$route_path"  # 超过 10-15 行表明真实实现

# 与数据源交互
grep -E "prisma\.|db\.|mongoose\.|sql|query|find|create|update|delete" "$route_path" -i

# 有错误处理
grep -E "try|catch|throw|error|Error" "$route_path"

# 返回有意义的响应
grep -E "Response\.json|res\.json|res\.send|return.*\{" "$route_path" | grep -v "message.*not implemented" -i

API 路由特有的存根模式:

// 危险信号 - 这些是存根:
export async function POST() {
  return Response.json({ message: "Not implemented" })
}

export async function GET() {
  return Response.json([])  // 空 array 无数据库查询
}

export async function PUT() {
  return new Response()  // 空响应
}

// 仅控制台日志:
export async function POST(req) {
  console.log(await req.json())
  return Response.json({ ok: true })
}

连接检查:

# 导入数据库/服务客户端
grep -E "^import.*prisma|^import.*db|^import.*client" "$route_path"

# 实际使用请求体(对于 POST/PUT
grep -E "req\.json\(\)|req\.body|request\.json\(\)" "$route_path"

# 验证输入(不仅仅信任请求)
grep -E "schema\.parse|validate|zod|yup|joi" "$route_path"

功能验证(人工或自动化):

  • GET 是否从数据库返回真实数据?
  • POST 是否实际创建记录?
  • 错误响应是否有正确的状态码?
  • 认证检查是否实际执行?

</api_routes>

<database_schema>

数据库模式Prisma / Drizzle / SQL

存在检查:

# 模式文件存在
[ -f "prisma/schema.prisma" ] || [ -f "drizzle/schema.ts" ] || [ -f "src/db/schema.sql" ]

# 模型/表已定义
grep -E "^model $model_name|CREATE TABLE $table_name|export const $table_name" "$schema_path"

实质性检查:

# 有预期字段(不仅仅是 id
grep -A 20 "model $model_name" "$schema_path" | grep -E "^\s+\w+\s+\w+"

# 有预期关系
grep -E "@relation|REFERENCES|FOREIGN KEY" "$schema_path"

# 有适当的字段类型(不全是 String
grep -A 20 "model $model_name" "$schema_path" | grep -E "Int|DateTime|Boolean|Float|Decimal|Json"

模式特有的存根模式:

// 危险信号 - 这些是存根:
model User {
  id String @id
  // TODO: add fields
}

model Message {
  id        String @id
  content   String  // 只有一个真实字段
}

// 缺少关键字段:
model Order {
  id     String @id
  // 缺少: userId, items, total, status, createdAt
}

连接检查:

# 迁移存在且已应用
ls prisma/migrations/ 2>/dev/null | wc -l  # 应该 > 0
npx prisma migrate status 2>/dev/null | grep -v "pending"

# 客户端已生成
[ -d "node_modules/.prisma/client" ]

功能验证:

# 可以查询表(自动化)
npx prisma db execute --stdin <<< "SELECT COUNT(*) FROM $table_name"

</database_schema>

<hooks_utilities>

自定义 Hooks 和工具

存在检查:

# 文件存在且导出函数
[ -f "$hook_path" ] && grep -E "export (default )?(function|const)" "$hook_path"

实质性检查:

# Hook 使用 React hooks对于自定义 hooks
grep -E "useState|useEffect|useCallback|useMemo|useRef|useContext" "$hook_path"

# 有有意义的返回值
grep -E "return \{|return \[" "$hook_path"

# 超过琐碎长度
[ $(wc -l < "$hook_path") -gt 10 ]

Hooks 特有的存根模式:

// 危险信号 - 这些是存根:
export function useAuth() {
  return { user: null, login: () => {}, logout: () => {} }
}

export function useCart() {
  const [items, setItems] = useState([])
  return { items, addItem: () => console.log('add'), removeItem: () => {} }
}

// 硬编码返回:
export function useUser() {
  return { name: "Test User", email: "test@example.com" }
}

连接检查:

# Hook 实际在某处被导入
grep -r "import.*$hook_name" src/ --include="*.tsx" --include="*.ts" | grep -v "$hook_path"

# Hook 实际被调用
grep -r "$hook_name()" src/ --include="*.tsx" --include="*.ts" | grep -v "$hook_path"

</hooks_utilities>

<environment_config>

环境变量和配置

存在检查:

# .env 文件存在
[ -f ".env" ] || [ -f ".env.local" ]

# 必需变量已定义
grep -E "^$VAR_NAME=" .env .env.local 2>/dev/null

实质性检查:

# 变量有实际值(非占位符)
grep -E "^$VAR_NAME=.+" .env .env.local 2>/dev/null | grep -v "your-.*-here|xxx|placeholder|TODO" -i

# 值对类型看起来有效:
# - URL 应以 http 开头
# - 密钥应足够长
# - 布尔值应为 true/false

环境变量特有的存根模式:

# 危险信号 - 这些是存根:
DATABASE_URL=your-database-url-here
STRIPE_SECRET_KEY=sk_test_xxx
API_KEY=placeholder
NEXT_PUBLIC_API_URL=http://localhost:3000  # 生产环境仍指向 localhost

连接检查:

# 变量实际在代码中使用
grep -r "process\.env\.$VAR_NAME|env\.$VAR_NAME" src/ --include="*.ts" --include="*.tsx"

# 变量在验证模式中(如果使用 zod 等验证 env
grep -E "$VAR_NAME" src/env.ts src/env.mjs 2>/dev/null

</environment_config>

<wiring_verification>

连接验证模式

连接验证检查组件是否实际通信。这是大多数存根隐藏的地方。

模式:组件 → API

检查: 组件是否实际调用 API

# 查找 fetch/axios 调用
grep -E "fetch\(['\"].*$api_path|axios\.(get|post).*$api_path" "$component_path"

# 验证未被注释掉
grep -E "fetch\(|axios\." "$component_path" | grep -v "^.*//.*fetch"

# 检查响应被使用
grep -E "await.*fetch|\.then\(|setData|setState" "$component_path"

危险信号:

// Fetch 存在但响应被忽略:
fetch('/api/messages')  // 无 await无 .then无赋值

// Fetch 在注释中:
// fetch('/api/messages').then(r => r.json()).then(setMessages)

// Fetch 到错误的端点:
fetch('/api/message')  // 拼写错误 - 应该是 /api/messages

模式API → 数据库

检查: API 路由是否实际查询数据库?

# 查找数据库调用
grep -E "prisma\.$model|db\.query|Model\.find" "$route_path"

# 验证被 await
grep -E "await.*prisma|await.*db\." "$route_path"

# 检查结果被返回
grep -E "return.*json.*data|res\.json.*result" "$route_path"

危险信号:

// 查询存在但结果未返回:
await prisma.message.findMany()
return Response.json({ ok: true })  // 返回静态值,非查询结果

// 查询未被 await
const messages = prisma.message.findMany()  // 缺少 await
return Response.json(messages)  // 返回 Promise非数据

模式:表单 → 处理器

检查: 表单提交是否实际做些什么?

# 查找 onSubmit 处理器
grep -E "onSubmit=\{|handleSubmit" "$component_path"

# 检查处理器有内容
grep -A 10 "onSubmit.*=" "$component_path" | grep -E "fetch|axios|mutate|dispatch"

# 验证不仅仅是 preventDefault
grep -A 5 "onSubmit" "$component_path" | grep -v "only.*preventDefault" -i

危险信号:

// 处理器仅阻止默认:
onSubmit={(e) => e.preventDefault()}

// 处理器仅日志:
const handleSubmit = (data) => {
  console.log(data)
}

// 处理器为空:
onSubmit={() => {}}

模式:状态 → 渲染

检查: 组件是否渲染状态,而非硬编码内容?

# 查找 JSX 中的状态使用
grep -E "\{.*messages.*\}|\{.*data.*\}|\{.*items.*\}" "$component_path"

# 检查状态的 map/render
grep -E "\.map\(|\.filter\(|\.reduce\(" "$component_path"

# 验证动态内容
grep -E "\{[a-zA-Z_]+\." "$component_path"  # 变量插值

危险信号:

// 硬编码而非状态:
return <div>
  <p>Message 1</p>
  <p>Message 2</p>
</div>

// 状态存在但未渲染:
const [messages, setMessages] = useState([])
return <div>No messages</div>  // 总是显示 "no messages"

// 渲染错误的状态:
const [messages, setMessages] = useState([])
return <div>{otherData.map(...)}</div>  // 使用不同数据

</wiring_verification>

<verification_checklist>

快速验证清单

对于每种工件类型,运行此清单:

组件清单

  • 文件存在于预期路径
  • 导出函数/const 组件
  • 返回 JSX非 null/空)
  • 渲染中无占位符文本
  • 使用 props 或 state非静态
  • 事件处理器有真实实现
  • 导入正确解析
  • 在应用某处被使用

API 路由清单

  • 文件存在于预期路径
  • 导出 HTTP 方法处理器
  • 处理器超过 5 行
  • 查询数据库或服务
  • 返回有意义的响应(非空/占位符)
  • 有错误处理
  • 验证输入
  • 从前端调用

模式清单

  • 模型/表已定义
  • 有所有预期字段
  • 字段有适当类型
  • 如需要关系已定义
  • 迁移存在且已应用
  • 客户端已生成

Hook/工具清单

  • 文件存在于预期路径
  • 导出函数
  • 有有意义的实现(非空返回)
  • 在应用某处被使用
  • 返回值被消费

连接清单

  • 组件 → API: fetch/axios 调用存在且使用响应
  • API → 数据库: 查询存在且结果返回
  • 表单 → 处理器: onSubmit 调用 API/mutation
  • 状态 → 渲染: 状态变量出现在 JSX 中

</verification_checklist>

<automated_verification_script>

自动化验证方法

对于验证子代理,使用此模式:

# 1. 检查存在
check_exists() {
  [ -f "$1" ] && echo "EXISTS: $1" || echo "MISSING: $1"
}

# 2. 检查存根模式
check_stubs() {
  local file="$1"
  local stubs=$(grep -c -E "TODO|FIXME|placeholder|not implemented" "$file" 2>/dev/null || echo 0)
  [ "$stubs" -gt 0 ] && echo "STUB_PATTERNS: $stubs in $file"
}

# 3. 检查连接(组件调用 API
check_wiring() {
  local component="$1"
  local api_path="$2"
  grep -q "$api_path" "$component" && echo "WIRED: $component$api_path" || echo "NOT_WIRED: $component$api_path"
}

# 4. 检查实质性(超过 N 行,有预期模式)
check_substantive() {
  local file="$1"
  local min_lines="$2"
  local pattern="$3"
  local lines=$(wc -l < "$file" 2>/dev/null || echo 0)
  local has_pattern=$(grep -c -E "$pattern" "$file" 2>/dev/null || echo 0)
  [ "$lines" -ge "$min_lines" ] && [ "$has_pattern" -gt 0 ] && echo "SUBSTANTIVE: $file" || echo "THIN: $file ($lines lines, $has_pattern matches)"
}

对每个必须有工件运行这些检查。汇总结果到 VERIFICATION.md。

</automated_verification_script>

<human_verification_triggers>

何时需要人工验证

有些事情无法编程验证。标记这些需要人工测试:

始终人工:

  • 视觉外观(看起来对吗?)
  • 用户流程完成(能实际做那件事吗?)
  • 实时行为WebSocket、SSE
  • 外部服务集成Stripe、邮件发送
  • 错误消息清晰度(消息有帮助吗?)
  • 性能感觉(感觉快吗?)

如不确定则人工:

  • grep 无法追踪的复杂连接
  • 依赖状态的动态行为
  • 边缘情况和错误状态
  • 移动端响应式
  • 无障碍性

人工验证请求格式:

## 需要人工验证

### 1. 聊天消息发送
**测试:** 输入消息并点击发送
**预期:** 消息出现在列表中,输入框清空
**检查:** 刷新后消息是否持久?

### 2. 错误处理
**测试:** 断开网络,尝试发送
**预期:** 错误消息出现,消息未丢失
**检查:** 重连后能重试吗?

</human_verification_triggers>

<checkpoint_automation_reference>

检查点前自动化

关于自动化优先的检查点模式、服务器生命周期管理、CLI 安装处理和错误恢复协议,请参阅:

@~/.claude/get-shit-done/references/checkpoints.md<automation_reference> 部分

关键原则:

  • Claude 在呈现检查点之前设置验证环境
  • 用户从不运行 CLI 命令(仅访问 URL
  • 服务器生命周期:检查点前启动、处理端口冲突、持续运行
  • CLI 安装:安全处自动安装,否则检查点让用户选择
  • 错误处理:检查点前修复损坏环境,绝不呈现有失败设置的检查点

</checkpoint_automation_reference>