From b7819b175ef81c2f71586a051ac67d4e3bdb0646 Mon Sep 17 00:00:00 2001 From: OpeItcLoc03 Date: Thu, 7 May 2026 05:09:49 +0000 Subject: [PATCH] meta(tasks): update [tdd-criteria-skill-write] in claude-skills --- .tasks/STATUS.md | 102 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/.tasks/STATUS.md b/.tasks/STATUS.md index 267df01..9f2968a 100644 --- a/.tasks/STATUS.md +++ b/.tasks/STATUS.md @@ -290,7 +290,107 @@ version: 0.1.0 --- -## ⚪ [tdd-criteria-skill-write] — Write `~/projects/claude-skills/skills/tdd-criteria/SKILL.md` — runtime artefact for the TDD-criteria policy. Design rationale (the «why» behind every rule + the anti-vandalism leading argument) lives in `.wiki/concepts/tdd-criteria-design.md` (already promoted via `meeting-room-promote-brainstorm` 2026-05-07). This task writes only the **runtime SKILL.md** — short triggers + procedure, no rationale duplication. +## ⚪ [tdd-criteria-skill-write] — Write `~/projects/claude-skills/skills/tdd-criteria/SKILL.md` — runtime artefact for the TDD-criteria policy. Design rationale (the «why» behind every rule + the anti-vandalism leading argument + the test-immutability second-order defence) lives in `.wiki/concepts/tdd-criteria-design.md` (already promoted via `meeting-room-promote-brainstorm` 2026-05-07; NOTE: design doc was promoted at 2026-05-07T04:00 with 3 anti-loophole rules; **rule 4 (test-immutability) was added in this task description below at 2026-05-07T05:00 after user surfaced the symmetric vandalism risk; the impl session must mirror rule 4 into the design doc as well — see «Design doc amendment» section below**). + +This task writes the **runtime SKILL.md** + amends the design doc to include rule 4. + +**Frontmatter (YAML):** +- `name: tdd-criteria` +- `version: 0.1.0` +- `description: >` (multi-line) — must include trigger phrases the agent recognises: "TDD", "test-driven", "следуй TDD", "use TDD", "should I write tests", "skip tdd", "[skip-tdd: ...]", "[test-modify: ...]", and the bare topic name `tdd-criteria`. Also state cross-agent applicability and reference the design page. + +**Body sections (use `project-discipline` and `recommend-dont-menu` as structural templates):** +1. **`# tdd-criteria`** — one-line tag-line. +2. **`## When this runs`** — trigger phrases (session-start trigger via `follow tdd-criteria` line in CLAUDE.md, plus on-demand triggers); explicit «applies before any code touches a *.ts/*.js/*.py file the agent didn't author this session». +3. **`## Default mode`** — one sentence: «TDD by default. Skip only if one of four bright-line carve-outs matches and is marked in commit subject.» +4. **`## Decision algorithm (8 questions, top-down)`** — copy the algorithm block verbatim from design doc. +5. **`## Ironclad rules (TDD obligatory)`** — 4 rules, each ≤3 lines: trigger property + what test type. No rationale (rationale = design doc). +6. **`## Permissive carve-outs (skip + marker required)`** — 4 categories, each ≤2 lines: trigger + marker. +7. **`## Anti-loophole`** — **4 rules** (was 3 before the 2026-05-07 amendment): + - Rule 1: skip-without-category invalid + - Rule 2: spike-survivor (backfill-tests task on merge) + - Rule 3: friction is the point (don't relax before ≥2 weeks) + - **Rule 4 (NEW): tests are append-only by default.** Modifying assertion / deleting test / disabling (`it.skip`/`xit`/`@skip`/`@Disabled`) requires: + - **Marker in commit subject:** `[test-modify: : was ; is ; reason: ]` where `` and `` are the **literal assertion expressions** (not paraphrased). + - **Separate commit from impl changes:** a commit must not modify both `*.test.*` and `src/*` files (or project-equivalents). `git log --grep '\[test-modify'` must give a clean test-only audit trail. + - **Why literal `was/is`:** an agent forced to write the literal assertion publishes exactly what they're rewriting. Reasons like «updated to match new behaviour» hide vandalism — agents will use them whenever allowed. + - **Bright-line check** for an optional pre-commit hook (see follow-up task `tdd-criteria-precommit-hook`): diff includes removed `expect(...)` / changed assertion args / added `.skip`/`xit`/`@skip` / deleted test definition AND commit subject has no matching `[test-modify: ...]` → block. AND files include both test-pattern and impl-pattern → block (require split). + - Composite-task pattern (decompose by artefact) is example, not rule. +8. **`## Cross-agent applicability`** — pure policy, no Claude tool refs, Hermes-mappable as `mode: auto`. +9. **`## 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. +10. **`## Why this exists`** — one paragraph: «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 at `.wiki/concepts/tdd-criteria-design.md`.» + +**Constraints:** +- No `Read/Edit/Glob/Bash` references in body — keep it agent-agnostic. +- No code blocks with shell commands — pure policy doc. +- Length target: ≤220 lines (project-discipline ~140, this is denser due to rule 4). +- Frontmatter version starts at `0.1.0` (per Rule 3 of `project-discipline`: first edit of new versioned artefact = add 0.1.0, not bump). + +**Design doc amendment (do in same impl session):** + +After SKILL.md is written, also amend `~/projects/claude-skills/.wiki/concepts/tdd-criteria-design.md` to add rule 4 to the «Anti-loophole» section AND extend «The argument behind TDD-default» with a sub-section «The contract is only as strong as the contract itself». Full text below — paste verbatim, no rewriting: + +--- + +**ADD to «The argument behind TDD-default» section, after the existing paragraphs:** + +```markdown +### The contract is only as strong as the contract itself + +But there's a **second-order vandalism mode** that the bare contract argument doesn't cover: the agent doesn't delete the code, it rewrites the **test**. Test fails → agent changes the expected value, adds `.skip`, or deletes the test → test now passes → success reported. + +If the contract artefact (the test) is rewritable by the same agent that's failing to satisfy it, the invariant collapses back into an artefact. The defence requires **two layers**: + +1. **Code is defended by tests.** Ironclad rules 1-4 below. +2. **Tests are defended by process discipline.** Anti-loophole rule 4 below — append-only by default, modifications require literal-`was/is` marker in commit subject, test changes are separate commits from impl changes. + +Both layers are needed. Either alone leaves a path-of-least-resistance route to «success». +``` + +**ADD to «Anti-loophole» section (after rule 3):** + +```markdown +4. **Tests are append-only by default** (added 2026-05-07). New tests: free. **Modifying** an existing assertion, **deleting** a test, or **disabling** it (`it.skip`, `xit`, `@pytest.mark.skip`, `@Disabled`, etc.) requires both: + + **a)** A marker in commit subject: + `​`` + [test-modify: : was ; is ; reason: ] + `​`` + Where `` and `` are the **literal assertion expressions** before and after, not paraphrased. Example: + `​`` + [test-modify: validates email format: was expect(isValid("a@b")).toBe(true); is expect(isValid("a@b.com")).toBe(true); reason: tightened spec to require TLD] + `​`` + + **b)** Test changes go in a **separate commit** from any impl changes. A single commit must not modify both `*.test.*` and `src/*` files (or their project-equivalents). This forces an audit-able split — `git log --grep '\[test-modify'` shows every test rewrite cleanly. + + **Why literal `was/is`, not free-form reason:** an agent forced to write the literal assertion publishes exactly what they're rewriting. If `42` was the correct expectation and they changed it to `43` to make a buggy fix pass, the literal `was 42; is 43` line in `git log` identifies the culprit. A reason like «updated to match new behaviour» hides everything — agents will use it whenever it is allowed. + + **Bright-line check** (for an optional pre-commit hook, see follow-up task `tdd-criteria-precommit-hook`): + - `git diff --cached` includes a removed `expect(...)` / `assert(...)` / `assertThat(...)` line, OR + - changes the arguments of an existing assertion call, OR + - adds `.skip`, `xit`, `@skip`, `@Disabled`, etc. annotation, OR + - deletes a test file or `it(...)` / `test(...)` / `def test_*` definition + + AND the commit subject does not contain `[test-modify: ...]` matching the format above → block. + + AND `git diff --cached --name-only` includes both test-pattern and impl-pattern files → block (require split). + +5. **Don't apply rule 4 retroactively** to tests written before the rule was adopted. The rule applies to test changes made after the project's CLAUDE.md picks up `follow tdd-criteria`. Existing test bodies aren't grandfathered into requiring `was/is` for a one-time rewrite. +``` + +**ADD to «What's excluded as not bright-line»:** +- ~~«Tests should not be modified casually»~~ — paraphrasable, agents will modify casually and call it «refactor». Replaced by Anti-loophole rule 4 with literal-evidence requirement. + +**ADD to «Trade-offs» section:** +- **The literal-`was/is` requirement is verbose** for a renamed test or trivial typo fix. The verbosity is the point — an agent that genuinely fixed a typo writes the same assertion twice with one character changed; an agent that rewrote a failing test writes obviously different assertions. Reading `git log --grep '\[test-modify'` shows the difference at a glance. + +**UPDATE frontmatter** to add `amended: "2026-05-07: added test-immutability defence (Anti-loophole rule 4) after user noted symmetric vandalism risk on tests"`. + +--- + +**Commit pattern for impl session:** +- Commit 1: `skills/tdd-criteria/SKILL.md` (new file). Subject: `feat(skills): tdd-criteria skill v0.1.0 [TDD-default + 4 carve-outs + 4 anti-loophole rules incl. test-immutability]`. +- Commit 2: `.wiki/concepts/tdd-criteria-design.md` (amend). Subject: `docs(tdd-criteria): rule 4 — test-immutability defence (was X; is Y marker)`. Note: this is wiki, not test code, so `[test-modify]` rule doesn't apply to this commit — rule 4 governs *test* file changes, not wiki rationale changes. **Frontmatter (YAML):** - `name: tdd-criteria`