Files
claude-skills/dist-hermes/software-development/tdd-criteria/SKILL.md
vitya 94b4c441e6 fix(tdd-criteria): review findings — v0.1.0→v0.2.0
- Remove session-authorship trigger loophole (applies to ALL code changes)
- Add composite-tasks paragraph (criterion applies per artefact, not per task)
- Add refactoring carve-out (existing passing tests sufficient, no new tests)
- Expand file-extension list (go, rs, java, rb, ex, swift, kt, cs, php)
- Clarify wrapper line-count (non-blank non-comment lines)
- Add spike-survivor fallback (TODO/GitHub issue if no .tasks/)
- Fix "чужая schema" → "foreign schema" in algorithm + design doc
- Define TDD procedurally (red→green→refactor) in Default mode section
- Sync design doc with same changes
- Rebuild dist + dist-hermes + reinstall

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 10:12:59 +03:00

6.4 KiB
Raw Blame History

name, version, description
name version description
tdd-criteria 0.2.0 TDD by default with four bright-line carve-outs. Applies before any code change. Triggers: "TDD", "test-driven", "следуй TDD", "use TDD", "should I write tests", "skip tdd", "[skip-tdd: ...]", "[test-modify: ...]", "tdd-criteria". Cross-agent policy — no tool refs. Full rationale: .wiki/concepts/tdd-criteria-design.md

tdd-criteria

TDD by default. Skip only with a bright-line marker. Tests defend code from silent deletion; rule 4 defends tests from silent rewriting.

When this runs

At session start — when CLAUDE.md contains the line follow tdd-criteria.

Before any code change — touching a *.ts, *.js, *.py, *.go, *.rs, *.java, *.rb, *.ex, *.swift, *.kt, *.cs, *.php, or similar source file triggers the decision algorithm below.

On explicit reference — when the user says "TDD", "test-driven", "следуй TDD", "use TDD", "should I write tests", "skip tdd", "tdd-criteria", or includes [skip-tdd: ...] or [test-modify: ...] in a commit subject.

Default mode

TDD by default: write a failing test first, then write the minimum code to make it pass, then refactor. Skip only if one of four bright-line carve-outs matches and is marked in the commit subject.

Decision algorithm (8 questions, top-down)

Walk through in order. First «yes» determines mode. All «no» → TDD by default.

  1. Bug fix? → TDD (red-test first)
  2. Consuming external contract (SDK, REST API, foreign schema)? → TDD (contract-test)
  3. Security / auth / money / identifiers? → TDD
  4. Pure logic — function (input → output) without I/O, global state, bounded inputs? → TDD
  5. Visual / config — CSS, layout, design tokens, .env.example, prompts, wiki, README? → SKIP, [skip-tdd: visual]
  6. Spike — explicit POC «throwaway» in commit/PR/task subject? → SKIP, [skip-tdd: spike] + spike-survivor task if code survives
  7. One-shot — migration, ETL backfill, ad-hoc cleanup, runs once? → SKIP, [skip-tdd: oneshot]
  8. Transit wrapper ≤10 non-blank non-comment lines — no branching, re-export / glue? → SKIP, [skip-tdd: wrapper]

(default) → TDD

Composite tasks. A task that doesn't fit one category is composite — break it down per artefact type. The criterion applies per artefact, not per task. Example: a settings page = CSS layout [skip-tdd: visual] + validation logic [TDD] + API wrapper [TDD].

Refactoring — restructuring code without changing observable behaviour, with existing tests covering it — does not require new tests. Existing tests must still pass. If refactoring introduces new behaviour, that part is subject to the decision algorithm as a separate artefact.

Ironclad rules (TDD obligatory)

  1. Bug fix — if there's an issue / failure log / repro, write a red-test codifying the repro. Marginal cost: 5 min. Marginal benefit: regression test forever.
  2. Pure logic, bounded inputs — no fixtures, no mocks, no setup. Test = input/output pair. Skipping is gratuitous.
  3. Third-party contract — SDK bumps change signatures silently. Contract-test pins known input → known shape. Catches break at first install, not days later.
  4. Security / auth / money / IDs — asymmetric blast radius: false positive ≪ false negative. TDD = insurance.

In all four, recovery cost from silent deletion is high. The test is the only artefact that makes deletion visible.

Permissive carve-outs (skip + marker required)

# Category Trigger Marker
5 Visual / config CSS, layout, tokens, .env.example, prompts, wiki [skip-tdd: visual]
6 Spike «POC, throwaway» in commit/PR/task subject [skip-tdd: spike]
7 One-shot Migration, ETL, ad-hoc cleanup; runs once [skip-tdd: oneshot]
8 Wrapper ≤10 non-blank non-comment lines, no branching, re-export / glue [skip-tdd: wrapper]

These are accepted-risk zones — you accept that an agent can vandalise without immediate signal, because recovery is cheap (eyeball next render; throwaway by contract; runs once; reconstruct ≤ delete).

Anti-loophole

  1. Skip without category is invalid. One of four explicit categories required — not «other reasons». No marker = violation.
  2. Spike survivor rule. Merged spike → same merge-commit creates [backfill-tests-<slug>] task (in .tasks/ if available, otherwise a TODO comment or GitHub issue). Otherwise «spike» becomes «skipped tests forever».
  3. Friction is the point. [skip-tdd: visual] 50× in a design-system rework is irritating — that's the fence. Re-evaluate after ≥2 weeks, not before.
  4. Tests are append-only by default. Modifying an assertion, deleting a test, or disabling it (it.skip/xit/@pytest.mark.skip/@Disabled) requires:
    • Marker in commit subject: [test-modify: <test-name>: was <X>; is <Y>; reason: <Z>]<X> and <Y> are literal assertion expressions, not paraphrased. Example: [test-modify: validates email: was expect(isValid("a@b")).toBe(true); is expect(isValid("a@b.com")).toBe(true); reason: tightened spec to require TLD]
    • Separate commit from impl changes. A commit must not modify both *.test.* and src/* files (or project-equivalents). git log --grep '\[test-modify' must show a clean test-only audit trail.
    • Why literal was/is: an agent forced to write the literal assertion publishes exactly what they're rewriting. «Updated to match new behaviour» hides everything — agents will use that whenever allowed.

Cross-agent applicability

Pure policy — no agent-specific tool references in this body. Works on Claude, Gemini, Copilot, or any future agent. Hermes mapping: mode: auto, category: software-development, no replace-rules.

Out of scope

  • Does not enforce via git hooks (separate optional task: tdd-criteria-precommit-hook).
  • Does not modify project CLAUDE.md (that's project-bootstrap's job).
  • Does not run tests.
  • Does not apply rule 4 retroactively to tests written before the rule was adopted.

Why this exists

Tests make behaviour an invariant; without them, code is an artefact silent-deletable by agents. AND: the test itself must be defended too (rule 4) — otherwise the contract collapses back into an artefact when the agent rewrites the failing test. Full rationale: .wiki/concepts/tdd-criteria-design.md.