diff --git a/.tasks/STATUS.md b/.tasks/STATUS.md index 1b5a47c..2addafa 100644 --- a/.tasks/STATUS.md +++ b/.tasks/STATUS.md @@ -1,5 +1,5 @@ # Task Board -_Updated: 2026-05-07 (board cleanup: 4 stale → done, hermes-converter-ci unblocked; active: none)_ +_Updated: 2026-05-07 (tdd-criteria-review done → v0.2.0)_ + --- diff --git a/.wiki/concepts/tdd-criteria-design.md b/.wiki/concepts/tdd-criteria-design.md index fca4c02..5bdecf7 100644 --- a/.wiki/concepts/tdd-criteria-design.md +++ b/.wiki/concepts/tdd-criteria-design.md @@ -4,7 +4,7 @@ source: .meeting-room/.archive/2026-05-07-tdd-criteria.md status: promoted type: design title: tdd-criteria-design -amended: "2026-05-07: added test-immutability defence (Anti-loophole rule 4) after user noted symmetric vandalism risk on tests" +amended: "2026-05-07: added test-immutability defence (Anti-loophole rule 4) after user noted symmetric vandalism risk on tests; 2026-05-07 v0.2.0 review: removed session-authorship trigger loophole, added composite-tasks/refactoring sections, expanded file-extension list, clarified wrapper line-count" ingested_at: '2026-05-07T04:01:23.616Z' ingested_by: OpeItcLoc03@DESKTOP-NSEF0UK source_project: .meeting-room @@ -55,7 +55,7 @@ Walk through 8 questions top-to-bottom. First «yes» determines mode. All «no ``` 1. Это исправление бага? → TDD (red-test первым) 2. Это код, потребляющий внешний контракт → TDD (contract-test) - (SDK, REST API, чужая schema)? + (SDK, REST API, foreign schema)? 3. Это security / auth / money / identifiers? → TDD 4. Это pure logic — функция (input → output) → TDD без I/O, без global state, bounded inputs? @@ -73,6 +73,10 @@ Walk through 8 questions top-to-bottom. First «yes» determines mode. All «no (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. + +**Refactoring.** Restructuring existing code without changing observable behaviour, where existing tests already cover it, does not require new tests. If the refactoring introduces new behaviour, that part is a separate artefact subject to the decision algorithm. + ## Ironclad — why TDD is cheaper than skipping | # | Rule | Checkable property | Why TDD here | @@ -93,7 +97,7 @@ Not «TDD doesn't apply». **«You accept that an agent can vandalise this witho | 5 | Visual / config | CSS, layout, design tokens, `.env.example`, prompts, wiki, README | `[skip-tdd: visual]` | Eyeball on next render. Visual regression infra exists (Playwright screenshots, Percy) but heavyweight for most projects. | | 6 | Spike | Explicit POC «throwaway» in commit/PR/task subject | `[skip-tdd: spike]` | Throwaway by contract — deletion isn't a problem. **Survivor rule**: if spike code reaches master, the same merge-commit creates `[backfill-tests-]` task. Otherwise this category becomes the loophole. | | 7 | One-shot | Migrations, ETL backfill, ad-hoc cleanup; runs once | `[skip-tdd: oneshot]` | Test never re-executes — cost not recovered. After run, deletion is irrelevant. | -| 8 | Wrapper | ≤10 lines, no branching (re-export, glue) | `[skip-tdd: wrapper]` | Test on `function foo(x) { return bar(x) }` re-states `bar`. Reconstruct cost ≈ delete cost. The defence is on `bar`, not `foo`. | +| 8 | Wrapper | ≤10 non-blank non-comment lines, no branching (re-export, glue) | `[skip-tdd: wrapper]` | Test on `function foo(x) { return bar(x) }` re-states `bar`. Reconstruct cost ≈ delete cost. The defence is on `bar`, not `foo`. | ## Anti-loophole @@ -149,7 +153,7 @@ Example: «add a user-profile-settings page»: - Validation form (email format, password strength) → Ironclad-4 (security) → TDD - API call wrapper for save → Ironclad-3 (third-party contract if PUT to external endpoint) → TDD - Update Pinia store reducer → Ironclad-2 (pure logic if bounded reducer) → TDD -- Hook `useProfileForm` composing the above → wrapper if ≤10 lines glue, else Ironclad-2 +- Hook `useProfileForm` composing the above → wrapper if ≤10 non-blank non-comment lines glue, else Ironclad-2 One «task» yields 4-5 commits with different modes. **The criterion applies per artefact, not per task.** This is the point — no «overall this is exploratory». diff --git a/.wiki/log.md b/.wiki/log.md index 63accb0..2ddcd23 100644 --- a/.wiki/log.md +++ b/.wiki/log.md @@ -53,3 +53,5 @@ Parseable: `grep "^## \[" .wiki/log.md | tail -20`. ## [2026-05-06] ingest | concepts/hermes-skills-rollout-design ## [2026-05-07] ingest | concepts/tdd-criteria-design + +## [2026-05-07] review | tdd-criteria v0.2.0 — 4 findings applied: trigger-loophole fix (removed session-authorship clause), composite-tasks + refactoring sections, expanded file-extension list, clarified wrapper line-count + spike-survivor fallback + foreign-schema fix; design doc synced diff --git a/dist-hermes/software-development/tdd-criteria/SKILL.md b/dist-hermes/software-development/tdd-criteria/SKILL.md index 5f4e5c9..57ea071 100644 --- a/dist-hermes/software-development/tdd-criteria/SKILL.md +++ b/dist-hermes/software-development/tdd-criteria/SKILL.md @@ -1,13 +1,12 @@ --- name: tdd-criteria -version: 0.1.0 +version: 0.2.0 description: > TDD by default with four bright-line carve-outs. Applies before any code - change the agent didn't author this session. 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 + 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 @@ -18,29 +17,33 @@ description: > **At session start** — when `CLAUDE.md` contains the line `follow tdd-criteria`. -**Before any code change** the agent didn't author this session — touching a `*.ts`, `*.js`, `*.py`, or similar source file triggers the decision algorithm below. +**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. Skip only if one of four bright-line carve-outs matches and is marked in the commit subject. +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, чужая schema)? → TDD (contract-test) +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 lines — no branching, re-export / glue? → SKIP, `[skip-tdd: wrapper]` +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. @@ -57,14 +60,14 @@ In all four, recovery cost from silent deletion is high. The test is the only ar | 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 lines, no branching, re-export / glue | `[skip-tdd: wrapper]` | +| 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-]` task. Otherwise «spike» becomes «skipped tests forever». +2. **Spike survivor rule.** Merged spike → same merge-commit creates `[backfill-tests-]` 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: : was ; is ; reason: ]` — `` and `` 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]` diff --git a/dist/tdd-criteria.skill b/dist/tdd-criteria.skill index 32bf4c7..571831d 100644 Binary files a/dist/tdd-criteria.skill and b/dist/tdd-criteria.skill differ diff --git a/skills/tdd-criteria/SKILL.md b/skills/tdd-criteria/SKILL.md index 5f4e5c9..57ea071 100644 --- a/skills/tdd-criteria/SKILL.md +++ b/skills/tdd-criteria/SKILL.md @@ -1,13 +1,12 @@ --- name: tdd-criteria -version: 0.1.0 +version: 0.2.0 description: > TDD by default with four bright-line carve-outs. Applies before any code - change the agent didn't author this session. 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 + 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 @@ -18,29 +17,33 @@ description: > **At session start** — when `CLAUDE.md` contains the line `follow tdd-criteria`. -**Before any code change** the agent didn't author this session — touching a `*.ts`, `*.js`, `*.py`, or similar source file triggers the decision algorithm below. +**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. Skip only if one of four bright-line carve-outs matches and is marked in the commit subject. +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, чужая schema)? → TDD (contract-test) +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 lines — no branching, re-export / glue? → SKIP, `[skip-tdd: wrapper]` +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. @@ -57,14 +60,14 @@ In all four, recovery cost from silent deletion is high. The test is the only ar | 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 lines, no branching, re-export / glue | `[skip-tdd: wrapper]` | +| 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-]` task. Otherwise «spike» becomes «skipped tests forever». +2. **Spike survivor rule.** Merged spike → same merge-commit creates `[backfill-tests-]` 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: : was ; is ; reason: ]` — `` and `` 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]`