meta(tasks): update [tdd-criteria-skill-write] in claude-skills
This commit is contained in:
102
.tasks/STATUS.md
102
.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: <test-name>: was <X>; is <Y>; reason: <Z>]` where `<X>` and `<Y>` 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: <test-name>: was <X>; is <Y>; reason: <Z>]
|
||||||
|
```
|
||||||
|
Where `<X>` and `<Y>` 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):**
|
**Frontmatter (YAML):**
|
||||||
- `name: tdd-criteria`
|
- `name: tdd-criteria`
|
||||||
|
|||||||
Reference in New Issue
Block a user