test-driven-development
Use when implementing any feature or bugfix, before writing implementation code
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Use when implementing any feature or bugfix, before writing implementation code
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Keeps skill definitions aligned between Claude Code and Codex. Use when creating, migrating, renaming, or updating skills that must work across `.claude/skills` and `.agents/skills`, or when auditing drift between the two systems.
When to open a pull request in the DorkOS repo (review the pushed branch first, open the PR after it converges) and how the automated review behaves, including its controls (skip-review, review:light/deep, re-review). Use when finishing a branch, opening a PR, deciding how much review a PR should get, or requesting a re-review after addressing feedback.
Decides when agent work needs an isolated git worktree and how to create, enter, and clean one up safely. Use when starting code changes in a checkout that may be shared with another agent, dispatching a Linear task, executing a spec, or running any parallel work that mutates tracked files.
Writes non-release DorkOS blog posts - feature posts, decision essays, ecosystem posts, and release recaps - from the repo's own ADRs, specs, research reports, and changelog fragments. Use when drafting, researching, or reviewing anything in blog/ that is not a version release note.
Guides writing concise, effective Architecture Decision Records. Use when creating ADRs, extracting decisions from specs, or reviewing ADR quality.
Patterns for adding, renaming, removing, or retyping fields in DorkOS user config. Use when editing UserConfigSchema, MarketplacesFileSchema, or any conf-backed store — walks the Zod field → defaults → conf migration → docs → test lifecycle end-to-end.
| name | test-driven-development |
| description | Use when implementing any feature or bugfix, before writing implementation code |
Write the test first. Watch it fail. Write minimal code to pass.
Core principle: If you didn't watch the test fail, you don't know if it tests the right thing.
Violating the letter of the rules is violating the spirit of the rules.
Always: new features, bug fixes, refactoring, behavior changes.
Exceptions (ask the user): throwaway prototypes, generated code, configuration files.
Thinking "skip TDD just this once"? Stop. That's rationalization.
NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST
Write code before the test? Delete it. Start over. Don't keep it as "reference", don't "adapt" it while writing tests — delete means delete. Implement fresh from tests.
One minimal test showing what should happen: one behavior, a name that describes it, real code under test (mocks only if unavoidable — a test that only exercises vi.fn() chains tests the mock, not the code).
MANDATORY. Never skip.
pnpm vitest run path/to/test.test.ts
Confirm:
Test passes? You're testing existing behavior — fix the test. Test errors? Fix the error and re-run until it fails correctly.
Write the simplest code that passes the test. No extra options, no speculative parameters, no refactoring other code, no "improvements" beyond what the test demands (YAGNI).
MANDATORY.
pnpm vitest run path/to/test.test.ts
Confirm: the test passes, other tests still pass, output is pristine (no errors or warnings).
Test fails? Fix the code, not the test. Other tests fail? Fix now.
After green only: remove duplication, improve names, extract helpers. Keep tests green. Don't add behavior. Then repeat with the next failing test.
Tests written after code pass immediately — and passing immediately proves nothing: they're biased by your implementation, test what you built rather than what's required, and you never saw them catch anything. Test-first forces you to see the failure, which is the proof the test works. Manual testing doesn't substitute: no record, can't re-run, edge cases forgotten under pressure.
| Excuse | Reality |
|---|---|
| "Too simple to test" | Simple code breaks. Test takes 30 seconds. |
| "I'll test after" | Tests passing immediately prove nothing. |
| "Already manually tested" | Ad-hoc does not equal systematic. No record, can't re-run. |
| "Deleting X hours is wasteful" | Sunk cost fallacy. Keeping unverified code is technical debt. |
| "Keep as reference, write tests first" | You'll adapt it. That's testing after. Delete means delete. |
| "Need to explore first" | Fine. Throw away exploration, start with TDD. |
| "Test hard = design unclear" | Listen to the test. Hard to test = hard to use. |
| "TDD will slow me down" | TDD is faster than debugging. Pragmatic = test-first. |
| "Existing code has no tests" | You're improving it. Add tests for existing code. |
Red flags — stop and start over: code before test, test passes immediately, can't explain why the test failed, "just this once", "it's about spirit not ritual", "this is different because...".
Full conventions: .claude/rules/testing.md. The essentials:
__tests__/ directories alongside source (e.g. apps/server/src/services/__tests__/foo.test.ts)@vitest-environment jsdom directive and a mock Transport via TransportProvider + createMockTransport() from @dorkos/test-utilsFakeAgentRuntime from @dorkos/test-utils, never hand-rolled runtime mockscollectDurableEvents from @dorkos/test-utils (trigger the turn first — message POSTs are trigger-only 202s)pnpm vitest run path/to/test.test.ts # one file — the TDD inner loop
pnpm test -- --run # full suite (via turbo)
Gotcha: never run bare pnpm vitest run for a full run — in the dev environment it falsely fails 2 tests. Full runs go through pnpm test -- --run; bare pnpm vitest run is only for scoped paths.
Before marking work complete:
Can't check all boxes? You skipped TDD. Start over.
| Problem | Solution |
|---|---|
| Don't know how to test | Write wished-for API. Write assertion first. Ask the user. |
| Test too complicated | Design too complicated. Simplify interface. |
| Must mock everything | Code too coupled. Use dependency injection. |
| Test setup huge | Extract helpers. Still complex? Simplify design. |
Bug found? Write a failing test reproducing it, then follow the TDD cycle. The test proves the fix and prevents regression. Never fix bugs without a test.
When adding mocks or test utilities, read @testing-anti-patterns.md to avoid common pitfalls: testing mock behavior instead of real behavior, adding test-only methods to production classes, mocking without understanding dependencies.
Production code -> test exists and failed first
Otherwise -> not TDD
No exceptions without the user's permission.