agile-tdd
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTDD (Test-Driven Development)
TDD(测试驱动开发)
Guide the Red-Green-Refactor cycle and pragmatic testing strategy. "Write tests. Not too many. Mostly integration."
Initial context received via slash: $ARGUMENTS
If is filled (e.g., module name, feature description), use as starting point.
If empty, ask what will be tested.
$ARGUMENTS指导Red-Green-Refactor循环与务实的测试策略。遵循原则:"编写测试,但不要过多,以集成测试为主。"
通过斜杠命令接收初始上下文:$ARGUMENTS
如果已填写(如模块名称、功能描述),则以此为起点。
如果为空,则询问用户要测试的内容。
$ARGUMENTSLanguage
语言规范
Write artifacts and test descriptions in the user's language. When in doubt, ask. Test code itself (function names, assertions) stays in English.
用用户的语言编写工件和测试描述。如有疑问,可询问用户。测试代码本身(函数名、断言)需保持英文。
Project root
项目根目录
This skill writes artifacts at paths relative to the project root (the repo where the work happens), not the agent's current working directory.
- If invoked from inside the project, use the relative paths shown in this skill.
- If invoked from another directory (e.g., a sibling repo, or when the project lives elsewhere), prepend to every artifact path.
<project-root>/ - When the project root is ambiguous, confirm with the user via the harness question tool before writing.
本技能会在项目根目录(即工作所在的仓库)的相对路径下生成工件,而非Agent当前的工作目录。
- 如果从项目内部调用,使用本技能中显示的相对路径。
- 如果从其他目录调用(如兄弟仓库,或项目位于其他位置),请在每个工件路径前添加。
<project-root>/ - 当项目根目录不明确时,在生成工件前通过工具询问用户确认。
Prompting
提示规范
Follow the project-wide convention in / ("Skill Prompting Conventions"). Use the harness's structured-question tool — (Claude Code), (Codex), or (OpenCode) — for the decision points below. Use free-form text only where a path/name/value cannot be enumerated.
CLAUDE.mdAGENTS.mdAskUserQuestionask_user_questionquestion| Decision point | Why structured | Suggested options |
|---|---|---|
| Enforcement mode (when installing) | Hard-to-undo policy choice | warn · block · keep current |
| Test strategy (when ambiguous) | Affects file layout | sibling · sibling_dir · tests_root |
| Exempt a specific path | Edits guardrails config | yes · no · review later |
Free-form prompts (no structured tool):
- Test descriptions
- Exemption rationale
No-pause mode: if the user has explicitly disabled mid-skill clarification, convert every structured prompt into an entry under Open questions (or equivalent) and proceed without blocking.
遵循/中的项目级规范("技能提示约定")。使用工具的结构化提问功能——Claude Code的、Codex的或OpenCode的——处理以下决策点。仅在路径/名称/值无法枚举时使用自由格式文本。
CLAUDE.mdAGENTS.mdAskUserQuestionask_user_questionquestion| 决策点 | 为何使用结构化 | 建议选项 |
|---|---|---|
| 安装时的强制执行模式 | 难以撤销的策略选择 | warn · block · keep current |
| 测试策略(存在歧义时) | 影响文件布局 | sibling · sibling_dir · tests_root |
| 豁免特定路径 | 编辑防护配置 | yes · no · review later |
自由格式提示(无需结构化工具):
- 测试描述
- 豁免理由
无暂停模式:如果用户明确禁用了技能执行中的澄清环节,请将所有结构化提示转换为「未解决问题」(或等效条目)并继续执行,无需阻塞。
When to use
适用场景
- Starting a new feature with TDD
- Adding tests to existing code
- Establishing test coverage for a module
- Unclear whether something needs unit, integration, or E2E tests
- 采用TDD启动新功能开发
- 为现有代码添加测试
- 为模块建立测试覆盖率
- 不确定某功能需要单元测试、集成测试还是端到端(E2E)测试
When NOT to use
不适用场景
- Quick prototypes where tests add no value -- use
/agile-proto - Throwaway scripts
- Pure documentation changes
- 测试无价值的快速原型——请使用
/agile-proto - 一次性脚本
- 纯文档变更
TDD cycle
TDD循环
- Red -- write a failing test that describes the desired behavior
- Green -- write the minimum code to make it pass
- Refactor -- improve structure without changing behavior
- Repeat
Present each step explicitly. Do not skip Red -- the test must fail first.
- Red(红)——编写一个描述期望行为的失败测试
- Green(绿)——编写最少代码使测试通过
- Refactor(重构)——在不改变行为的前提下优化代码结构
- 重复上述步骤
需明确呈现每个步骤,不得跳过Red阶段——测试必须先失败。
Test pyramid (pragmatic)
务实测试金字塔
| Layer | Target | Focus |
|---|---|---|
| Unit | 60% | Pure functions, transformers, utils |
| Integration | 30% | Services, DB interactions, API routes |
| E2E | 10% | Critical user flows |
Overall coverage target: 75%+.
For front-end work, treat these percentages as risk guidance, not quotas. Prefer integration tests that exercise user behavior, validation, local state, API contracts, permissions, offline/sync behavior, and critical flows. Avoid tests that only assert static text, that a button rendered, or implementation details of a design-system component.
When a project keeps business rules in , use those rule IDs to decide what deserves tests. Tests should prove behavior behind important rules, not restate the rule text.
planning/<initiative>/business/*.md| 层级 | 占比目标 | 关注重点 |
|---|---|---|
| 单元测试 | 60% | 纯函数、转换器、工具类 |
| 集成测试 | 30% | 服务、数据库交互、API路由 |
| E2E测试 | 10% | 关键用户流程 |
整体覆盖率目标:75%+。
对于前端工作,这些百分比仅作为风险指导,而非硬性指标。优先选择能覆盖用户行为、验证逻辑、本地状态、API契约、权限、离线/同步行为及关键流程的集成测试。避免仅断言静态文本、按钮是否渲染或设计系统组件实现细节的测试。
当项目在中存储业务规则时,使用这些规则ID来决定哪些内容需要测试。测试应验证重要规则背后的行为,而非重复规则文本。
planning/<initiative>/business/*.mdFile structure
文件结构
- Unit: co-located with source (beside
foo.test.ts)foo.ts - Integration/E2E: with
tests/,integration/,e2e/,helpers/,fixtures/mocks/ - Naming: (unit/integration),
.test.ts(E2E).e2e.test.ts - Never
.spec.ts
- 单元测试:与源码同目录(与
foo.test.ts相邻)foo.ts - 集成/E2E测试:存放在目录下,包含
tests/、integration/、e2e/、helpers/、fixtures/子目录mocks/ - 命名规范:(单元/集成测试),
.test.ts(E2E测试).e2e.test.ts - 禁止使用
.spec.ts
Rules
规则
- AAA pattern (Arrange / Act / Assert)
- One concept per test
- Descriptive names that read as sentences
- Always use factories (e.g., ) over hardcoded data
faker - Isolate with -- no shared state between tests
beforeEach - Test behavior, not implementation details
- AAA模式(Arrange / Act / Assert,准备/执行/断言)
- 每个测试仅验证一个概念
- 描述性命名,可读为完整句子
- 始终使用工厂函数(如)而非硬编码数据
faker - 使用隔离测试——测试间无共享状态
beforeEach - 测试行为,而非实现细节
Anti-patterns (avoid)
反模式(需避免)
- Interdependent tests (test A depends on test B running first)
- Arbitrary -- use proper waits
sleep(ms) - Testing private methods -- test through public API
- in tests -- use proper assertions
console.log - Order-dependent tests
- Mocking what you own (mock external dependencies, not your own code)
- 依赖型测试(测试A依赖测试B先执行)
- 随意使用——使用合适的等待机制
sleep(ms) - 测试私有方法——通过公共API进行测试
- 测试中使用——使用正规断言
console.log - 顺序依赖型测试
- 模拟自身代码(仅模拟外部依赖,而非自有代码)
Coverage targets (granular)
细分覆盖率目标
| Area | Target |
|---|---|
| Transformers / pure functions | 90%+ |
| Utils | 85%+ |
| Services | 80%+ |
| Routes / handlers | 70%+ |
| 领域 | 目标 |
|---|---|
| 转换器/纯函数 | 90%+ |
| 工具类 | 85%+ |
| 服务 | 80%+ |
| 路由/处理器 | 70%+ |
Commands (Bun)
Bun命令
bun test
bun test --watch
bun test --coverage
bun test --filter "name"
bun test src/dir/Adjust for other runtimes (vitest, jest) as needed. Detect the project's test runner from or config files before suggesting commands.
package.jsonbun test
bun test --watch
bun test --coverage
bun test --filter "name"
bun test src/dir/根据其他运行时(vitest、jest)调整命令。在推荐命令前,先从或配置文件中检测项目的测试运行器。
package.jsonProcess
流程
1. Understand what to test
1. 明确测试对象
Explore the code to understand:
- What module or feature needs tests
- What behaviors are critical
- What is already covered (check existing tests)
- Which business rule IDs, acceptance criteria, or prototype flows the change must satisfy
探索代码以了解:
- 哪个模块或功能需要测试
- 哪些行为是关键的
- 已有哪些测试覆盖(检查现有测试)
- 变更必须满足哪些业务规则ID、验收标准或原型流程
2. Choose the right test type
2. 选择合适的测试类型
Use the test pyramid as guide:
- Pure function with no side effects? Unit test.
- Service that talks to DB or external API? Integration test.
- Critical user flow that spans multiple systems? E2E test.
- Front-end behavior with validation, API contract, permission, optimistic update, or offline/sync state? Integration test.
- Static copy, simple rendering, or visual-only detail with no rule? Usually no test unless it protects a known regression.
For local-first products, give priority to tests that cover command validation, optimistic state, offline queue persistence, reconciliation, conflict handling, permissions, and audit events.
以测试金字塔为指导:
- 无副作用的纯函数?使用单元测试。
- 与数据库或外部API交互的服务?使用集成测试。
- 跨多个系统的关键用户流程?使用E2E测试。
- 包含验证逻辑、API契约、权限、乐观更新或离线/同步状态的前端行为?使用集成测试。
- 静态文案、简单渲染或无规则的纯视觉细节?通常无需测试,除非能防止已知回归。
对于本地优先产品,优先测试命令验证、乐观状态、离线队列持久化、协调、冲突处理、权限和审计事件。
3. Execute the TDD cycle
3. 执行TDD循环
For each behavior:
- Write the failing test (Red)
- Implement the minimum code (Green)
- Refactor if needed
- Verify the test still passes
Record the business rule ID or acceptance criterion in the test description or surrounding story artifact when that mapping helps future refinement.
针对每个行为:
- 编写失败测试(Red)
- 实现最少代码(Green)
- 必要时进行重构
- 验证测试仍能通过
当业务规则ID或验收标准与测试的映射有助于未来优化时,将其记录在测试描述或相关的故事工件中。
4. Verify coverage
4. 验证覆盖率
Run coverage and check against targets. Fill gaps in critical areas first.
运行覆盖率检查并对照目标。优先填补关键领域的缺口。
Chaining
流程联动
- During feature implementation: work inside the checklist
/agile-story - After implementation: to review test quality
/agile-refinement - Before closing: ensure tests are part of (closure mode) verification
/agile-status - If the TDD workflow exposes repeated friction, missing guidance, weak templates, or unclear verification, capture a concise skill feedback note with the affected skill/template, evidence, proposed change, and validation artifact.
- If repeated TDD friction suggests a skill/template change, use before editing the process library.
/agile-skill-feedback
- 功能实现期间:在检查清单内工作
/agile-story - 实现完成后:使用评审测试质量
/agile-refinement - 关闭前:确保测试是(关闭模式)验证的一部分
/agile-status - 如果TDD工作流程暴露出重复摩擦、缺失指导、薄弱模板或模糊验证,需记录简洁的技能反馈笔记,包含受影响的技能/模板、证据、提议的变更及验证工件。
- 如果重复的TDD摩擦表明需要修改技能/模板,请在编辑流程库前使用。
/agile-skill-feedback
Enforcement (optional, opt-in per project)
强制执行(可选,按项目启用)
Beyond advisory guidance, this skill ships hook templates that turn the TDD rule into a project-level guardrail. When a project enables them, every implementation session is checked at the file-write level — without the agent having to remember to invoke this skill.
除了指导性建议,本技能还提供钩子模板,可将TDD规则转化为项目级防护措施。当项目启用后,每次实现会话都会在文件写入层面进行检查——无需Agent刻意调用本技能。
What gets enforced
强制执行内容
- PreToolUse on — if the target file matches
Write|Edit|MultiEditinsource_pathsand a companion test does not exist, the hook warns (.tdd-guardrails.yml) or blocks the tool call (mode: warn).mode: block - Stop hook — at session end, scans the git diff and reports source files touched without a companion test.
- SessionStart hook — announces "TDD enforcement active" so the agent knows the rule is in force.
The hook templates live at and the config schema at .
skills/agile-tdd/templates/hooks/*.sh.tmplskills/agile-tdd/templates/tdd-guardrails.yml.tmpl- 操作前的PreToolUse检查——如果目标文件匹配
Write|Edit|MultiEdit中的.tdd-guardrails.yml且未存在配套测试,钩子会发出警告(source_paths)或阻止工具调用(mode: warn)。mode: block - Stop钩子——会话结束时,扫描git差异并报告未添加配套测试的已修改源码文件。
- SessionStart钩子——宣布"TDD强制执行已激活",让Agent知晓规则生效。
钩子模板位于,配置模式位于。
skills/agile-tdd/templates/hooks/*.sh.tmplskills/agile-tdd/templates/tdd-guardrails.yml.tmplConfig (.tdd-guardrails.yml
)
.tdd-guardrails.yml配置(.tdd-guardrails.yml
)
.tdd-guardrails.yml| Key | Meaning |
|---|---|
| Global on/off |
| |
| Globs that require a companion test |
| |
| Globs allowed without a test (entry points, generated files, UI primitives) |
Pattern semantics: the hook scripts use bash globs, not extended globstar. In bash patterns, matches any sequence of characters including ; there is no . So matches both and . Do not use in or .
casecase*/**apps/*/src/*.tsapps/server/src/handler.tsapps/server/src/auth/handler.ts**source_pathsexemptions| 键名 | 含义 |
|---|---|
| 全局开关 |
| |
| 需要配套测试的文件匹配模式(Glob) |
| |
| 无需测试的文件匹配模式(入口文件、生成文件、UI原语) |
模式语义:钩子脚本使用bash 模式,而非扩展globstar。在bash 模式中,匹配包括在内的任意字符序列;不存在。因此既匹配也匹配。请勿在或中使用。
casecase*/**apps/*/src/*.tsapps/server/src/handler.tsapps/server/src/auth/handler.tssource_pathsexemptions**Enforcement caveats
强制执行注意事项
The hook checks file-pair existence — it does not, and cannot, verify:
- That the test was written before the source (no Red-before-Green order check).
- That the test actually exercises the source (no semantic match).
- That the test currently passes (no test execution).
Semantic discipline (one behavior per test, factories over hardcoded data, descriptive names, AAA) still belongs to the agent. The hooks are guardrails, not a guarantee.
钩子仅检查文件对是否存在——无法验证:
- 测试是否在源码之前编写(无法检查Red先于Green的顺序)。
- 测试是否实际覆盖了源码(无语义匹配)。
- 测试当前是否通过(无测试执行)。
语义规范(每个测试对应一个行为、使用工厂函数而非硬编码数据、描述性命名、AAA模式)仍需Agent遵守。钩子只是防护措施,而非质量保证。
Manual install (until a tdd-init
script exists)
tdd-init手动安装(直到tdd-init
脚本推出)
tdd-initPer-harness mechanics differ — Claude Code and Codex run shell hooks directly; OpenCode runs a JS plugin that orchestrates the same shell scripts.
-
Copyto
templates/tdd-guardrails.yml.tmpland edit<project-root>/.tdd-guardrails.ymlandsource_pathsto match the repo layout.exemptionsThen run the project-type detectors to populate:project_typesbashfor type in .claude/skills/agile-tdd/templates/project-types/*/; do name="$(basename "$type")" [ -f "$type/detect.sh" ] || continue bash "$type/detect.sh" "$PWD" && echo "detected: $name" doneFor each detected type, append itsto the newly-createdguardrails.partial.yml(or merge if the block already exists) and add the type slug to the.tdd-guardrails.ymllist.project_types -
Copy the hook templates. Fromto
templates/hooks/,<project-root>/.claude/hooks/, and<project-root>/.codex/hooks/(the OpenCode plugin invokes the same scripts via<project-root>/.opencode/hooks/). Drop thenode:child_process.spawnsuffix and.tmpl.chmod +xAlso copy the shared helpers:templates/lib/audit-helpers.sh.tmpl → .claude/hooks/lib/audit-helpers.shand the project-types directory:templates/project-types/ → .claude/hooks/project-types/The session-audit script searches both locations. Codex/OpenCode should mirror underand.codex/hooks/..opencode/hooks/ -
Register the hooks in each harness config:
- Claude Code (): add a
.claude/settings.jsonentry matchingPreToolUsecallingWrite|Edit|MultiEdit, a$CLAUDE_PROJECT_DIR/.claude/hooks/tdd-pre-write.shentry callingStop, and atdd-session-audit.shentry callingSessionStart. Merge with existing hooks (e.g. wiki-init) — do not replace.tdd-announce.sh - Codex (): add
.codex/hooks.jsonmatchingPreToolUse,apply_patch|Edit|Write|MultiEdit, andStopentries. UseSessionStartas the command form (Codex pattern). Includebash "$(git rev-parse --show-toplevel)/.codex/hooks/<script>.sh"field for each.statusMessage - OpenCode: copy to
templates/opencode-plugin.js.tmpl. The plugin subscribes to<project-root>/.opencode/plugins/tdd-guardrails.js(PreToolUse equivalent),tool.execute.before(SessionStart equivalent), andsession.created(closest to Stop — the audit shell is idempotent via a tmp state file so multi-firing is safe). The plugin spawns the samesession.idlescripts via.opencode/hooks/tdd-*.sh. OpenCode does not invoke shell scripts directly; the plugin is the entry point.node:child_process.spawn
- Claude Code (
-
Append the contents ofto
templates/agents-block.md.tmplandAGENTS.mdso the agent is told the project has TDD enforcement.CLAUDE.mdFor each detected project-type, also append itsto the same files (preserving thetemplates/project-types/<type>/agents-block.partial.mdmarkers so re-installs can update in place).<!-- agile-tdd:<type>:start --> / :end -
Register the type-specific matchers in:
.claude/settings.json- For : a
taurientry matchingPostToolUsecallingmcp__tauri__webview_screenshot|mcp__tauri__webview_execute_js|mcp__tauri__webview_dom_snapshot|mcp__tauri__manage_window.$CLAUDE_PROJECT_DIR/.claude/hooks/tdd-record-mcp.sh
The Codex equivalent goes in; for OpenCode, subscribe to.codex/hooks.jsonfiltered by tool name and spawn the same shell viatool.execute.after.node:child_process.spawn - For
The hooks are guardrails, not a guarantee — they check file-pair existence, not test quality. Semantic discipline (one behavior per test, factories over hardcoded data) still belongs to the agent.
不同工具的实现机制不同——Claude Code和Codex直接运行shell钩子;OpenCode运行JS插件来协调相同的shell脚本。
-
将复制到
templates/tdd-guardrails.yml.tmpl,并编辑<project-root>/.tdd-guardrails.yml和source_paths以匹配仓库布局。exemptions然后运行项目类型检测器来填充:project_typesbashfor type in .claude/skills/agile-tdd/templates/project-types/*/; do name="$(basename "$type")" [ -f "$type/detect.sh" ] || continue bash "$type/detect.sh" "$PWD" && echo "detected: $name" done对于每个检测到的类型,将其追加到新创建的guardrails.partial.yml中(如果块已存在则合并),并将类型标识添加到.tdd-guardrails.yml列表。project_types -
复制钩子模板。从复制到
templates/hooks/、<project-root>/.claude/hooks/以及<project-root>/.codex/hooks/(OpenCode插件通过<project-root>/.opencode/hooks/调用相同的脚本)。移除node:child_process.spawn后缀并执行.tmpl赋予执行权限。chmod +x同时复制共享工具函数:templates/lib/audit-helpers.sh.tmpl → .claude/hooks/lib/audit-helpers.sh以及项目类型目录:templates/project-types/ → .claude/hooks/project-types/会话审计脚本会搜索这两个位置。Codex/OpenCode应在和.codex/hooks/下镜像相同结构。.opencode/hooks/ -
在每个工具的配置中注册钩子:
- Claude Code():添加匹配
.claude/settings.json的Write|Edit|MultiEdit条目,调用PreToolUse;添加调用$CLAUDE_PROJECT_DIR/.claude/hooks/tdd-pre-write.sh的tdd-session-audit.sh条目;添加调用Stop的tdd-announce.sh条目。与现有钩子(如wiki-init)合并——请勿替换。SessionStart - Codex():添加匹配
.codex/hooks.json的apply_patch|Edit|Write|MultiEdit、PreToolUse和Stop条目。使用SessionStart作为命令格式(Codex规范)。为每个条目添加bash "$(git rev-parse --show-toplevel)/.codex/hooks/<script>.sh"字段。statusMessage - OpenCode:将复制到
templates/opencode-plugin.js.tmpl。该插件订阅<project-root>/.opencode/plugins/tdd-guardrails.js(等效于PreToolUse)、tool.execute.before(等效于SessionStart)和session.created(最接近Stop——审计shell通过临时状态文件实现幂等,因此多次触发安全)。插件通过session.idle调用相同的node:child_process.spawn脚本。OpenCode不直接调用shell脚本——插件是入口点。.opencode/hooks/tdd-*.sh
- Claude Code(
-
将的内容追加到
templates/agents-block.md.tmpl和AGENTS.md中,告知Agent项目已启用TDD强制执行。CLAUDE.md对于每个检测到的项目类型,还需将其追加到相同文件中(保留templates/project-types/<type>/agents-block.partial.md标记,以便重新安装时可以原地更新)。<!-- agile-tdd:<type>:start --> / :end -
在中注册类型特定匹配器:
.claude/settings.json- 对于:添加匹配
tauri的mcp__tauri__webview_screenshot|mcp__tauri__webview_execute_js|mcp__tauri__webview_dom_snapshot|mcp__tauri__manage_window条目,调用PostToolUse。$CLAUDE_PROJECT_DIR/.claude/hooks/tdd-record-mcp.sh
Codex的等效配置在中;对于OpenCode,订阅.codex/hooks.json并按工具名称过滤,通过tool.execute.after调用相同的shell脚本。node:child_process.spawn - 对于
钩子只是防护措施,而非质量保证——它们仅检查文件对是否存在,不验证测试质量。语义规范(每个测试对应一个行为、使用工厂函数而非硬编码数据)仍需Agent遵守。
Harness compatibility matrix
工具兼容性矩阵
| Harness | Entry point | Pre-write event | Stop equivalent | Session start |
|---|---|---|---|---|
| Claude Code | | | | |
| Codex | | | | |
| OpenCode | | | | |
| 工具 | 入口点 | 预写入事件 | 等效Stop事件 | 会话启动事件 |
|---|---|---|---|---|
| Claude Code | | | | |
| Codex | | | | |
| OpenCode | | | | |
Bypassing intentionally
有意绕过强制执行
- For one path: add it to .
.tdd-guardrails.yml → exemptions - For one session: temporarily set (and revert before commit).
enabled: false - Never delete the test file just to silence the hook — that defeats the point.
- 单个路径:将其添加到。
.tdd-guardrails.yml → exemptions - 单个会话:临时设置(提交前恢复)。
enabled: false - 请勿为了消除钩子警告而删除测试文件——这违背了初衷。
Project-type templates
项目类型模板
Beyond the base companion-test rule, ships project-type
templates under . Each template
opts into project-specific evidence that the Stop hook checks in
addition to companion tests. The session-audit script composes the
base check with one fragment per active type — modes are independent
per type, so you can run TDD in and a stricter type in .
agile-tddtemplates/project-types/<type>/warnblock除了基础的配套测试规则,还在下提供项目类型模板。每个模板会启用项目特定的验证规则,Stop钩子除了检查配套测试外还会检查这些规则。会话审计脚本会将基础检查与每个激活类型的片段组合——每种类型的模式独立,因此可以在模式下运行TDD,同时在模式下运行更严格的类型规则。
agile-tddtemplates/project-types/<type>/warnblockAvailable types
可用类型
| Type | Detects | Enforces (in addition to companion tests) |
|---|---|---|
| | |
| (planejado) | service worker rebuilt + offline smoke test |
| (planejado) | platform build green + simulator screenshot |
| (planejado) electron/forge config | electron-builder validate + window screenshot |
Active types are listed in . At
install, each runs against
the repo; types that succeed get appended to the config along with
their block.
.tdd-guardrails.yml → project_typestemplates/project-types/<type>/detect.shguardrails.partial.yml| 类型 | 检测依据 | 额外强制执行规则(除配套测试外) |
|---|---|---|
| | |
| (规划中) | 服务工作者已重建 + 离线冒烟测试通过 |
| (规划中) | 平台构建成功 + 模拟器截图 |
| (规划中)electron/forge配置 | electron-builder验证通过 + 窗口截图 |
激活类型列在中。安装时,每个会针对仓库运行;检测成功的类型会被追加到配置中,并附带其块。
.tdd-guardrails.yml → project_typestemplates/project-types/<type>/detect.shguardrails.partial.ymlAnatomy of a type
类型结构
A type is a folder with:
- — exits 0 when the type applies to the repo.
detect.sh - — block appended to
guardrails.partial.yml..tdd-guardrails.yml - — section appended to
agents-block.partial.md/CLAUDE.mdinsideAGENTS.mdmarkers.<!-- agile-tdd:<type>:start --> - — sourced by
audit.partial.sh. Exportstdd-session-audit.shreturning textual violations. Setscheck_<type>_evidence "$ROOT"(uppercase) so the framework can decide block vs warn.<TYPE>_MODE - — optional extra hooks the type registers (e.g. PostToolUse evidence recorder).
hooks/*.sh.tmpl - — human description of the type.
README.md
The framework calls helpers from for
yml parsing, glob matching, app-root discovery, and freshness checks.
Adding a new type does not require touching the framework script.
templates/lib/audit-helpers.sh一个类型是包含以下文件的文件夹:
- ——当类型适用于仓库时返回0。
detect.sh - ——追加到
guardrails.partial.yml的配置块。.tdd-guardrails.yml - ——追加到
agents-block.partial.md/CLAUDE.md的部分内容,位于AGENTS.md标记之间。<!-- agile-tdd:<type>:start --> - ——由
audit.partial.sh加载。导出tdd-session-audit.sh函数,返回文本形式的违规信息。设置check_<type>_evidence "$ROOT"(大写),以便框架决定是block还是warn模式。<TYPE>_MODE - ——可选的额外钩子,由类型注册(如PostToolUse证据记录器)。
hooks/*.sh.tmpl - ——类型的人工描述。
README.md
框架从调用工具函数进行yml解析、glob匹配、应用根目录发现和新鲜度检查。添加新类型无需修改框架脚本。
templates/lib/audit-helpers.shInstalling types
安装类型
The base install already follows the "Manual install" steps
above. For each detected type:
agile-tdd- Append to
templates/project-types/<type>/guardrails.partial.yml(or merge if the block already exists)..tdd-guardrails.yml - Append to
agents-block.partial.md/CLAUDE.mdbetween the marker pair.AGENTS.md - Copy to
hooks/*.sh.tmpl,.claude/hooks/, and.codex/hooks/, dropping the.opencode/hooks/suffix and.tmpl-ing.chmod +x - Add the type's matcher to each harness config:
- Claude Code (→
.claude/settings.json): add a matcher entry for the type's PostToolUse hooks. ForPostToolUse:tauricallingmcp__tauri__webview_screenshot|mcp__tauri__webview_execute_js|mcp__tauri__webview_dom_snapshot|mcp__tauri__manage_window.$CLAUDE_PROJECT_DIR/.claude/hooks/tdd-record-mcp.sh - Codex (): same matcher pattern; command uses
.codex/hooks.json.bash "$(git rev-parse --show-toplevel)/.codex/hooks/tdd-record-mcp.sh" - OpenCode: extend to subscribe to
.opencode/plugins/tdd-guardrails.jswith a filter on tool name, spawning the same shell script viatool.execute.after.node:child_process.spawn
- Claude Code (
- Append the type slug (e.g. ) to
tauriinproject_types. The session-audit reads this list at run..tdd-guardrails.yml
基础安装已遵循上述「手动安装」步骤。对于每个检测到的类型:
agile-tdd- 将追加到
templates/project-types/<type>/guardrails.partial.yml(如果块已存在则合并)。.tdd-guardrails.yml - 将追加到
agents-block.partial.md/CLAUDE.md的标记对之间。AGENTS.md - 将复制到
hooks/*.sh.tmpl、.claude/hooks/和.codex/hooks/,移除.opencode/hooks/后缀并执行.tmpl赋予执行权限。chmod +x - 在每个工具配置中添加类型的匹配器:
- Claude Code(→
.claude/settings.json):添加类型PostToolUse钩子的匹配器条目。对于PostToolUse:tauri,调用mcp__tauri__webview_screenshot|mcp__tauri__webview_execute_js|mcp__tauri__webview_dom_snapshot|mcp__tauri__manage_window。$CLAUDE_PROJECT_DIR/.claude/hooks/tdd-record-mcp.sh - Codex():使用相同的匹配模式;命令格式为
.codex/hooks.json。bash "$(git rev-parse --show-toplevel)/.codex/hooks/tdd-record-mcp.sh" - OpenCode:扩展以订阅
.opencode/plugins/tdd-guardrails.js并按工具名称过滤,通过tool.execute.after调用相同的shell脚本。node:child_process.spawn
- Claude Code(
- 将类型标识(如)追加到
tauri的.tdd-guardrails.yml列表中。会话审计脚本会在运行时读取此列表。project_types
Tauri MCP validation (project type)
Tauri MCP验证(项目类型)
Knowhow consolidated from real Tauri debug/test sessions. Read this
before touching or any
in a Tauri-detected repo.
src-tauri/src/**src/{routes,components,hooks}/**整合自真实Tauri调试/测试会话的经验。在Tauri检测到的仓库中修改或前,请阅读以下内容。
src-tauri/src/**src/{routes,components,hooks}/**Toolbox (canonical references)
工具库(权威参考)
- — always invoke first; subsequent webview tools assume an active session.
mcp__tauri__driver_session(start) - — the canonical "I opened the screen" signal. Pass
mcp__tauri__webview_screenshot(Tauri apps usually havewindowId="main"+main).tray-panel - — for invokes that take longer than the JS executor timeout (≈seconds), fire-and-forget via
mcp__tauri__webview_execute_js; poll DB or screenshot instead ofwindow.__TAURI_INTERNALS__.invoke(cmd, args).then(r => window.__last = r)-ing.await - /
mcp__tauri__webview_interact/ refs inwebview_find_element— these depend onwebview_dom_snapshotwhich is undefined after dev-server HMR reload. Preferwindow.__MCP__.resolveRefviadocument.querySelector(...).click().webview_execute_js
- ——始终先调用此工具;后续webview工具假设存在活跃会话。
mcp__tauri__driver_session(start) - ——标准的「已打开页面」信号。传递
mcp__tauri__webview_screenshot(Tauri应用通常有windowId="main"+main窗口)。tray-panel - ——对于执行时间超过JS执行器超时(约数秒)的调用,通过
mcp__tauri__webview_execute_js实现即发即弃;轮询数据库或截图而非使用window.__TAURI_INTERNALS__.invoke(cmd, args).then(r => window.__last = r)。await - /
mcp__tauri__webview_interact/webview_find_element中的引用——这些依赖webview_dom_snapshot,而该对象在开发服务器HMR重载后未定义。优先通过window.__MCP__.resolveRef执行webview_execute_js。document.querySelector(...).click()
Rebuild flow
重建流程
- Rust change → forces
touch src-tauri/src/<file.rs>to rebuild (cargo's mtime watcher).tauri-dev - Monitor the dev process output for +
Finishedbefore re-running MCP calls; HMR only covers the frontend.MCP Bridge plugin initialized - In Claude Code, prefer with the filter
Monitor.tail -f tauri.log | grep --line-buffered -E "Finished|error\\[|MCP Bridge plugin initialized"
- Rust代码变更→强制
touch src-tauri/src/<file.rs>重建(cargo的修改时间监视器)。tauri-dev - 在重新运行MCP调用前,监控开发进程输出是否出现+
Finished;HMR仅覆盖前端。MCP Bridge plugin initialized - 在Claude Code中,优先使用工具并设置过滤条件
Monitor。tail -f tauri.log | grep --line-buffered -E "Finished|error\\[|MCP Bridge plugin initialized"
Validation patterns
验证模式
- DB direct read — cheaper than for state assertions:
invoke().sqlite3 <workspace>/<app>.db "SELECT ..." - GPU offload check — . Apps using Metal/CUDA show low CPU (5-15%) when the GPU is doing the work; CPU-only fallback shows 200-400%.
ps -p $(pgrep -x <app-name>) -o pcpu,etime - Visual check — via
location.assign('/route/...'), wait ~500ms, thenwebview_execute_js.webview_screenshot
- 直接读取数据库——比更高效的状态断言方式:
invoke()。sqlite3 <workspace>/<app>.db "SELECT ..." - GPU卸载检查——。使用Metal/CUDA的应用在GPU工作时CPU占用率低(5-15%);仅使用CPU的回退方案CPU占用率为200-400%。
ps -p $(pgrep -x <app-name>) -o pcpu,etime - 视觉检查——通过执行
webview_execute_js,等待约500ms,然后调用location.assign('/route/...')。webview_screenshot
Known gotchas (cross-project patterns)
已知陷阱(跨项目模式)
Each project keeps its own list of in-tree fixes (e.g. in a
for projects that follow the LLM
wiki pattern). The items below describe the patterns — the actual
file paths vary per project.
wiki/technical/tauri-gotchas.md-
whisper-rs 0.14.xtype confusion — in v0.14.4 the trampoline is instantiated with the original closure type, while
set_abort_callback_safeis actually written asuser_data. The callback dereferences the wrong memory layout and returns garbage bools, aborting whisper at 0–5% with errorBox<dyn FnMut() -> bool>. Workaround: use the-6variantunsafe+set_abort_callbackwith a hand-written trampoline that matches the stored type:set_abort_callback_user_datarustunsafe extern "C" fn abort_trampoline(ud: *mut std::ffi::c_void) -> bool { let f = &mut *(ud as *mut Box<dyn FnMut() -> bool>); f() } let closure: Box<dyn FnMut() -> bool> = Box::new({ let cancel = cancel.clone(); move || cancel.load(Ordering::Relaxed) }); let ud = Box::into_raw(Box::new(closure)) as *mut std::ffi::c_void; unsafe { params.set_abort_callback(Some(abort_trampoline)); params.set_abort_callback_user_data(ud); } -
Background jobs stuckafter binary kill — a tokio task that dies mid-flight (cargo restart, app crash) never updates its DB row to a terminal state. Add a reconcile pass at pool-open:
runningrust// db::open_pool, after schema migration sqlx::query( "UPDATE jobs SET status='cancelled', error_message=COALESCE(error_message,'interrupted by app restart'), finished_at=? WHERE status IN ('running','pending')" ).bind(now_iso()).execute(&pool).await?; -
Modal libraries that coupleto data state (e.g. Base UI Dialog, some Radix patterns) — when the data prop goes
openin the same render thatnullflips false, the modal unmounts before the close animation finishes; pointer-events stay trapped onopenand the page becomes unclickable. Decoupledocument.body(boolean) from the data, then defer the data clear withopen(or use ansetTimeout(..., 200)callback when the library exposes one).onCloseComplete -
is a no-op on existing tables — reused workspaces / DBs do NOT pick up new columns added in the schema file. Symptom: runtime
CREATE TABLE IF NOT EXISTSafter upgrade. Fix options: explicitno such columnper drift; OR, if the DB has no production data, drop and re-bootstrap.ALTER TABLE … ADD COLUMN IF NOT EXISTS -
only decodes WAV — for
hound::WavReader/.mp3/.mp4/.m4a, pipe through ffmpeg to.webmbefore feeding Whisper:f32le @ 16 kHz monorust// ffmpeg -hide_banner -loglevel error -nostdin -i <input> // -ac 1 -ar 16000 -f f32le - let mut child = Command::new(ffmpeg) .args(["-hide_banner","-loglevel","error","-nostdin","-i"]) .arg(input) .args(["-ac","1","-ar","16000","-f","f32le","-"]) .stdout(Stdio::piped()).spawn()?; // read stdout, reinterpret bytes as &[f32]
每个项目都有自己的内部修复列表(例如,遵循LLM wiki模式的项目会存放在中)。以下描述的是模式——实际文件路径因项目而异。
wiki/technical/tauri-gotchas.md-
whisper-rs 0.14.x类型混淆——在v0.14.4中,蹦床使用原始闭包类型实例化,而
set_abort_callback_safe实际存储为user_data。回调会错误地解引用内存布局并返回无效布尔值,导致whisper在0–5%时中止并返回错误Box<dyn FnMut() -> bool>。解决方法:使用-6变体unsafe+set_abort_callback,并手写与存储类型匹配的蹦床:set_abort_callback_user_datarustunsafe extern "C" fn abort_trampoline(ud: *mut std::ffi::c_void) -> bool { let f = &mut *(ud as *mut Box<dyn FnMut() -> bool>); f() } let closure: Box<dyn FnMut() -> bool> = Box::new({ let cancel = cancel.clone(); move || cancel.load(Ordering::Relaxed) }); let ud = Box::into_raw(Box::new(closure)) as *mut std::ffi::c_void; unsafe { params.set_abort_callback(Some(abort_trampoline)); params.set_abort_callback_user_data(ud); } -
二进制终止后后台任务仍处于状态——中途终止的tokio任务(cargo重启、应用崩溃)永远不会将其数据库行更新为终端状态。在连接池打开时添加协调步骤:
runningrust// db::open_pool,在 schema 迁移后 sqlx::query( "UPDATE jobs SET status='cancelled', error_message=COALESCE(error_message,'interrupted by app restart'), finished_at=? WHERE status IN ('running','pending')" ).bind(now_iso()).execute(&pool).await?; -
将与数据状态耦合的模态库(如Base UI Dialog、部分Radix模式)——当数据属性在
open变为false的同一渲染周期中变为open时,模态框会在关闭动画完成前卸载;指针事件仍被null捕获,页面变得无法点击。将document.body(布尔值)与数据解耦,然后使用open延迟清除数据(或使用库提供的setTimeout(..., 200)回调)。onCloseComplete -
对现有表无作用——复用的工作区/数据库不会拾取模式文件中新增的列。症状:升级后运行时出现
CREATE TABLE IF NOT EXISTS错误。修复选项:对每个变更使用显式的no such column;或者,如果数据库没有生产数据,删除并重新初始化。ALTER TABLE … ADD COLUMN IF NOT EXISTS -
仅解码WAV格式——对于
hound::WavReader/.mp3/.mp4/.m4a格式,先通过ffmpeg转换为.webm格式,再输入给Whisper:f32le @ 16 kHz monorust// ffmpeg -hide_banner -loglevel error -nostdin -i <input> // -ac 1 -ar 16000 -f f32le - let mut child = Command::new(ffmpeg) .args(["-hide_banner","-loglevel","error","-nostdin","-i"]) .arg(input) .args(["-ac","1","-ar","16000","-f","f32le","-"]) .stdout(Stdio::piped()).spawn()?; // 读取stdout,将字节重新解释为&[f32]
Definition of Done (Tauri)
完成定义(Tauri)
For each change that touches in every affected
app (monorepo: per-app):
affected_paths- green.
cargo check --manifest-path <app>/src-tauri/Cargo.toml --lib - (or
bun run typecheck) green.tsc --noEmit - showed
tauri-devafter the lastFinishedintouch.<app>/src-tauri/src/** - Companion test exists (base TDD rule, if applicable).
- ≥1 call after the last edit under
mcp__tauri__webview_screenshot.<app>/{src-tauri,src}/** - Post-operation state confirmed: DB query, DOM snapshot, or evidence visible in the screenshot.
The Stop hook checks (1), (2), and (5)
automatically via the template. Steps (3), (4), and (6) remain
agent responsibility.
tdd-session-audit.shtauri对于每个修改每个受影响应用(单体仓库:按应用)中的变更:
affected_paths- 执行成功。
cargo check --manifest-path <app>/src-tauri/Cargo.toml --lib - (或
bun run typecheck)执行成功。tsc --noEmit - 在最后一次修改后,
<app>/src-tauri/src/**显示tauri-dev。Finished - 存在配套测试(基础TDD规则,如适用)。
- **最后一次修改后至少有1次
<app>/{src-tauri,src}/**调用。mcp__tauri__webview_screenshot - 确认操作后状态:数据库查询、DOM快照或截图中可见的证据。
tdd-session-audit.shtauriRelationship with the flow
与流程的关系
mermaid
flowchart LR
A["/agile-story"] --> B[TDD cycle]
B --> C[Red: failing test]
C --> D[Green: minimum code]
D --> E[Refactor]
E --> F{More?}
F -->|Yes| C
F -->|No| G["/agile-refinement"]This skill operates during execution. It pairs with (which defines what to build) and feeds into (which validates the result). When the optional enforcement is installed, the rule is also applied automatically at every tool call.
/agile-story/agile-refinementWrite/Edit/MultiEditmermaid
flowchart LR
A["/agile-story"] --> B[TDD cycle]
B --> C[Red: failing test]
C --> D[Green: minimum code]
D --> E[Refactor]
E --> F{More?}
F -->|Yes| C
F -->|No| G["/agile-refinement"]本技能在执行阶段运作。它与(定义要构建的内容)配合,并为(验证结果)提供输入。当启用可选的强制执行时,规则也会在每次工具调用时自动应用。
/agile-story/agile-refinementWrite/Edit/MultiEdit