dev-tdd — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited dev-tdd (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
NO PRODUCTION CODE WITHOUT A FAILING TEST FIRSTIf code was written before the test: delete it. Start over with TDD.
Implement from scratch starting from the tests. Period.
┌─────────┐ ┌─────────┐ ┌──────────┐
│ RED │ ──▶ │ GREEN │ ──▶ │ REFACTOR │
│ Test │ │ Code │ │ Clean │
│ fail │ │ pass │ │ up │
└─────────┘ └─────────┘ └──────────┘
▲ │
└──────────────────────────────┘describe('Module', () => {
describe('function', () => {
it('should [behavior] when [condition]', () => {
// Arrange - Prepare
// Act - Execute
// Assert - Verify
});
});
});Good: Clear name, tests real behavior, one thing only
test('retries failed operations 3 times', async () => {
let attempts = 0;
const operation = () => {
attempts++;
if (attempts < 3) throw new Error('fail');
return 'success';
};
const result = await retryOperation(operation);
expect(result).toBe('success');
expect(attempts).toBe(3);
});Bad: Vague name, tests the mock instead of the code
test('retry works', async () => {
const mock = jest.fn()
.mockRejectedValueOnce(new Error())
.mockRejectedValueOnce(new Error())
.mockResolvedValueOnce('success');
await retryOperation(mock);
expect(mock).toHaveBeenCalledTimes(3);
});npm test path/to/test.test.tsConfirm:
Test passes immediately? You're testing existing behavior. Fix the test.
Test has a syntax error? Fix it, rerun until you get a proper failure.
Write the simplest code to pass the test. Nothing more.
Good: Just enough to pass
async function retryOperation<T>(fn: () => Promise<T>): Promise<T> {
for (let i = 0; i < 3; i++) {
try {
return await fn();
} catch (e) {
if (i === 2) throw e;
}
}
throw new Error('unreachable');
}Bad: Over-engineering, YAGNI
async function retryOperation<T>(
fn: () => Promise<T>,
options?: {
maxRetries?: number;
backoff?: 'linear' | 'exponential';
onRetry?: (attempt: number) => void;
}
): Promise<T> {
// Features not requested by a test
}Don't add features, refactor other code, or "improve" beyond the test.
npm test path/to/test.test.tsConfirm:
Test fails? Fix the code, not the test.
Other tests fail? Fix them now.
After GREEN only:
Keep tests green. Don't add behavior.
git commit -m "test(scope): add tests for [feature]"
git commit -m "feat(scope): implement [feature]"Then start over: next failing test for the next feature.
Tests written after the code pass immediately. Passing immediately proves nothing:
Test-first forces you to see the test fail, proving it tests something.
Manual testing is ad-hoc:
Automated tests are systematic. They run the same way every time.
Sunk cost fallacy. The time is already lost. The choice now:
The "waste" is keeping code you can't trust.
| Excuse | Reality |
|---|---|
| "Too simple to test" | Simple code breaks. The test takes 30 seconds. |
| "I'll write the tests after" | Tests that pass immediately prove nothing. |
| "Tests-after reach the same goal" | Tests-after = "what does it do?" Tests-first = "what should it do?" |
| "I already manually tested" | Ad-hoc ≠ systematic. No record, not replayable. |
| "Deleting X hours of work is a waste" | Sunk cost. Keeping unverified code = technical debt. |
| "I keep it as a reference and write the tests first" | You'll adapt it. It's disguised test-after. Delete = delete. |
| "I need to explore first" | OK. Throw away the exploration, start in TDD. |
| "It's hard to test = unclear design" | Listen to the test. Hard to test = hard to use. |
| "TDD will slow me down" | TDD faster than debugging. Pragmatic = test-first. |
| "Manual testing is faster" | Manual testing doesn't prove edge cases. You retest on every change. |
| "Existing code has no tests" | We're improving it. Add tests for the existing code. |
| "It's different because..." | No. No exception without explicit user permission. |
Stop immediately if you find yourself:
All these signals mean: delete the code. Start over in TDD.
| Quality | Good | Bad |
|---|---|---|
| Minimal | One thing only. "and" in the name? Split it. | test('validates email and domain and whitespace') |
| Clear | The name describes the behavior | test('test1') |
| Intentional | Demonstrates the desired API | Obscures what the code should do |
Bug: Empty email accepted
RED
test('rejects empty email', async () => {
const result = await submitForm({ email: '' });
expect(result.error).toBe('Email required');
});Verify RED
$ npm test
FAIL: expected 'Email required', got undefinedGREEN
function submitForm(data: FormData) {
if (!data.email?.trim()) {
return { error: 'Email required' };
}
// ...
}Verify GREEN
$ npm test
PASSREFACTOR: Extract the validation for other fields if necessary.
Before declaring the work done:
Can't check all boxes? TDD was skipped. Start over.
| Problem | Solution |
|---|---|
| Don't know how to test | Write the desired API. Write the assertion first. Ask the user. |
| Test too complicated | Design too complicated. Simplify the interface. |
| Everything must be mocked | Code too coupled. Use dependency injection. |
| Huge test setup | Extract helpers. Still complex? Simplify the design. |
# Run the tests
npm test
# Tests in watch mode
npm run test:watch
# With coverage
npm run test:coverage
# A specific file
npm test -- --grep "test name"TDD is test-first, but the same discipline applies when back-filling tests on code that already exists (legacy, coverage gaps).
null, undefined, "", [], {}, 0, -1, MAX_INT, empty/very long strings.Same rules as test-first: no mocks except external deps, tests independent and order-free.
When a project has no test setup yet:
go test (Go).__tests__; global setup + shared mocks.test, test:watch, test:ui, test:coverage, test:ci./ops:ops-ci).~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.