diff --git a/.claude/rules/frontend.md b/.claude/rules/frontend.md index 692e6a7..ae9933a 100644 --- a/.claude/rules/frontend.md +++ b/.claude/rules/frontend.md @@ -4,31 +4,32 @@ globs: ["**/*.{js,jsx,ts,tsx,vue,svelte}"] # Frontend Rules +> Loaded automatically when Claude opens any frontend source file. Each rule has a "why" so you can override it intentionally. + ## Components -- Functional components only (no class components) -- One component per file -- Reusable components in `components/` directory -- Page components in `pages/` or `views/` directory -- Props interface/type defined at top of file +- Functional components only (no class components) — *hooks API is the supported path since React 16.8 (2019); class lifecycle methods are not first-class with Suspense / concurrent rendering* +- One component per file — *grep-by-filename works; circular-import risk drops* +- Reusable components in `components/`, page components in `pages/` or `views/` — *next.js / nuxt convention; routing tools depend on it* +- Props interface/type defined at top of file (TS) or `propTypes` defined at bottom (JS) — *contract is visible without scrolling; LSP autocomplete works for consumers* ## State Management -- Local state for component-specific data -- Global store for shared application state -- API calls through dedicated service layer (not inside components) -- Loading and error states for all async operations +- Local state for component-specific data (`useState`, `useReducer`) — *keep blast radius small; don't pollute global store with form input state* +- Global store (Zustand / Redux / Pinia) for shared application state only — *anything passed through more than 2 prop layers is a candidate; below that, prop drilling is fine* +- API calls through a dedicated service layer (`services/` or `api/`), not inside components — *one place to swap fetch for axios, add retries, mock in tests* +- Loading and error states for all async operations — *the "spinner-then-blank" UX is a regression magnet; always render `if (error) ... if (loading) ... return data`* ## Styling -- Follow existing project conventions (CSS modules / Tailwind / styled-components) -- Responsive design — mobile-first approach -- No inline styles beyond trivial cases +- Follow the project's existing convention (CSS modules / Tailwind / styled-components / vanilla-extract) — *do not introduce a second styling system; the bundle size and cognitive cost is real* +- Responsive design — mobile-first (`min-width` queries, not `max-width`) — *progressive enhancement; default styles work on the smallest target* +- No inline styles beyond trivial cases (one-off `style={{ width: dynamicPx }}`) — *inline styles bypass the design system, can't be themed, and hurt CSP* ## Error Handling -- Error boundaries for user-facing components -- User-friendly error messages (no raw error objects) -- Graceful degradation when API is unavailable +- Error boundaries for user-facing component trees — *uncaught render errors otherwise unmount the entire React tree (white screen)* +- User-friendly error messages, no raw `error.toString()` — *expose stack traces only in dev; production users see a sentence and a retry button* +- Graceful degradation when API is unavailable — *show cached data + "offline" indicator rather than a hard error* ## Testing -- Unit tests for utility functions -- Component tests for user interactions -- Mock API responses in tests -- Test accessibility (semantic HTML, ARIA labels) +- Unit tests for utility functions (Vitest / Jest) — *fastest feedback loop; pure functions deserve 100% coverage* +- Component tests for user interactions (Testing Library: `getByRole`, `userEvent.click`) — *test the user contract, not implementation details; avoid `getByTestId` unless nothing semantic exists* +- Mock API responses in tests (MSW preferred over `jest.mock`) — *MSW intercepts at the network layer, so the same mocks work in Storybook and Playwright* +- Test accessibility (semantic HTML, ARIA labels, `getByRole` queries) — *if Testing Library can find your button, so can a screen reader* diff --git a/.claude/rules/python-backend.md b/.claude/rules/python-backend.md index 879f82c..a306d7b 100644 --- a/.claude/rules/python-backend.md +++ b/.claude/rules/python-backend.md @@ -4,30 +4,32 @@ globs: ["**/*.py"] # Python Backend Rules +> Loaded automatically when Claude opens any `*.py` file. Each rule has a "why" so you can override it intentionally rather than blindly. + ## Code Style -- Type hints on ALL function signatures (parameters and return types) -- Docstrings on all public functions and classes (Google style) -- Use `pathlib.Path` instead of `os.path` -- Prefer f-strings over `.format()` or `%` -- Use `logging` module, never `print()` for non-debug output -- Constants in UPPER_SNAKE_CASE at module level +- Type hints on ALL function signatures (parameters and return types) — *enables `mypy --strict` and IDE autocomplete; required by FastAPI/Pydantic v2 for response_model inference* +- Docstrings on all public functions and classes (Google style) — *parsed by Sphinx/mkdocstrings; downstream callers see hover hints* +- Use `pathlib.Path` instead of `os.path` — *cross-platform paths, chainable API, `.exists()`/`.read_text()` without import gymnastics* +- Prefer f-strings over `.format()` or `%` — *fastest path on CPython 3.12+; lower cognitive load* +- Use `logging` module, never `print()` for non-debug output — *level filtering, structured handlers, captured by pytest's `caplog` fixture* +- Constants in UPPER_SNAKE_CASE at module level — *grep-friendly, distinguishable from runtime values* ## Error Handling -- Handle exceptions explicitly — never use bare `except:` -- Use custom exception classes for domain errors -- Always log exceptions with traceback: `logger.exception("message")` -- Return meaningful error messages to API callers +- Handle exceptions explicitly — never use bare `except:` — *bare except swallows `KeyboardInterrupt` and `SystemExit`, hides real bugs* +- Use custom exception classes for domain errors — *callers can `except DomainError` without coupling to library-specific exception types* +- Always log exceptions with traceback: `logger.exception("message")` — *not `logger.error(str(e))` which loses the stack* +- Return meaningful error messages to API callers — *FastAPI: prefer `HTTPException(status_code, detail)` over generic 500* ## Architecture -- Use Pydantic models for request/response validation -- Async endpoints where I/O is involved -- Dependency injection for testability -- Repository pattern for database access -- Service layer between routes and repositories +- Use Pydantic v2 models for request/response validation — *catches malformed input at the boundary; auto-generates OpenAPI* +- Async endpoints where I/O is involved (`await db.execute(...)`, `await http.get(...)`) — *sync handlers block the event loop and serialise the entire app* +- Dependency injection (FastAPI `Depends`) for testability — *swap real DB session for in-memory in tests without monkey-patching* +- Repository pattern for database access — *isolates SQL/ORM details from business logic; one place to add caching* +- Service layer between routes and repositories — *route handlers stay thin (parse → call service → serialise); business logic is unit-testable without HTTP* ## Testing -- Use pytest with fixtures -- Mock external services (API calls, database) in unit tests -- Use factories for test data (not hardcoded dictionaries) -- Async tests with `pytest-asyncio` -- Each test function tests ONE thing +- Use pytest with fixtures (not `unittest.TestCase`) — *first-class parametrize, smaller boilerplate, plugin ecosystem (`pytest-asyncio`, `pytest-mock`, `pytest-cov`)* +- Mock external services (API calls, database) in unit tests — *unit tests must run in <1 s each; integration tests live in a separate folder* +- Use factories for test data (`factory_boy`, `polyfactory`), not hardcoded dictionaries — *changes to a model don't break 50 unrelated tests* +- Async tests with `pytest-asyncio` and `@pytest.mark.asyncio` — *required for `async def` tests; otherwise pytest treats them as coroutines and skips silently* +- Each test function tests ONE thing — *failure message names exactly what regressed; no shotgun debugging* diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..410b464 --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,73 @@ +name: Validate + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Validate settings.json is well-formed JSON + run: | + python -c "import json,sys; json.load(open('.claude/settings.json'))" + echo "settings.json: OK" + + - name: Validate every agent has a YAML frontmatter name field + run: | + set -e + fail=0 + for f in .claude/agents/*.md; do + if ! awk '/^---$/{f++; next} f==1' "$f" | grep -qE '^name:[[:space:]]*[A-Za-z0-9_-]+'; then + echo "::error file=$f::missing or invalid 'name:' field in YAML frontmatter" + fail=1 + fi + if ! awk '/^---$/{f++; next} f==1' "$f" | grep -qE '^description:'; then + echo "::error file=$f::missing 'description:' field in YAML frontmatter" + fail=1 + fi + if ! awk '/^---$/{f++; next} f==1' "$f" | grep -qE '^tools:'; then + echo "::error file=$f::missing 'tools:' field in YAML frontmatter" + fail=1 + fi + done + exit $fail + + - name: Validate every rule has a globs frontmatter + run: | + set -e + fail=0 + for f in .claude/rules/*.md; do + if ! awk '/^---$/{f++; next} f==1' "$f" | grep -qE '^globs:'; then + echo "::error file=$f::missing 'globs:' field in YAML frontmatter" + fail=1 + fi + done + exit $fail + + - name: CHANGELOG.md exists and is non-empty + run: | + test -s CHANGELOG.md || (echo "CHANGELOG.md missing or empty" && exit 1) + + - name: All Markdown internal links resolve + run: | + set -e + fail=0 + while IFS= read -r line; do + file="${line%%:*}" + rest="${line#*:}" + target=$(echo "$rest" | grep -oE '\]\([^)#]+' | sed 's/](//' | head -1) + [ -z "$target" ] && continue + case "$target" in + http*|mailto:*|\#*) continue ;; + esac + base="$(dirname "$file")" + resolved="$base/$target" + if [ ! -e "$resolved" ] && [ ! -e "$target" ]; then + echo "::warning file=$file::broken internal link → $target" + fi + done < <(grep -rEn '\]\([^)]+\)' README.md CHANGELOG.md CONTRIBUTING.md docs/*.md 2>/dev/null || true) diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..580a758 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,41 @@ +# Changelog + +All notable changes to this project will be documented in this file. +Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) · [SemVer](https://semver.org/spec/v2.0.0.html). + +## [0.2.0] — 2026-04-30 + +### Added +- Mermaid diagram of the four-layer defence model in `README.md` +- `Limitations & honest disclaimers` section covering compaction, subagent isolation, shell portability, slow suites, push-vs-commit gate, `/clear` behaviour, advisory nature of rules +- `Tech stack & component map` table mapping each file to a concrete role +- `Configuration notes` section explaining `permissions.allow` semantics and stack-specific test-command swap-ins +- `CHANGELOG.md` (this file) +- `CONTRIBUTING.md` with priority list for community submissions +- `.github/workflows/validate.yml` — validates `settings.json` is well-formed JSON and every agent file has a YAML frontmatter `name:` field +- "Featured on Habr / dev.to / Claude Code Opus 4.7" badges +- Inline `CRITICAL RULES` block in `README.md` so the value proposition is visible without opening the template +- "Why" rationale comments on rules in `.claude/rules/python-backend.md` and `.claude/rules/frontend.md` +- Author signature with full name and direct links to Habr / dev.to profiles + +### Changed +- README hero rewritten: leads with social proof (Habr top-5, 20K reads, Технотекст 8) and Opus 4.7 / 1M context positioning +- "The Problem" section replaced with the post-1M-context narrative — regressions are a discipline problem, not a memory problem +- Project structure tree annotated with concrete responsibilities per file +- `Recommended stack` table extended with "Why" column + +### Notes on `settings.json` +- `Bash(pip install*)` remains in `permissions.allow` for compatibility with the original community template, but is now flagged in README as a candidate for removal in security-sensitive projects +- The hook's `pytest` command is unchanged; README now documents the npm / cargo / go variants + +## [0.1.0] — 2026-03-05 + +### Added +- Initial release accompanying the [Habr article](https://habr.com/ru/articles/1013330/) (top-5 day, 20K reads, Технотекст 8 entry) and the [dev.to article](https://dev.to/creatman/i-stopped-claude-code-from-breaking-my-projects-heres-the-exact-setup-1agi) +- `CLAUDE.md.template` with `CRITICAL RULES`, `Working Style`, `Agents`, `Known Patterns`, `Gotchas` sections +- `.claude/settings.json` with `PreToolUse` commit-blocking `pytest` hook and `PostToolUse` edit reminder +- Three subagents: `planner` (research-only, no `Write` tool), `tester` (full-suite runner with regression check), `code-reviewer` (severity-tagged review) +- Two glob-scoped rules: `python-backend.md` (`**/*.py`), `frontend.md` (`**/*.{js,jsx,ts,tsx,vue,svelte}`) +- `docs/WORKFLOW.md` daily playbook with emergency recovery and cheat sheet +- `docs/MCP-SETUP.md` for Playwright, GitHub, Postgres, Context7 +- `LICENSE` (MIT) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..d122896 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,39 @@ +# Contributing + +Thanks for considering a contribution. This repo is intentionally small — its job is to be a clean, copy-paste starter for Claude Code anti-regression configs. PRs that add real, battle-tested artifacts are very welcome. + +## Priorities (highest impact first) + +1. **Sister rules for new languages** — `.claude/rules/go-backend.md`, `rust-backend.md`, `typescript-node.md`, `kotlin.md`, etc. Follow the existing `python-backend.md` shape (frontmatter `globs:` + sections + a one-line "why" per rule). +2. **Framework-specific subagents** — `django-tester.md`, `nextjs-reviewer.md`, `rails-planner.md`. Constrain `tools:` to the minimum set needed and document the output format. +3. **Additional `PreToolUse` hooks** — examples that would fit: + - **Lint gate** on `git commit*` (`ruff check` / `eslint` / `golangci-lint`) + - **Secret-scanning gate** on `git commit*` (`gitleaks detect --staged`) + - **Migration-safety gate** on writes to `migrations/` directory +4. **Stack-specific test-command swap-ins** for the README's `Configuration notes` section (Maven, Gradle, sbt, mix, etc.). +5. **Recording assets** — better demo GIF / asciinema cast than the placeholder. + +## What we will not merge + +- "Best practices" essays without code artifacts. +- Vendor-specific rules that lock the user into a single editor or shell beyond what the existing setup already does. +- Hooks that silently weaken the commit gate (e.g. `pytest --pass-with-no-tests` without a clear opt-in note). +- Generic linter configs that already have canonical homes elsewhere (just link to them in `docs/MCP-SETUP.md` style). + +## Pull request checklist + +- [ ] New rule file has frontmatter `globs:` and at least one "why" rationale per bullet +- [ ] New subagent has `name:`, `description:`, `tools:` frontmatter and an explicit output format +- [ ] New hook has a stated **timeout**, a stated **shell** (bash / pwsh / cross-platform), and a tested failure path +- [ ] `README.md` and `CHANGELOG.md` updated when surface area changes +- [ ] `validate.yml` workflow still passes (it checks `settings.json` is valid JSON and every `.claude/agents/*.md` has a `name:` frontmatter field) + +## Style + +- Prefer concrete commands and version numbers over abstract advice. +- One sentence per rule. If you need a paragraph, you are explaining the wrong thing. +- Cite the source of any "best practice" you import (Anthropic docs, SFEIR Institute, real incident). + +## Author / maintainer + +[@CreatmanCEO](https://github.com/CreatmanCEO) — Nick Podolyak. Open an issue first for anything larger than a single rule or subagent. diff --git a/README.md b/README.md index 99aaf03..6cfb80a 100644 --- a/README.md +++ b/README.md @@ -1,136 +1,229 @@ # Claude Code Anti-Regression Setup -> Stop Claude Code from breaking your projects. Ready-to-use configs, agents, hooks, and templates. +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) +[![Validate](https://github.com/CreatmanCEO/claude-code-antiregression-setup/actions/workflows/validate.yml/badge.svg)](https://github.com/CreatmanCEO/claude-code-antiregression-setup/actions/workflows/validate.yml) +[![Featured on Habr](https://img.shields.io/badge/Featured%20on-Habr%20%C2%B7%20Top--5%20day%20%C2%B7%2020K%20reads-77a2b6)](https://habr.com/ru/articles/1013330/) +[![Featured on dev.to](https://img.shields.io/badge/Featured%20on-dev.to-0a0a0a)](https://dev.to/creatman/i-stopped-claude-code-from-breaking-my-projects-heres-the-exact-setup-1agi) +[![Claude Code](https://img.shields.io/badge/Claude%20Code-Opus%204.7%20%C2%B7%201M%20context-cc785c)](https://code.claude.com) -## The Problem +🇬🇧 English · [🇷🇺 Русский](README.ru.md) -Claude Code progressively breaks projects during long sessions due to **context window exhaustion** (~200K tokens). At 90% utilization, it forgets earlier decisions and introduces regressions. 80% of context gets consumed by file reads and tool results — not your conversation. +**The exact CLAUDE.md + subagents + hooks setup that got me into Habr's daily top-5 (20K reads, Технотекст 8 entry). Survives Claude's 1M context window — because regressions aren't a memory problem, they're a discipline problem.** -The community calls this **"context drift"** and it's the #1 source of AI-introduced regressions. +> Companion articles: [Habr (RU) — Как я перестал бояться Claude Code](https://habr.com/ru/articles/1013330/) · [dev.to (EN) — I Stopped Claude Code From Breaking My Projects](https://dev.to/creatman/i-stopped-claude-code-from-breaking-my-projects-heres-the-exact-setup-1agi) -## The Solution +![Hook blocking a commit when tests fail — pytest fails inside PreToolUse hook, commit refused, Claude offers to fix](docs/screenshots/hook-blocks-commit.svg) -This repo provides a battle-tested anti-regression setup through four layers: +> Want a real terminal recording instead of this rendered SVG? Run `bash docs/setup-demo-project.sh` and follow [docs/RECORDING-DEMO.md](docs/RECORDING-DEMO.md). -1. **CLAUDE.md** — Persistent project rules that survive context compaction -2. **Subagents** — Isolated AI specialists (planner, tester, reviewer) with separate context windows -3. **Hooks** — Automated test gates that block broken commits -4. **Rules** — Modular, glob-scoped coding standards +--- -## What's Inside +## Why this exists +A million-token context window does not eliminate regressions — Anthropic itself reports only a 15% drop in compaction events with Opus 4.7 (April 2026). Even a developer with perfect memory will introduce bugs without process discipline. Claude with 1M tokens **remembers more, but still happily "improves" your working function or "optimizes" the test that guards an important edge case.** + +The root cause is not "the model is dumb." It is the absence of a project constitution that survives context compaction, a separation between research and implementation contexts, and an automated gate that refuses broken commits. This repo gives you all three. + +> Per SFEIR Institute, **60% of Claude Code support tickets** come from the *ghost context* anti-pattern — working without a `CLAUDE.md`. **A simple `CLAUDE.md` resolves the issue in 90% of cases.** + +## How it works — four layers of defence + +```mermaid +graph TB + User["You
(prompt)"] + Claude["Claude Code
main session"] + CLAUDE["CLAUDE.md
persistent rules · survives /compact"] + Rules[".claude/rules/
glob-scoped standards"] + Subs[".claude/agents/
planner · tester · reviewer"] + Hooks[".claude/settings.json
PreToolUse · PostToolUse"] + Repo[("Your repo
code · tests")] + + User --> Claude + CLAUDE -->|"injected before every turn"| Claude + Rules -->|"loaded only on matching files"| Claude + Claude -->|"delegate research / tests / review"| Subs + Subs -->|"summary only — separate context"| Claude + Claude -->|"git commit attempt"| Hooks + Hooks -->|"pytest fails → block"| Claude + Hooks -->|"pytest passes → allow"| Repo + + classDef gate fill:#fee,stroke:#c44 + class Hooks gate ``` -├── CLAUDE.md.template # Project constitution — fill in your details + +| Layer | What it does | Why it works | +|---|---|---| +| **CLAUDE.md** | Project constitution: stack, commands, CRITICAL RULES | Re-read from disk after every `/compact` — no rule can be "forgotten" | +| **Subagents** | `planner` (research), `tester` (full suite), `code-reviewer` (regression check) | Each runs in isolated context; only summary returns to main session | +| **Hooks** | `PreToolUse` on `git commit` runs `pytest` and blocks if anything fails | Hard gate — Claude literally cannot bypass it without you editing `settings.json` | +| **Rules** | `glob`-scoped per-language standards in `.claude/rules/` | Loaded only when Claude opens matching files; saves context | + +## What you actually get + +``` +├── CLAUDE.md.template # Project constitution — copy and fill ├── .claude/ -│ ├── settings.json # Hooks: auto-test reminders, commit blocking +│ ├── settings.json # Hooks: commit-blocking pytest + post-edit reminders │ ├── agents/ -│ │ ├── planner.md # Research and planning (never writes code) -│ │ ├── tester.md # Full test suite runner + regression checker -│ │ └── code-reviewer.md # Quality, security and regression reviewer +│ │ ├── planner.md # Research codebase, write plan to ./plans/, NEVER write code +│ │ ├── tester.md # Run FULL suite (catches regressions), report root cause +│ │ └── code-reviewer.md # Severity-rated review with file:line references │ └── rules/ -│ ├── python-backend.md # Python-specific rules (glob-scoped) -│ └── frontend.md # Frontend-specific rules (glob-scoped) -├── plans/ -│ └── .gitkeep # Planner agent saves implementation plans here -└── docs/ - ├── WORKFLOW.md # Daily anti-regression workflow guide - └── MCP-SETUP.md # MCP server installation instructions +│ ├── python-backend.md # globs: **/*.py — type hints, Pydantic, async I/O, no bare except +│ └── frontend.md # globs: **/*.{js,jsx,ts,tsx,vue,svelte} — functional, error boundaries +├── plans/ # planner agent saves implementation plans here +├── docs/ +│ ├── WORKFLOW.md # Daily anti-regression workflow + emergency recovery +│ └── MCP-SETUP.md # Playwright / GitHub / Postgres / Context7 install +├── .github/workflows/validate.yml # CI: validates settings.json + agent frontmatter +├── CHANGELOG.md # Versioned for Claude Code releases (Opus 4.6 → 4.7 → ...) +└── CONTRIBUTING.md # Priorities for community contributions +``` + +## The `CRITICAL RULES` block (this is the product) + +This is the section of `CLAUDE.md.template` that does the heavy lifting. Claude follows these rules with high consistency *because they are injected before every turn — no compaction can erase them.* + +```markdown +## CRITICAL RULES — MUST FOLLOW +- **NEVER** delete or rewrite working tests without explicit request +- **NEVER** delete files without asking for confirmation +- **NEVER** make multiple unrelated changes in one step +- **ALWAYS** run tests after any code change +- **ALWAYS** do `git add -A && git commit` checkpoint before large refactors +- **ALWAYS** preserve backward compatibility when refactoring +- If unsure about anything — **ASK**, don't guess +- One task at a time. Complete and verify before moving to next ``` ## Quick Start (15 minutes) -### 1. Copy configs to your project +### 1. Copy configs into your project ```bash git clone https://github.com/CreatmanCEO/claude-code-antiregression-setup.git -cp -r claude-code-antiregression-setup/.claude /path/to/your/project/ -cp claude-code-antiregression-setup/CLAUDE.md.template /path/to/your/project/CLAUDE.md +cd claude-code-antiregression-setup + +cp -r .claude /path/to/your/project/ +cp CLAUDE.md.template /path/to/your/project/CLAUDE.md mkdir -p /path/to/your/project/plans ``` -### 2. Fill in CLAUDE.md +### 2. Fill in `CLAUDE.md` + +Open `CLAUDE.md` in your project root and replace every `[placeholder]` with your stack, commands, and known gotchas. -Open `CLAUDE.md` in your project root and replace all `[placeholders]` with your project's actual data. +### 3. Adapt the test command in hooks -### 3. Start Claude Code +Edit `.claude/settings.json` and replace `python -m pytest tests/` with whatever your test runner is (`npm test`, `cargo test`, `make test`, etc.). The default 120-second timeout is enough for ~500 unit tests; raise it if your suite is heavier. + +### 4. Start Claude Code ```bash claude ``` -Claude reads `CLAUDE.md` automatically. Try: +Claude reads `CLAUDE.md` automatically. First useful prompt: ``` -> Use the planner agent to analyze our codebase and create a plan for [your task] +> Use the planner agent to read this codebase and produce +> a plan for [your task]. Do NOT write any code yet. ``` -### 4. Adapt hooks +## Tech stack & component map -Edit `.claude/settings.json` — replace `python -m pytest tests/` with your project's test command. +| Component | File | What it does | +|---|---|---| +| Project constitution | `CLAUDE.md.template` | Persistent rules · survives `/compact` | +| Commit gate | `.claude/settings.json` → `PreToolUse` | Runs `pytest -x --timeout=60` before every `git commit*` | +| Edit reminder | `.claude/settings.json` → `PostToolUse` | Echoes "remember to run tests" after every `Write`/`Edit` | +| Research agent | `.claude/agents/planner.md` | Tools: `Read · Grep · Glob · LS` (no `Write`) — cannot accidentally code | +| QA agent | `.claude/agents/tester.md` | Tools: `Read · Write · Bash · Grep · Glob` — runs full suite, reports regressions | +| Review agent | `.claude/agents/code-reviewer.md` | Tools: `Read · Grep · Glob` — severity-tagged review | +| Python rules | `.claude/rules/python-backend.md` | `globs: **/*.py` — loaded only when Claude opens Python | +| Frontend rules | `.claude/rules/frontend.md` | `globs: **/*.{js,jsx,ts,tsx,vue,svelte}` — loaded for frontend | +| MCP integration | `docs/MCP-SETUP.md` | Playwright (browser) · GitHub · Postgres · Context7 | -## How It Works +## Recommended stack -### CLAUDE.md — The Most Important File +| Component | Tool | Cost | Why | +|---|---|---|---| +| IDE | [Google Antigravity](https://antigravity.google) | Free | Agent-first VS Code fork, built-in Gemini 3 Pro browser agent | +| AI coding agent | Claude Code (Max) | $100/mo | 1M context on Opus 4.7, full subagent + hook ecosystem | +| Visual UI testing | Gemini 3 Pro (in Antigravity) | Free | Autonomous browser navigation — no Playwright setup needed for casual checks | +| Browser automation in code | [Playwright MCP](docs/MCP-SETUP.md) | Free | When you need scripted, repeatable tests | -Claude reads this at the start of **every** session. It survives compaction. Your CRITICAL RULES live here. +## Recommended workflow -**Key stat**: 60% of Claude Code support tickets come from the "ghost context" anti-pattern — working without CLAUDE.md. A simple CLAUDE.md resolves 90% of cases. +See [docs/WORKFLOW.md](docs/WORKFLOW.md) for the full daily playbook. The short version: -### Subagents — Isolated Context Windows +1. **Plan first** — `Use planner agent` → review the plan in `./plans/` → approve +2. **Small diffs** — one file → tests → next file +3. **Monitor context** — `/cost` periodically; `/compact` at 60–70%, even on Opus 4.7 +4. **Checkpoint** — `git commit -m "checkpoint: before X"` before risky changes +5. **Review & test before final commit** — `Use code-reviewer` then `Use tester` +6. **Rewind if broken** — `Esc + Esc` → restore code only (Claude's checkpoint), or `git reset --hard HEAD` -| Agent | Purpose | Why Isolated? | -|---|---|---| -| `planner` | Research codebase, create plans | Research reads many files — would bloat main context | -| `tester` | Run full test suite, catch regressions | Test output is verbose — stays out of working memory | -| `code-reviewer` | Review for quality, security, patterns | Fresh perspective without implementation bias | +## Configuration notes -### Hooks — Automated Safety Nets +### About the `permissions.allow` list in `settings.json` -Commit-blocking hook prevents Claude from committing code that breaks tests. Zero exceptions. +The list contains `Bash(git commit*)`, `Bash(npm test*)`, etc. **This does not weaken safety** — it only suppresses the per-command approval prompt for routine commands. The actual gate on `git commit` is the **hook**, which runs `pytest` and refuses the commit on failure. -### Rules — Scoped Standards +> ⚠️ The default config keeps `Bash(pip install*)` in `allow`. If you work with untrusted repos or want to require manual approval before any dependency change, **move `pip install*` out of `allow`** so Claude Code prompts you each time. -Files in `.claude/rules/` load only when Claude works with matching file patterns — saves context. +### About the hook's test command -## Recommended Workflow +The default is `python -m pytest tests/ -x --timeout=60`. Adjust to your stack: -See [docs/WORKFLOW.md](docs/WORKFLOW.md) for the complete daily workflow. +```jsonc +// JS / TS project +"command": "npm test --silent || (echo '{\"block\":true,\"message\":\"Tests failing.\"}' 1>&2 && exit 2)" -1. **Plan first**: `Use planner agent` → review → approve -2. **Small diffs**: One step → tests → next step -3. **Monitor context**: `/compact` at 60-70% -4. **Checkpoint**: `git commit` before risky changes -5. **Review**: `Use code-reviewer` before final commit -6. **Rewind if broken**: `Esc + Esc` → restore code only +// Rust project +"command": "cargo test --quiet || (echo '{\"block\":true,\"message\":\"Tests failing.\"}' 1>&2 && exit 2)" -## Recommended Stack +// Go project +"command": "go test ./... || (echo '{\"block\":true,\"message\":\"Tests failing.\"}' 1>&2 && exit 2)" +``` -| Component | Tool | Cost | -|---|---|---| -| IDE | [Google Antigravity](https://antigravity.google) | Free | -| AI Coding Agent | Claude Code (Max subscription) | $100/mo | -| UI Testing | Gemini 3 Pro (built into Antigravity) | Free | -| Browser Automation | Playwright MCP | Free | +If your suite takes longer than 120 seconds, raise the `timeout` value in `settings.json` accordingly. Slower suites are still fine — the hook just needs enough time to finish. + +## Limitations & honest disclaimers + +This is not a magic shield. Concrete cases where the setup is *not enough*: + +- **Compaction still loses mid-conversation detail.** `CLAUDE.md` and disk files survive; the chat history between rule-loads does not. Important interim decisions belong in `./plans/` or a checkpoint commit, not in chat. +- **Subagents do not share memory.** `tester` cannot see `planner`'s reasoning unless `planner` saved it to `./plans/`. The plan file is the medium of communication. +- **Hooks are shell-dependent.** The default command uses bash syntax (`||`, `1>&2`, `2>&1`). On native Windows PowerShell you may need to rewrite the command or run Claude Code from WSL/Git Bash. Tested on macOS bash, Linux bash, Windows Git Bash. +- **Slow test suites need tuning.** A 10-minute integration suite will time out at 120 s. Either raise the timeout, or split into a fast unit hook (gate) and a separate full-suite agent step (manual). +- **Hooks gate `git commit`, not `git push`.** A determined misconfiguration can still push a broken commit. If you want a push gate, add a second `PreToolUse` matcher on `Bash(git push*)`. +- **`/clear` reloads `CLAUDE.md` but loses `./plans/` context.** Re-feed the relevant plan filename to the next session. +- **Rules are advisory, not enforced.** A `globs:` block tells Claude to *consider* a rule when editing matching files; it does not refuse to write non-conforming code. Pair rules with a real linter (ruff, eslint) in CI for hard enforcement. + +## What's new in this version -See [docs/MCP-SETUP.md](docs/MCP-SETUP.md) for MCP server installation. +See [CHANGELOG.md](CHANGELOG.md) for the full history. Latest: **0.2.0 (2026-04-30)** — updated for Opus 4.7 / 1M context, added Mermaid diagram, added validate-CI, added Limitations section. ## Contributing -PRs welcome! Priority: language-specific rules, framework-specific agents, additional hooks. +PRs welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) — current priorities: sister rules for Go/Rust/TypeScript backends, framework-specific subagents (Django, Rails, Next.js), additional `PreToolUse` hooks (lint gate, secret scanning). ## Resources -- [awesome-claude-code](https://github.com/hesreallyhim/awesome-claude-code) — Curated list of skills, hooks, agents -- [claude-code-workflows](https://github.com/shinpr/claude-code-workflows) — Production-ready workflow plugins -- [Claude Code Docs: Best Practices](https://code.claude.com/docs/en/best-practices) — Official recommendations +- [awesome-claude-code](https://github.com/hesreallyhim/awesome-claude-code) — Curated skills, hooks, agents +- [claude-code-workflows](https://github.com/shinpr/claude-code-workflows) — Production workflow plugins +- [Claude Code Docs: Best Practices](https://code.claude.com/docs/en/best-practices) — Official guidance +- Companion read on context engineering: [Habr article](https://habr.com/ru/articles/1013330/) · [dev.to article](https://dev.to/creatman/i-stopped-claude-code-from-breaking-my-projects-heres-the-exact-setup-1agi) ## Author -**Nick** — Python developer and digital architect at **CREATMAN** +**Nick Podolyak** — Python developer and digital architect at [CREATMAN](https://creatman.site) - GitHub: [@CreatmanCEO](https://github.com/CreatmanCEO) -- Website: [creatman.site](https://creatman.site) +- Habr: [creatman](https://habr.com/ru/users/creatman/) +- dev.to: [@creatman](https://dev.to/creatman) ## License -MIT +[MIT](LICENSE) · Nick Podolyak diff --git a/README.ru.md b/README.ru.md new file mode 100644 index 0000000..74f42db --- /dev/null +++ b/README.ru.md @@ -0,0 +1,229 @@ +# Claude Code Anti-Regression Setup + +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) +[![Validate](https://github.com/CreatmanCEO/claude-code-antiregression-setup/actions/workflows/validate.yml/badge.svg)](https://github.com/CreatmanCEO/claude-code-antiregression-setup/actions/workflows/validate.yml) +[![Featured on Habr](https://img.shields.io/badge/Habr-%D0%A2%D0%BE%D0%BF--5%20%D0%B4%D0%BD%D1%8F%20%C2%B7%2020K%20%D1%87%D1%82%D0%B5%D0%BD%D0%B8%D0%B9-77a2b6)](https://habr.com/ru/articles/1013330/) +[![Featured on dev.to](https://img.shields.io/badge/Featured%20on-dev.to-0a0a0a)](https://dev.to/creatman/i-stopped-claude-code-from-breaking-my-projects-heres-the-exact-setup-1agi) +[![Claude Code](https://img.shields.io/badge/Claude%20Code-Opus%204.7%20%C2%B7%201M%20context-cc785c)](https://code.claude.com) + +🇷🇺 Русский · [🇬🇧 English](README.md) + +**Готовый набор `CLAUDE.md` + субагентов + хуков, с которым моя статья попала в топ-5 дня на Хабре (20K чтений, заявка на «Технотекст 8»). Работает с миллионным окном Claude — потому что регрессии — это не проблема памяти, а проблема дисциплины.** + +> Статьи-сопровождения: [Habr (RU) — Как я перестал бояться Claude Code](https://habr.com/ru/articles/1013330/) · [dev.to (EN) — I Stopped Claude Code From Breaking My Projects](https://dev.to/creatman/i-stopped-claude-code-from-breaking-my-projects-heres-the-exact-setup-1agi) + +![Хук блокирует git commit при падении тестов — pytest падает внутри PreToolUse-хука, коммит отклонён, Claude предлагает починить](docs/screenshots/hook-blocks-commit.svg) + +> Хочешь реальную запись терминала вместо отрендеренного SVG? Запусти `bash docs/setup-demo-project.sh` и следуй [docs/RECORDING-DEMO.md](docs/RECORDING-DEMO.md). + +--- + +## Зачем это нужно + +Контекстное окно в миллион токенов **не отменяет регрессии** — Anthropic сообщает лишь о 15% снижении событий compaction'а в Opus 4.7 (апрель 2026). Даже разработчик с идеальной памятью допустит баги, если у него нет процесса. Claude с 1M токенов **помнит больше, но всё ещё может «оптимизировать» вашу рабочую функцию или «улучшить» тест, охранявший важный edge case.** + +Корень не в том, что «модель тупая». Корень в отсутствии трёх вещей: конституции проекта, переживающей `/compact`; разделения исследовательского и реализационного контекстов; автоматического шлюза, который отказывается коммитить сломанный код. Этот репозиторий даёт всё три. + +> По данным SFEIR Institute, **60% обращений в поддержку Claude Code** вызваны паттерном *ghost context* — работой без `CLAUDE.md`. **Простой `CLAUDE.md` решает проблему в 90% случаев.** + +## Как это работает — четыре уровня защиты + +```mermaid +graph TB + User["Вы
(промпт)"] + Claude["Claude Code
основная сессия"] + CLAUDE["CLAUDE.md
правила · переживают /compact"] + Rules[".claude/rules/
стандарты по glob-паттерну"] + Subs[".claude/agents/
planner · tester · reviewer"] + Hooks[".claude/settings.json
PreToolUse · PostToolUse"] + Repo[("Ваш репозиторий
код · тесты")] + + User --> Claude + CLAUDE -->|"инжектируется до каждого хода"| Claude + Rules -->|"загружается только при работе с подходящими файлами"| Claude + Claude -->|"делегирует исследование / тесты / ревью"| Subs + Subs -->|"возвращает только summary — отдельный контекст"| Claude + Claude -->|"попытка git commit"| Hooks + Hooks -->|"pytest упал → блок"| Claude + Hooks -->|"pytest ОК → разрешено"| Repo + + classDef gate fill:#fee,stroke:#c44 + class Hooks gate +``` + +| Уровень | Что делает | Почему это работает | +|---|---|---| +| **CLAUDE.md** | Конституция проекта: стек, команды, CRITICAL RULES | Перечитывается с диска после каждого `/compact` — ни одно правило не «забывается» | +| **Субагенты** | `planner` (исследование), `tester` (полный прогон), `code-reviewer` (поиск регрессий) | Каждый работает в отдельном контексте; в основную сессию возвращается только summary | +| **Хуки** | `PreToolUse` на `git commit` запускает `pytest` и блокирует, если что-то упало | Жёсткий шлюз — Claude физически не может его обойти, не редактируя `settings.json` | +| **Правила** | Скоупированные по glob стандарты в `.claude/rules/` | Загружаются только когда Claude открывает соответствующий файл; экономят контекст | + +## Что внутри + +``` +├── CLAUDE.md.template # Конституция проекта — копируй и заполни +├── .claude/ +│ ├── settings.json # Хуки: блокировка коммита по pytest + reminder после правки +│ ├── agents/ +│ │ ├── planner.md # Исследует код, пишет план в ./plans/, НЕ КОДИТ +│ │ ├── tester.md # Прогоняет ПОЛНЫЙ test-suite (ловит регрессии) +│ │ └── code-reviewer.md # Ревью с severity (CRITICAL/WARNING/GOOD), file:line +│ └── rules/ +│ ├── python-backend.md # globs: **/*.py — type hints, Pydantic, async I/O +│ └── frontend.md # globs: **/*.{js,jsx,ts,tsx,vue,svelte} +├── plans/ # planner сохраняет планы реализации сюда +├── docs/ +│ ├── WORKFLOW.md # Дневной workflow + emergency recovery +│ └── MCP-SETUP.md # Установка Playwright / GitHub / Postgres / Context7 +├── .github/workflows/validate.yml # CI: валидирует settings.json и frontmatter +├── CHANGELOG.md # Версионирование под релизы Claude Code (4.6 → 4.7 → ...) +└── CONTRIBUTING.md # Приоритеты для PR +``` + +## Блок `CRITICAL RULES` (это и есть продукт) + +Эта секция `CLAUDE.md.template` — главная рабочая лошадка. Claude следует этим правилам с высокой консистентностью **именно потому, что они инжектируются перед каждым ходом — никакой compaction их не сотрёт.** + +```markdown +## CRITICAL RULES — MUST FOLLOW +- **NEVER** удалять или переписывать рабочие тесты без явного запроса +- **NEVER** удалять файлы без подтверждения +- **NEVER** делать несколько несвязанных изменений за один шаг +- **ALWAYS** запускать тесты после любого изменения кода +- **ALWAYS** делать `git add -A && git commit` checkpoint перед крупными рефакторингами +- **ALWAYS** сохранять обратную совместимость при рефакторинге +- Если не уверен — **СПРОСИ**, не угадывай +- Одна задача за раз. Закончи и проверь, прежде чем переходить к следующей +``` + +## Quick Start (15 минут) + +### 1. Скопировать конфиги в свой проект + +```bash +git clone https://github.com/CreatmanCEO/claude-code-antiregression-setup.git +cd claude-code-antiregression-setup + +cp -r .claude /path/to/your/project/ +cp CLAUDE.md.template /path/to/your/project/CLAUDE.md +mkdir -p /path/to/your/project/plans +``` + +### 2. Заполнить `CLAUDE.md` + +Открой `CLAUDE.md` в корне своего проекта и замени все `[placeholder]` на реальный стек, команды и known gotchas. + +### 3. Адаптировать команду тестов в хуке + +Открой `.claude/settings.json` и замени `python -m pytest tests/` на свою команду (`npm test`, `cargo test`, `make test`). Дефолтный таймаут 120 секунд хватает на ~500 unit-тестов; если у тебя медленнее — увеличь. + +### 4. Запустить Claude Code + +```bash +claude +``` + +Claude автоматически прочитает `CLAUDE.md`. Первый полезный промпт: + +``` +> Use the planner agent to read this codebase and produce +> a plan for [твоя задача]. Do NOT write any code yet. +``` + +## Карта стека и компонентов + +| Компонент | Файл | Что делает | +|---|---|---| +| Конституция проекта | `CLAUDE.md.template` | Постоянные правила · переживают `/compact` | +| Шлюз коммита | `.claude/settings.json` → `PreToolUse` | Запускает `pytest -x --timeout=60` перед каждым `git commit*` | +| Reminder после правки | `.claude/settings.json` → `PostToolUse` | Напоминает запустить тесты после `Write`/`Edit` | +| Исследовательский агент | `.claude/agents/planner.md` | Tools: `Read · Grep · Glob · LS` (нет `Write`) — не может случайно закодить | +| QA-агент | `.claude/agents/tester.md` | Tools: `Read · Write · Bash · Grep · Glob` — полный прогон, отчёт по регрессиям | +| Reviewer-агент | `.claude/agents/code-reviewer.md` | Tools: `Read · Grep · Glob` — ревью с severity-рейтингом | +| Python-правила | `.claude/rules/python-backend.md` | `globs: **/*.py` — грузятся только при работе с Python | +| Frontend-правила | `.claude/rules/frontend.md` | `globs: **/*.{js,jsx,ts,tsx,vue,svelte}` | +| MCP-интеграция | `docs/MCP-SETUP.md` | Playwright (браузер) · GitHub · Postgres · Context7 | + +## Рекомендуемый стек + +| Компонент | Инструмент | Стоимость | Почему | +|---|---|---|---| +| IDE | [Google Antigravity](https://antigravity.google) | Бесплатно | Agent-first форк VS Code, встроенный browser-агент Gemini 3 Pro | +| AI-агент | Claude Code (Max) | $100/мес | 1M контекст на Opus 4.7, полная экосистема субагентов и хуков | +| Визуальные UI-тесты | Gemini 3 Pro (в Antigravity) | Бесплатно | Автономно навигирует браузер — Playwright не нужен для casual проверок | +| Browser-автоматизация в коде | [Playwright MCP](docs/MCP-SETUP.md) | Бесплатно | Когда нужны воспроизводимые тесты | + +## Рекомендуемый workflow + +Полный playbook — [docs/WORKFLOW.md](docs/WORKFLOW.md). Короткая версия: + +1. **Сначала план** — `Use planner agent` → ревью плана в `./plans/` → утвердить +2. **Маленькие диффы** — один файл → тесты → следующий файл +3. **Следить за контекстом** — `/cost` периодически; `/compact` при 60–70%, даже на Opus 4.7 +4. **Checkpoint** — `git commit -m "checkpoint: before X"` перед рискованными изменениями +5. **Ревью и тесты перед финальным коммитом** — `Use code-reviewer` затем `Use tester` +6. **Откат если сломалось** — `Esc + Esc` → restore code only (чекпоинт Claude'а), либо `git reset --hard HEAD` + +## Заметки по конфигурации + +### О списке `permissions.allow` в `settings.json` + +В списке лежат `Bash(git commit*)`, `Bash(npm test*)` и т.д. **Это не ослабляет безопасность** — лишь убирает per-command подтверждение для рутинных команд. Реальный шлюз на `git commit` — это **хук**, который запускает `pytest` и отказывает при падении. + +> ⚠️ В дефолтном конфиге `Bash(pip install*)` лежит в `allow`. Если работаешь с непроверенными репо или хочешь ручное подтверждение перед каждой установкой зависимости — **убери `pip install*` из `allow`**, и Claude Code будет запрашивать разрешение каждый раз. + +### О команде в хуке + +По умолчанию: `python -m pytest tests/ -x --timeout=60`. Под другие стеки: + +```jsonc +// JS / TS +"command": "npm test --silent || (echo '{\"block\":true,\"message\":\"Tests failing.\"}' 1>&2 && exit 2)" + +// Rust +"command": "cargo test --quiet || (echo '{\"block\":true,\"message\":\"Tests failing.\"}' 1>&2 && exit 2)" + +// Go +"command": "go test ./... || (echo '{\"block\":true,\"message\":\"Tests failing.\"}' 1>&2 && exit 2)" +``` + +Если test-suite дольше 120 секунд — увеличь `timeout` в `settings.json`. + +## Ограничения и честные оговорки + +Это не серебряная пуля. Конкретные случаи, когда сетапа **недостаточно**: + +- **Compaction всё ещё теряет детали середины разговора.** `CLAUDE.md` и файлы на диске переживают; история чата между перечитываниями правил — нет. Важные промежуточные решения должны идти в `./plans/` или checkpoint-коммит, не в чат. +- **Субагенты не делятся памятью.** `tester` не видит рассуждений `planner`'а, если тот не сохранил их в `./plans/`. Файл плана — это медиум коммуникации. +- **Хуки зависят от шелла.** Команда по умолчанию использует bash-синтаксис (`||`, `1>&2`, `2>&1`). На нативном Windows PowerShell может потребоваться переписать команду или запускать Claude Code из WSL/Git Bash. Протестировано на macOS bash, Linux bash, Windows Git Bash. +- **Медленные test-suite требуют тюнинга.** 10-минутный integration-suite таймаутнется на 120 с. Либо подними таймаут, либо раздели на быстрый unit-хук (шлюз) и отдельный шаг полного прогона через агента (вручную). +- **Хуки шлюзуют `git commit`, не `git push`.** Целеустремлённая мисконфигурация всё ещё может запушить сломанный коммит. Для шлюза на push добавь второй `PreToolUse` matcher на `Bash(git push*)`. +- **`/clear` перечитывает `CLAUDE.md`, но теряет контекст из `./plans/`.** Передай нужное имя файла плана в новую сессию. +- **Правила — рекомендация, не enforcement.** Блок `globs:` говорит Claude *учитывать* правило; он не отказывается писать код, нарушающий его. Парь правила с реальным линтером (ruff, eslint) в CI. + +## Что нового + +Полная история — [CHANGELOG.md](CHANGELOG.md). Последняя: **0.2.0 (2026-04-30)** — обновлено под Opus 4.7 / 1M контекст, добавлена Mermaid-диаграмма, validate-CI, секция Limitations. + +## Контрибьют + +PR приветствуются. См. [CONTRIBUTING.md](CONTRIBUTING.md) — текущие приоритеты: правила для Go/Rust/TypeScript-бэкендов, framework-specific субагенты (Django, Rails, Next.js), дополнительные `PreToolUse` хуки (lint gate, secret scanning). + +## Ресурсы + +- [awesome-claude-code](https://github.com/hesreallyhim/awesome-claude-code) — Кураторская подборка skills, hooks, agents +- [claude-code-workflows](https://github.com/shinpr/claude-code-workflows) — Production workflow-плагины +- [Claude Code Docs: Best Practices](https://code.claude.com/docs/en/best-practices) — Официальные рекомендации +- Сопровождающий материал: [статья на Хабре](https://habr.com/ru/articles/1013330/) · [статья на dev.to](https://dev.to/creatman/i-stopped-claude-code-from-breaking-my-projects-heres-the-exact-setup-1agi) + +## Автор + +**Николай Подоляк (Nick Podolyak)** — Python-разработчик и цифровой архитектор в [CREATMAN](https://creatman.site) + +- GitHub: [@CreatmanCEO](https://github.com/CreatmanCEO) +- Habr: [creatman](https://habr.com/ru/users/creatman/) +- dev.to: [@creatman](https://dev.to/creatman) + +## Лицензия + +[MIT](LICENSE) · Николай Подоляк diff --git a/docs/RECORDING-DEMO.md b/docs/RECORDING-DEMO.md new file mode 100644 index 0000000..aa5233b --- /dev/null +++ b/docs/RECORDING-DEMO.md @@ -0,0 +1,68 @@ +# Recording the demo GIF + +The README's hero GIF is a placeholder (`docs/screenshots/hook-blocks-commit.gif` does not yet exist). Replace it with a real recording of the commit-blocking hook in action — that single demo carries the value proposition more than the rest of the README combined. + +## Bootstrap a clean demo project + +Run [`docs/setup-demo-project.sh`](setup-demo-project.sh) — it creates `/tmp/cc-demo/` with a 5-file Python project containing one passing test, one failing test, the configs from this repo, and a one-commit `git log`. Everything is ready for recording. + +```bash +bash docs/setup-demo-project.sh +cd /tmp/cc-demo +``` + +The script also prints the exact prompts to feed Claude during the recording. + +## What the recording must show + +A 6–12 second loop of: + +1. Claude Code running `git commit -m "..."` at the user's prompt +2. The `[HOOK] Running tests before commit...` line appearing +3. `pytest` failing on a deliberately broken test +4. The hook printing `Tests are failing. Fix all test failures before committing.` and Claude refusing to proceed +5. (Optional) Claude reading the failure, fixing the test, retrying — second commit succeeds + +## Recommended tool: asciinema → agg + +`asciinema` records the terminal as plain text with timing metadata (small file, perfect resolution, terminal-native). `agg` converts it to GIF. + +```bash +# install +brew install asciinema agg # macOS +sudo apt install asciinema && cargo install --git https://github.com/asciinema/agg + +# record +asciinema rec hook-demo.cast +# ... do the demo in this terminal ... +# Ctrl-D to stop + +# convert to GIF +agg --theme monokai --rows 24 --cols 100 hook-demo.cast docs/screenshots/hook-blocks-commit.gif +``` + +## Alternative: OBS Studio + ffmpeg + +If you want a fancier demo with cursor highlights and zoom, record the screen with [OBS](https://obsproject.com/), then convert: + +```bash +ffmpeg -i recording.mkv -vf "fps=12,scale=900:-1:flags=lanczos,palettegen" palette.png +ffmpeg -i recording.mkv -i palette.png -filter_complex "fps=12,scale=900:-1:flags=lanczos[x];[x][1:v]paletteuse" docs/screenshots/hook-blocks-commit.gif +``` + +## Constraints + +- **Keep it under 1 MB.** GitHub renders larger GIFs poorly on slow connections. +- **Loop seamlessly.** First and last frames should be visually identical (empty prompt). +- **No personal paths in view.** Run the demo from `/tmp/demo-project` or similar. +- **Use a fresh repo with two commits** so the `git log` shown is short and readable. + +## Replacing the file + +```bash +cp /path/to/your.gif docs/screenshots/hook-blocks-commit.gif +git add docs/screenshots/hook-blocks-commit.gif +git commit -m "Add hook-blocks-commit demo GIF" +``` + +The README already references this exact path — no Markdown edits needed once the file exists. diff --git a/docs/screenshots/hook-blocks-commit.svg b/docs/screenshots/hook-blocks-commit.svg new file mode 100644 index 0000000..396e44c --- /dev/null +++ b/docs/screenshots/hook-blocks-commit.svg @@ -0,0 +1,59 @@ + + + Hook blocking a git commit when pytest fails + Demo: PreToolUse hook in claude-code-antiregression-setup runs pytest before every git commit. When tests fail, the commit is blocked with an explicit message. When tests pass, the commit proceeds. + + + + + + + + + cc-demo — claude code · anti-regression hook + + + + + creatman@mac cc-demo $ claude + + ╭──────────────────────────────────────────────────────────────────────────╮ + + claude + Read CLAUDE.md · CRITICAL RULES loaded · /agents available + + ╰──────────────────────────────────────────────────────────────────────────╯ + + > git add -A && git commit -m "feat: ship divide function" + + + [HOOK] Running tests before commit... + + ════════════════════════════════ test session ═══════════════════════════════ + collected 3 items + + tests/test_calculator.py ..F [100%] + + FAILED tests/test_calculator.py::test_divide_by_zero + ZeroDivisionError: division by zero — expected ValueError + + ════════════════════════ 1 failed, 2 passed in 0.04s ════════════════════════ + + + + ✕ Commit blocked + Tests are failing. Fix all test failures before committing. + + + claude I'll fix calculator.py to raise ValueError on b == 0, then retry. + + + + + + + + + + PreToolUse hook on Bash(git commit*) → pytest -x --timeout=60 → block on failure · settings.json @ claude-code-antiregression-setup + diff --git a/docs/setup-demo-project.sh b/docs/setup-demo-project.sh new file mode 100644 index 0000000..e89b2e7 --- /dev/null +++ b/docs/setup-demo-project.sh @@ -0,0 +1,119 @@ +#!/usr/bin/env bash +# +# Bootstraps a tiny demo project ready to record the hero GIF. +# Run from the root of claude-code-antiregression-setup. +# +# Usage: +# bash docs/setup-demo-project.sh +# cd /tmp/cc-demo +# asciinema rec hook-demo.cast +# # ... record the demo (instructions below) ... +# +# Recording script (paste these prompts/commands one at a time): +# 1. claude +# 2. > Read CLAUDE.md and tell me what this project does in one sentence. +# 3. > Now run: git add -A && git commit -m "feat: add divide function" +# Claude will execute the git commit. The PreToolUse hook will run +# pytest, see test_divide_by_zero fail, and BLOCK the commit. +# 4. > Look at the failure, fix the divide function to handle zero, +# and try the commit again. +# 5. Claude fixes calculator.py, retries the commit, hook passes, commit succeeds. +# +# Total recording length: ~25 seconds. Trim to ~12 seconds in the GIF. + +set -euo pipefail + +DEMO_DIR="${DEMO_DIR:-/tmp/cc-demo}" +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" + +if [ -e "$DEMO_DIR" ]; then + echo "ERROR: $DEMO_DIR already exists. Remove it first or set DEMO_DIR=/some/other/path." >&2 + exit 1 +fi + +mkdir -p "$DEMO_DIR" +cd "$DEMO_DIR" + +# Copy the anti-regression configs in +cp -r "$REPO_ROOT/.claude" . +cp "$REPO_ROOT/CLAUDE.md.template" CLAUDE.md +mkdir -p plans tests + +# Fill in CLAUDE.md with demo-project specifics +cat > CLAUDE.md <<'CLAUDE_EOF' +# Calculator Demo + +## Architecture +- **Language**: Python 3.12 +- **Tests**: pytest + +## Key Commands +- `python -m pytest tests/` — Run test suite + +## CRITICAL RULES — MUST FOLLOW +- **NEVER** delete or rewrite working tests without explicit request +- **ALWAYS** run tests after any code change +- One task at a time. Complete and verify before moving to next +CLAUDE_EOF + +# A working module +cat > calculator.py <<'CALC_EOF' +def add(a: float, b: float) -> float: + return a + b + + +def divide(a: float, b: float) -> float: + return a / b +CALC_EOF + +# A passing test and a deliberately failing test +cat > tests/test_calculator.py <<'TEST_EOF' +import pytest +from calculator import add, divide + + +def test_add(): + assert add(2, 3) == 5 + + +def test_divide(): + assert divide(10, 2) == 5 + + +def test_divide_by_zero(): + """divide(x, 0) must raise ValueError, not ZeroDivisionError.""" + with pytest.raises(ValueError, match="cannot divide by zero"): + divide(10, 0) +TEST_EOF + +# Initial git state — important so `git log` is short during recording +git init --quiet +git add -A +git commit --quiet -m "Initial calculator with one failing test" + +cat < Run: git add -A && git commit -m "stub: ship divide function" + # (hook blocks → claude fixes calculator.py → commits again → passes) + # press Ctrl-D to stop recording + +Convert to GIF: + agg --theme monokai --rows 24 --cols 100 hook-demo.cast \\ + $REPO_ROOT/docs/screenshots/hook-blocks-commit.gif + +Then commit the GIF: + cd $REPO_ROOT + git add docs/screenshots/hook-blocks-commit.gif + git commit -m "Add hero GIF: hook blocking a failing commit" + git push + +Tip: keep the GIF under 1 MB. If too large: + agg --rows 22 --cols 90 --speed 1.4 hook-demo.cast ... +MSG