From 94b4c441e645f37f6b36bd943b61a1138a69a6f3 Mon Sep 17 00:00:00 2001 From: vitya Date: Thu, 7 May 2026 10:12:17 +0300 Subject: [PATCH] =?UTF-8?q?fix(tdd-criteria):=20review=20findings=20?= =?UTF-8?q?=E2=80=94=20v0.1.0=E2=86=92v0.2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- .tasks/STATUS.md | 36 +++--------------- .wiki/concepts/tdd-criteria-design.md | 12 ++++-- .wiki/log.md | 2 + .../tdd-criteria/SKILL.md | 27 +++++++------ dist/tdd-criteria.skill | Bin 2862 -> 3155 bytes skills/tdd-criteria/SKILL.md | 27 +++++++------ 6 files changed, 46 insertions(+), 58 deletions(-) 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 32bf4c78abb1a82f2d93e12680c5096cb49b67a6..571831d4d35741c07c3e572aa915b6dba90f7d53 100644 GIT binary patch delta 3118 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 I3jhEB0AlC?-~a#s delta 2837 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 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]`