From f9032086e2d06b83e7ae78e3800322761f4feaf8 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 16 Apr 2026 16:55:11 +0200 Subject: [PATCH 1/7] refactor: updating skill --- template/.claude/hooks/post-edit-markdown.sh | 15 ++++-- .../hooks/pre-write-doc-file-warning.sh | 16 ++++-- .../.claude/skills/sdlc-workflow/SKILL.md | 49 ++++++++++++++++--- ...de_git_cliff %}cliff.toml{% endif %}.jinja | 4 +- 4 files changed, 67 insertions(+), 17 deletions(-) diff --git a/template/.claude/hooks/post-edit-markdown.sh b/template/.claude/hooks/post-edit-markdown.sh index bb52109..1e85513 100755 --- a/template/.claude/hooks/post-edit-markdown.sh +++ b/template/.claude/hooks/post-edit-markdown.sh @@ -49,17 +49,24 @@ if echo "$FILE_PATH" | grep -qE '(^|/)docs/'; then exit 0 fi +# Files under tasks_summary/ are allowed (SDLC workflow summaries) +if echo "$FILE_PATH" | grep -qE '(^|/)tasks_summary/'; then + exit 0 +fi + # ── Violation ────────────────────────────────────────────────────────────── echo "┌─ Markdown placement violation: $FILE_PATH" echo "│" echo "│ RULE: Markdown files produced during agent workflows or analysis" -echo "│ MUST be written inside the docs/ folder." +echo "│ MUST be written inside the docs/ or tasks_summary/ folder." echo "│" echo "│ Allowed exceptions:" -echo "│ • README.md (anywhere)" -echo "│ • CLAUDE.md (anywhere)" +echo "│ • README.md (anywhere)" +echo "│ • CLAUDE.md (anywhere)" +echo "│ • docs/** (any depth)" +echo "│ • tasks_summary/** (SDLC workflow summaries)" echo "│" -echo "│ Action required: delete this file and recreate it under docs/." +echo "│ Action required: delete this file and recreate it under docs/ or tasks_summary/." echo "└─" exit 0 diff --git a/template/.claude/hooks/pre-write-doc-file-warning.sh b/template/.claude/hooks/pre-write-doc-file-warning.sh index 997663b..ca7e82b 100755 --- a/template/.claude/hooks/pre-write-doc-file-warning.sh +++ b/template/.claude/hooks/pre-write-doc-file-warning.sh @@ -50,18 +50,26 @@ if echo "$FILE_PATH" | grep -qE '(^|/)docs/'; then exit 0 fi +# Files under tasks_summary/ are allowed (SDLC workflow summaries) +if echo "$FILE_PATH" | grep -qE '(^|/)tasks_summary/'; then + echo "$INPUT" + exit 0 +fi + # ── Violation — block the write ─────────────────────────────────────────────── echo "┌─ BLOCKED: Markdown placement violation" >&2 echo "│" >&2 echo "│ Attempted path: $FILE_PATH" >&2 echo "│" >&2 echo "│ RULE: Markdown files produced during workflows or analysis must" >&2 -echo "│ be written inside the docs/ folder." >&2 +echo "│ be written inside the docs/ or tasks_summary/ folder." >&2 echo "│" >&2 echo "│ Allowed exceptions:" >&2 -echo "│ • README.md — anywhere" >&2 -echo "│ • CLAUDE.md — anywhere" >&2 +echo "│ • README.md — anywhere" >&2 +echo "│ • CLAUDE.md — anywhere" >&2 +echo "│ • docs/** — any depth" >&2 +echo "│ • tasks_summary/** — SDLC workflow summaries" >&2 echo "│" >&2 -echo "│ Action: recreate the file under docs/ — e.g. docs/$(basename "$FILE_PATH")" >&2 +echo "│ Action: recreate the file under docs/ or tasks_summary/" >&2 echo "└─" >&2 exit 2 diff --git a/template/.claude/skills/sdlc-workflow/SKILL.md b/template/.claude/skills/sdlc-workflow/SKILL.md index ea87ecf..d243800 100644 --- a/template/.claude/skills/sdlc-workflow/SKILL.md +++ b/template/.claude/skills/sdlc-workflow/SKILL.md @@ -31,7 +31,7 @@ current, `○` for upcoming. ``` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -SDLC ○DESIGN ○RED ○GREEN ○REFACTOR ○QUALITY ○SECURE ○DOCS ○COMMIT ○PR +SDLC ○DESIGN ○RED ○GREEN ○REFACTOR ○QUALITY ○SECURE ○DOCS ○COMMIT ○PR ○SUMMARY ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ``` @@ -54,6 +54,7 @@ Stage Name Model Execution Sub-skills 6 DOCUMENTATION Haiku Agent (parallel) python-docstrings, markdown 7 COMMIT + CHANGELOG Haiku Agent (sequential) (inline guidance) 8 PULL REQUEST Haiku Agent (sequential) prepare_pr + 9 SUMMARY Haiku Agent (sequential) (task_summary_template.md) ``` Conditional stages (2.5, 3.5) only activate when flagged in task YAML. @@ -68,9 +69,10 @@ Conditional stages (2.5, 3.5) only activate when flagged in task YAML. 3. Run `just preflight TASK_ID` to execute pre-flight checks. 4. This project uses `just test` for tests and `just ci` for full CI. 5. Explore the codebase: `pyproject.toml`, `conftest.py`, existing tests, target modules. -6. Create `task_channel/task_TASK_ID/` directory for persistent artifacts. +6. Create `tasks_summary/` directory if it does not exist (`mkdir -p tasks_summary`). 7. Output: requirement summary, acceptance criteria list, files that will change. -8. Get user approval before proceeding. + +Proceed automatically to Stage 0.5. --- @@ -95,8 +97,7 @@ Gate: all pre-flight checks pass. No user approval needed. - Name: `test__when_` - Structure: AAA (Arrange-Act-Assert) - Every test file must set `pytestmark = pytest.mark.` at module level. -4. Show draft to user, wait for approval. -5. Write the test file(s). +4. Write the test file(s). 6. Run `just test`. Classify the failure: | Failure type | Meaning | Action | @@ -121,8 +122,7 @@ Do not proceed until RED is confirmed for all acceptance criteria. - Include type annotations on all public functions. - If logging is involved, follow the structlog guidelines in `CLAUDE.md` (see "Structured logging" and "Mandatory `logging_manager` usage" sections). -2. Show draft, wait for approval. -3. Write implementation. +2. Write implementation. 4. Run `just test`. All tests must pass. 5. Check coverage: `just coverage`. New code should be fully covered. - If coverage below 85%, write targeted tests for the uncovered lines @@ -256,6 +256,39 @@ Report: PR URL and title. --- +## Stage 9 — SUMMARY: Write task summary + +**Model:** Haiku (Agent call, sequential after stage 8) + +``` +Using the task_summary_template.md from .claude/skills/sdlc-workflow/assets/templates/task_summary_template.md: + +1. Create the tasks_summary/ directory if it does not already exist: + mkdir -p tasks_summary + +2. Collect the following values from pipeline outputs: + - task_id, title, type, status, owner from the task YAML + - target_branch from `git branch --show-current` + - commit_hash from `git log -1 --format=%h` + - pr_url from Stage 8 output + - date: today's date (ISO 8601) + - stage-by-stage results: test counts, coverage %, lint/type/security findings, refactor changes + +3. Fill every {{placeholder}} in the template with the collected values. + +4. Write the completed document to: + tasks_summary/TASK_ID_summary.md + where TASK_ID is the actual task ID from the YAML. + +5. Print the path of the written file. + +Report: path of written summary file. +``` + +Gate: file exists at `tasks_summary/TASK_ID_summary.md`. + +--- + ## Error handling | Stage | On failure | @@ -265,6 +298,7 @@ Report: PR URL and title. | 4-6 (parallel) | Each retries up to 3 times independently. Others continue. | | 7 (COMMIT) | CI fix loop, max 3 iterations. Then stop + report. | | 8 (PR) | Report error. Provide PR body for manual creation. | +| 9 (SUMMARY) | Log warning. Do not fail the pipeline — PR is already merged. | --- @@ -296,6 +330,7 @@ When TASK.md has several acceptance criteria: | Definition of Ready validator | [scripts/validate_dor.py](scripts/validate_dor.py) | | Task YAML starter template | [assets/templates/task_template.yaml](assets/templates/task_template.yaml) | | Task summary starter template | [assets/templates/task_summary_template.md](assets/templates/task_summary_template.md) | +| Task summary output location | `tasks_summary/TASK_ID_summary.md` (repo root) | ## Support scripts diff --git a/template/{% if include_git_cliff %}cliff.toml{% endif %}.jinja b/template/{% if include_git_cliff %}cliff.toml{% endif %}.jinja index c137944..d764d91 100644 --- a/template/{% if include_git_cliff %}cliff.toml{% endif %}.jinja +++ b/template/{% if include_git_cliff %}cliff.toml{% endif %}.jinja @@ -55,8 +55,8 @@ sort_commits = "newest" # Clean commit messages commit_preprocessors = [ -{ pattern = "\(#[0-9]+\)", replace = "" }, # remove PR numbers -{ pattern = "\s+", replace = " " }, # normalize whitespace +{ pattern = "\\(#[0-9]+\\)", replace = "" }, # remove PR numbers +{ pattern = "\\s+", replace = " " }, # normalize whitespace ] # ---------------------- From c72ce6ab907e8c3799958f11e67dc6485654295c Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 16 Apr 2026 19:45:13 +0200 Subject: [PATCH 2/7] Update alignment of root and template files --- .pre-commit-config.yaml | 9 ++-- justfile | 82 ++++++++++++++++-------------- pyproject.toml | 85 ++++++++++++++++++++++---------- template/.pre-commit-config.yaml | 9 ++-- template/justfile.jinja | 77 ++++++++++++++++------------- template/pyproject.toml.jinja | 26 +++++----- uv.lock | 25 ++++++++++ 7 files changed, 197 insertions(+), 116 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2d05c32..4550554 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,20 +17,21 @@ repos: entry: found Copier update rejection files; review and remove them before committing. language: fail files: \.rej$ + - id: ruff-format name: Ruff Format - entry: uv run ruff format + entry: uv run ruff format --quiet language: system types: [python] - pass_filenames: true + pass_filenames: false stages: [pre-commit] - id: ruff-check name: Ruff Lint (auto-fix) - entry: uv run ruff check --fix --exit-non-zero-on-fix + entry: uv run ruff check --fix --exit-non-zero-on-fix --quiet language: system types: [python] - pass_filenames: true + pass_filenames: false stages: [pre-commit] - id: basedpyright diff --git a/justfile b/justfile index 6650223..d7eb170 100644 --- a/justfile +++ b/justfile @@ -42,7 +42,7 @@ fix: fmt-check: @uv run ruff format --check . -# Check docstring coverage on all non-test Python files +# Check docstring coverage docs-check: @echo "=== Docstring coverage check ===" @uv run ruff check --select D . @@ -119,8 +119,13 @@ test-integration: test-failed-verbose: @uv run pytest --lf -vv +# Run tests with coverage report (full HTML/XML/term output) coverage: - @uv run pytest --cov --cov-report=term-missing --cov-report=xml + @uv run pytest tests/ \ + --cov \ + --cov-report=term-missing \ + --cov-report=html \ + --cov-report=xml # Test command matching GitHub CI (3.11 matrix leg in .github/workflows/tests.yml) test-ci: @@ -159,13 +164,14 @@ precommit-install: @uv run pre-commit install --hook-type pre-push @uv run pre-commit install --hook-type commit-msg @git config commit.template "$(git rev-parse --show-toplevel)/.gitmessage" + @echo "✓ Hooks installed, .gitmessage template configured" # Interactive conventional commit (Commitizen); alternative to `git commit`. cz-commit: @uv run cz commit precommit: - @uv run pre-commit run --all-files --verbose + @uv run pre-commit run --all-files # Dependency audit matching .github/workflows/security.yml (pip-audit). # Uses ``uv run --with pip-audit`` so the tool runs with the project Python (``uv tool run``/``uvx`` @@ -182,7 +188,7 @@ sync: _set_env update: @uv lock --upgrade - @uv sync --frozen --extra dev + @just sync # Check for outdated dependencies deps-outdated: @@ -296,40 +302,6 @@ doctor: @echo "Repo: python_project_template" @echo "Python: >= 3.11" -# ------------------------------------------------------------------------- -# Repo automation -# ------------------------------------------------------------------------- - -# Generate repo freshness dashboard + JSON artifacts -freshness: - @uv run python scripts/repo_file_freshness.py - -# Validate root/template sync map and parity checks -sync-check: - @uv run python scripts/check_root_template_sync.py - -# Print a conventional PR title + PR body (template + git log) for pr-policy compliance -pr-draft: - @uv run python scripts/pr_commit_policy.py draft - -# ------------------------------------------------------------------------- -# SDLC: Task management -# ------------------------------------------------------------------------- - -# Validate a task YAML against Definition of Ready -dor-check TASK_ID: - python3 .claude/skills/sdlc-workflow/scripts/validate_dor.py tasks/{{TASK_ID}}.yaml - -# List all tasks and their statuses -tasks: - @echo "Task ID Status Title" - @echo "---------- ---------- -----" - @python3 -c "import yaml; from pathlib import Path; [print(f\"{d['task_id']:<14}{d['status']:<14}{d['title']}\") for p in sorted(Path('tasks').glob('TASK_*.yaml')) if (d := yaml.safe_load(p.read_text()))]" - -# Run pre-flight checks before starting SDLC pipeline -preflight TASK_ID: - bash .claude/skills/sdlc-workflow/scripts/preflight.sh {{TASK_ID}} - # ------------------------------------------------------------------------- # Release & Versioning # ------------------------------------------------------------------------- @@ -401,3 +373,37 @@ release BUMP_TYPE="patch": @echo "✅ Release complete!" @git describe --tags --abbrev=0 + +# ------------------------------------------------------------------------- +# Repo automation +# ------------------------------------------------------------------------- + +# Generate repo freshness dashboard + JSON artifacts +freshness: + @uv run python scripts/repo_file_freshness.py + +# Validate root/template sync map and parity checks +sync-check: + @uv run python scripts/check_root_template_sync.py + +# Print a conventional PR title + PR body (template + git log) for pr-policy compliance +pr-draft: + @uv run python scripts/pr_commit_policy.py draft + +# ------------------------------------------------------------------------- +# SDLC: Task management +# ------------------------------------------------------------------------- + +# Validate a task YAML against Definition of Ready +dor-check TASK_ID: + python3 .claude/skills/sdlc-workflow/scripts/validate_dor.py tasks/{{TASK_ID}}.yaml + +# List all tasks and their statuses +tasks: + @echo "Task ID Status Title" + @echo "---------- ---------- -----" + @python3 -c "import yaml; from pathlib import Path; [print(f\"{d['task_id']:<14}{d['status']:<14}{d['title']}\") for p in sorted(Path('tasks').glob('TASK_*.yaml')) if (d := yaml.safe_load(p.read_text()))]" + +# Run pre-flight checks before starting SDLC pipeline +preflight TASK_ID: + bash .claude/skills/sdlc-workflow/scripts/preflight.sh {{TASK_ID}} diff --git a/pyproject.toml b/pyproject.toml index efc8bd9..ad40584 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,20 @@ dev = [ "commitizen>=4.0.0", ] +[dependency-groups] +changelog = [ + "git-cliff>=2.6.0", +] +[project.urls] +Homepage = "https://github.com/buddingengineers12345/python_project_template" +Documentation = "https://github.com/buddingengineers12345/python_project_template#readme" +Repository = "https://github.com/buddingengineers12345/python_project_template" +Issues = "https://github.com/buddingengineers12345/python_project_template/issues" + +# ========================================================================== +# Ruff Configuration +# ========================================================================== + [tool.ruff] line-length = 100 target-version = "py311" @@ -74,13 +88,42 @@ convention = "google" [tool.ruff.lint.mccabe] max-complexity = 10 -[tool.ruff.lint.isort] -known-first-party = ["scripts"] - [tool.ruff.lint.per-file-ignores] "tests/**" = ["ARG", "T20"] # print() OK in tests; unused args OK (fixtures); D enforced "scripts/**" = ["T20"] # CLI scripts may use print(); docstrings enforced (D) +[tool.ruff.lint.isort] +known-first-party = ["scripts"] + +# ========================================================================== +# BasedPyright Configuration +# ========================================================================== + +[tool.basedpyright] +pythonVersion = "3.11" +typeCheckingMode = "standard" + +include = ["scripts", "tests"] +exclude = [ + "**/__pycache__", + "**/.*", + "build", + "dist", +] + +venvPath = "." +venv = ".venv" +reportImplicitOverride = false +reportMissingImports = true +reportMissingTypeStubs = false +reportUnknownMemberType = false +reportUnknownArgumentType = false +reportUnknownVariableType = false + +# ========================================================================== +# Pytest Configuration +# ========================================================================== + [tool.pytest.ini_options] minversion = "8.0" testpaths = ["tests"] @@ -88,7 +131,17 @@ pythonpath = ["."] # "." exposes scripts/ python_files = ["test_*.py"] python_classes = ["Test*"] python_functions = ["test_*"] -addopts = ["-q", "--strict-markers", "--strict-config", "--tb=short", "--log-level=WARNING", "-ra"] +addopts = [ + "-q", + "--strict-markers", + "--strict-config", + "--cov=my_library", + "--cov-report=term-missing", + "--tb=short", + "--log-level=WARNING", + "-p", "no:cacheprovider", + "-ra", +] markers = [ "e2e: End-to-end tests across the full stack or external systems", "integration: Tests crossing module boundaries, real I/O, subprocesses, or services", @@ -103,13 +156,14 @@ filterwarnings = [ ] # ========================================================================== -# Coverage Configuration (for template-repo test suite) +# Coverage Configuration # ========================================================================== [tool.coverage.run] branch = true -source = ["scripts"] # measure application code, not the test suite +source = ["scripts"] omit = [ + "tests/*", "**/__init__.py", "*/tests/*", "*/__pycache__/*", @@ -137,25 +191,6 @@ output = "coverage.xml" [tool.coverage.html] directory = "htmlcov" -[tool.basedpyright] -pythonVersion = "3.11" -typeCheckingMode = "standard" -reportMissingImports = true -include = ["scripts", "tests"] -exclude = [ - "**/__pycache__", - "**/.*", - "build", - "dist", -] -venvPath = "." -venv = ".venv" -reportImplicitOverride = false -reportMissingTypeStubs = false -reportUnknownMemberType = false -reportUnknownArgumentType = false -reportUnknownVariableType = false - # Commitizen: conventional commits (`cz commit`), commit-msg validation via pre-commit. # version_provider = "pep621" means commitizen reads/writes [project].version — no duplicate. [tool.commitizen] diff --git a/template/.pre-commit-config.yaml b/template/.pre-commit-config.yaml index 2d05c32..4550554 100644 --- a/template/.pre-commit-config.yaml +++ b/template/.pre-commit-config.yaml @@ -17,20 +17,21 @@ repos: entry: found Copier update rejection files; review and remove them before committing. language: fail files: \.rej$ + - id: ruff-format name: Ruff Format - entry: uv run ruff format + entry: uv run ruff format --quiet language: system types: [python] - pass_filenames: true + pass_filenames: false stages: [pre-commit] - id: ruff-check name: Ruff Lint (auto-fix) - entry: uv run ruff check --fix --exit-non-zero-on-fix + entry: uv run ruff check --fix --exit-non-zero-on-fix --quiet language: system types: [python] - pass_filenames: true + pass_filenames: false stages: [pre-commit] - id: basedpyright diff --git a/template/justfile.jinja b/template/justfile.jinja index a1f06b1..21c946e 100644 --- a/template/justfile.jinja +++ b/template/justfile.jinja @@ -2,9 +2,11 @@ # Justfile for {{ project_name }} # # Usage: -# just # list commands -# just ci # run full CI locally -# just test # run tests +# just # list commands +# just sync # install dev deps for this template repo +# just test # run template tests (pytest) +# just lint # ruff lint this repo +# just fmt # ruff format this repo # ========================================================================== # Always show commands if no recipe is given @@ -24,27 +26,27 @@ _set_env: # ------------------------------------------------------------------------- fmt: - @uv run ruff format src tests + @uv run ruff format . lint: - @uv run ruff check src tests + @uv run ruff check . # Lint only changed (unstaged+staged) Python files — fast feedback loop lint-changed: @uv run ruff check $(git diff --name-only -- '*.py') fix: - @uv run ruff format src tests - @uv run ruff check --fix src tests + @uv run ruff format . + @uv run ruff check --fix . # Format check (read-only) — matches GitHub Actions fmt-check: - @uv run ruff format --check src tests + @uv run ruff format --check . -# Check docstring coverage on all source Python files (Google style, D rules) +# Check docstring coverage docs-check: @echo "=== Docstring coverage check ===" - @uv run ruff check --select D src + @uv run ruff check --select D . @echo "✓ Docstring check complete" # Run all static analysis + docstring checks (pre-merge review) @@ -146,7 +148,13 @@ cz-commit: @uv run cz commit precommit: - @uv run pre-commit run --all-files --verbose + @uv run pre-commit run --all-files + +# Dependency audit matching .github/workflows/security.yml (pip-audit). +# Uses ``uv run --with pip-audit`` so the tool runs with the project Python (``uv tool run``/``uvx`` +# can pick a different interpreter whose venv ``ensurepip`` fails on some hosts). +audit: + @uv export --frozen --format requirements-txt --extra dev | uv run --with pip-audit pip-audit --requirement /dev/stdin # ------------------------------------------------------------------------- # Dependency management @@ -253,8 +261,9 @@ clean: check: @uv sync --frozen --extra dev --extra test{% if include_docs %} --extra docs{% endif %}{% if include_git_cliff %} --group changelog{% endif %} @just fmt-check - @uv run ruff check src tests + @uv run ruff check . @uv run basedpyright + @just sync-check @just docs-check @just test-ci @uv run pre-commit run --all-files --verbose @@ -263,6 +272,28 @@ ci: @just fix @just check +# ------------------------------------------------------------------------- +# Doctor / Diagnostics +# ------------------------------------------------------------------------- + +doctor: + @echo "=== Environment ===" + @python --version + @uv --version + @echo "" + @echo "=== Python Tools ===" + @uv run ruff --version + @uv run basedpyright --version || echo "basedpyright not installed" + @uv run pytest --version + @uv run cz version || echo "commitizen installed" + @echo "" + @echo "=== System Tools ===" + @git-cliff --version || echo "⚠️ git-cliff not found (required for 'just release')" + @echo "" + @echo "=== Project ===" + @echo "Package: {{ package_name }}" + @echo "Python: >= {{ python_min_version }}" + # ------------------------------------------------------------------------- # Release & Versioning # ------------------------------------------------------------------------- @@ -334,25 +365,3 @@ release BUMP_TYPE="patch": @echo "✅ Release complete!" @git describe --tags --abbrev=0 - -# ------------------------------------------------------------------------- -# Doctor / Diagnostics -# ------------------------------------------------------------------------- - -doctor: - @echo "=== Environment ===" - @python --version - @uv --version - @echo "" - @echo "=== Python Tools ===" - @uv run ruff --version - @uv run basedpyright --version || echo "basedpyright not installed" - @uv run pytest --version - @uv run cz version || echo "commitizen installed" - @echo "" - @echo "=== System Tools ===" - @git-cliff --version || echo "⚠️ git-cliff not found (required for 'just release')" - @echo "" - @echo "=== Project ===" - @echo "Package: {{ package_name }}" - @echo "Python: >= {{ python_min_version }}" diff --git a/template/pyproject.toml.jinja b/template/pyproject.toml.jinja index 2b3d407..35f3032 100644 --- a/template/pyproject.toml.jinja +++ b/template/pyproject.toml.jinja @@ -112,10 +112,10 @@ select = [ "PGH", # pygrep-hooks "PT", # flake8-pytest-style "ARG", # flake8-unused-arguments - "D", # pydocstyle — enforce Google-style docstrings on public symbols - "C90", # McCabe complexity — flag functions above max-complexity + "D", # pydocstyle — enforce Google-style docstrings + "C90", # McCabe complexity "PERF", # perflint — catch performance anti-patterns - "T20", # flake8-print — no print() in application code (use structlog) + "T20", # flake8-print — discourage print() in non-test code ] ignore = [ @@ -129,8 +129,8 @@ convention = "google" max-complexity = 10 [tool.ruff.lint.per-file-ignores] -"tests/**" = ["ARG", "T20"] # tests: ruff D (docstrings) enforced; print() OK (T20) -"scripts/**" = ["T20"] # CLI-style scripts may use print +"tests/**" = ["ARG", "T20"] # print() OK in tests; unused args OK (fixtures); D enforced +"scripts/**" = ["T20"] # CLI scripts may use print(); docstrings enforced (D) [tool.ruff.lint.isort] known-first-party = ["{{ package_name }}"] @@ -142,10 +142,6 @@ known-first-party = ["{{ package_name }}"] [tool.basedpyright] pythonVersion = "{{ python_min_version }}" typeCheckingMode = "standard" -reportMissingImports = true -reportMissingTypeStubs = false -venvPath = "." -venv = ".venv" include = ["src", "tests"] exclude = [ @@ -155,6 +151,15 @@ exclude = [ "dist", ] +venvPath = "." +venv = ".venv" +reportImplicitOverride = false +reportMissingImports = true +reportMissingTypeStubs = false +reportUnknownMemberType = false +reportUnknownArgumentType = false +reportUnknownVariableType = false + # ========================================================================== # Pytest Configuration # ========================================================================== @@ -167,14 +172,13 @@ python_files = ["test_*.py"] python_classes = ["Test*"] python_functions = ["test_*"] addopts = [ + "-q", "--strict-markers", "--strict-config", "--cov={{ package_name }}", "--cov-report=term-missing", - "--cov-report=xml", "--tb=short", "--log-level=WARNING", - "-m", "not slow", "-p", "no:cacheprovider", "-ra", ] diff --git a/uv.lock b/uv.lock index ae25871..4dee646 100644 --- a/uv.lock +++ b/uv.lock @@ -394,6 +394,23 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d5/08/c2409cb01d5368dcfedcbaffa7d044cc8957d57a9d0855244a5eb4709d30/funcy-2.0-py2.py3-none-any.whl", hash = "sha256:53df23c8bb1651b12f095df764bfb057935d49537a56de211b098f4c79614bb0", size = 30891, upload-time = "2023-03-28T06:22:42.576Z" }, ] +[[package]] +name = "git-cliff" +version = "2.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/90/cf/dff8cd706d2e30e264cb3b9880235607188fb3ad596bfe6282147165bdcd/git_cliff-2.12.0.tar.gz", hash = "sha256:57b96b1f61167f85395353d6f47a89944b4882c03880312d53c09dacecb7ff86", size = 102106, upload-time = "2026-01-20T17:46:12.602Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/a5/dc5f800f6a6dc175faa0787653119754dbbe81a9db1274e041443690287b/git_cliff-2.12.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e9ee9aa29e9435211712fdab4b5ec9fb432c4bc9d244e39351b2be57aeba7999", size = 6879200, upload-time = "2026-01-20T17:45:55.964Z" }, + { url = "https://files.pythonhosted.org/packages/d7/b6/0e251bd49700e767c47d8d524a690ad713a3aed4318074278438042b8f25/git_cliff-2.12.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e18512138db5ef57302155b1163c0a2cf43c3d79071a5e083883b65bb990218c", size = 6456349, upload-time = "2026-01-20T17:45:58.202Z" }, + { url = "https://files.pythonhosted.org/packages/5e/63/4e8780f60ad28e8c26ae2b2b365daff9ffa84cb441a5d5bf62c42a75e75a/git_cliff-2.12.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d24c3e334fdf309c59802ea1a9cd3828e92c8c7cacdd619bcabdc638e00e2ade", size = 6916209, upload-time = "2026-01-20T17:45:59.931Z" }, + { url = "https://files.pythonhosted.org/packages/71/83/0bfab93065e10bcbe97e6136ccf6c1e8552715ef61c11eb678c397ff5fb0/git_cliff-2.12.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1aa25b05a0315d0f58fc2ac21503538ca749fc3dd7476ee5d6bdf380d9f26ab", size = 7305605, upload-time = "2026-01-20T17:46:01.991Z" }, + { url = "https://files.pythonhosted.org/packages/30/eb/78f624e387c1d9084ca7bcec3a8f28fda9fbbfbeb18c71465a727ee677b5/git_cliff-2.12.0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:91eafd2f3ecf226b9a9c2a6c54d96df6042479927b48a97fcf46b728e8744bf1", size = 6927694, upload-time = "2026-01-20T17:46:03.798Z" }, + { url = "https://files.pythonhosted.org/packages/49/3f/735ddcb426c9f77498a039e9398162345c59f29c7990fbf22a530a15fb97/git_cliff-2.12.0-py3-none-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:26c9771a50a039252c67803f4c7f187f2ce9c5eea336b8cef890e94483af7a9d", size = 7118983, upload-time = "2026-01-20T17:46:05.535Z" }, + { url = "https://files.pythonhosted.org/packages/f0/97/68a5bd8063904fc43df7811e713483ccd831a877751283c6514dfb5b079e/git_cliff-2.12.0-py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:168f48b82f81ab8e1625d42adb739471623e25bd0a7e25b8c70490bad9e90e2b", size = 7541855, upload-time = "2026-01-20T17:46:07.348Z" }, + { url = "https://files.pythonhosted.org/packages/f7/00/2ed0bf7d71340c20906c1317db50cd6c14bdf0c90fa68a62885c9daf40a9/git_cliff-2.12.0-py3-none-win32.whl", hash = "sha256:4bc609a748c1c3493fe3e00a48305d343255ddff80e564fbf8eb954aac387784", size = 6354818, upload-time = "2026-01-20T17:46:09.117Z" }, + { url = "https://files.pythonhosted.org/packages/c0/fd/679d54e4ed37fdbadb58080219af8f35b5f659dd25e47ab1951b6349d1d0/git_cliff-2.12.0-py3-none-win_amd64.whl", hash = "sha256:c992b5756298251ecdd4db8abe087e90d00327f9eaf0c2470a44dbff64377d07", size = 7303564, upload-time = "2026-01-20T17:46:11.154Z" }, +] + [[package]] name = "identify" version = "2.6.18" @@ -844,6 +861,11 @@ dev = [ { name = "typing-extensions" }, ] +[package.dev-dependencies] +changelog = [ + { name = "git-cliff" }, +] + [package.metadata] requires-dist = [ { name = "basedpyright", marker = "extra == 'dev'", specifier = ">=1.21.0" }, @@ -861,6 +883,9 @@ requires-dist = [ ] provides-extras = ["dev"] +[package.metadata.requires-dev] +changelog = [{ name = "git-cliff", specifier = ">=2.6.0" }] + [[package]] name = "pywin32" version = "311" From 165c6bf3960940b2d1e8bfc0043c09aa2c4cc67f Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Apr 2026 00:18:52 +0200 Subject: [PATCH 3/7] chore: align skills, workflows, and template with CI - Rename prepare_pr skill to prepare-pr; add verification-script reference - Update Claude skills, hooks, and settings across root and template - Refresh GitHub workflows, justfile, pyproject, and integration tests - Regenerate docs assets and uv.lock after just ci Made-with: Cursor --- .claude/hooks/pre-write-doc-file-warning.sh | 7 + .claude/settings.json | 71 +++---- .claude/skills/bash-guide/SKILL.md | 17 ++ .claude/skills/config-management/SKILL.md | 7 + .claude/skills/cron-scheduling/SKILL.md | 7 + .claude/skills/css-guide/SKILL.md | 17 ++ .claude/skills/html-guide/SKILL.md | 17 ++ .claude/skills/jinja-guide/SKILL.md | 17 ++ .claude/skills/maintain-skills/SKILL.md | 190 ++++++++++++------ .../references/audit-checklist.md | 74 ++++++- .../references/fixing-skills.md | 78 ++++++- .../references/maintenance-log.md | 2 + .../references/verification-script.md | 100 +++++++++ .claude/skills/markdown/SKILL.md | 22 +- .../{prepare_pr => prepare-pr}/SKILL.md | 12 +- .../references/section-rules.md | 0 .claude/skills/pytest/SKILL.md | 33 ++- .claude/skills/python-code-quality/SKILL.md | 21 ++ .claude/skills/python-code-reviewer/SKILL.md | 4 +- .claude/skills/python-docstrings/SKILL.md | 10 + .claude/skills/sdlc-workflow/SKILL.md | 23 ++- .../sdlc-workflow/scripts/validate_dor.py | 4 +- .claude/skills/security/SKILL.md | 7 + .claude/skills/tdd-test-planner/SKILL.md | 6 +- .claude/skills/tdd-workflow/SKILL.md | 11 + .claude/skills/test-quality-reviewer/SKILL.md | 8 + .claude/skills/type-checking/SKILL.md | 7 + .github/workflows/lint.yml | 2 +- .github/workflows/tests.yml | 4 +- .gitignore | 9 + CLAUDE.md | 2 +- assets/file_freshness.json | 4 +- assets/root-template-sync-map.yaml | 4 +- docs/repo_file_status_report.md | 4 +- docs/repo_instructions.md | 4 +- docs/repo_management.md | 2 +- justfile | 23 ++- pyproject.toml | 32 +-- .../hooks/pre-write-doc-file-warning.sh | 7 + template/.claude/skills/bash-guide/SKILL.md | 17 ++ .../.claude/skills/config-management/SKILL.md | 7 + .../.claude/skills/cron-scheduling/SKILL.md | 7 + template/.claude/skills/css-guide/SKILL.md | 17 ++ template/.claude/skills/html-guide/SKILL.md | 17 ++ template/.claude/skills/jinja-guide/SKILL.md | 17 ++ .../.claude/skills/maintain-skills/SKILL.md | 190 ++++++++++++------ .../references/audit-checklist.md | 74 ++++++- .../references/fixing-skills.md | 78 ++++++- .../references/maintenance-log.md | 2 + .../references/verification-script.md | 100 +++++++++ template/.claude/skills/markdown/SKILL.md | 22 +- .../{prepare_pr => prepare-pr}/SKILL.md | 12 +- .../references/section-rules.md | 0 template/.claude/skills/pytest/SKILL.md | 33 ++- .../skills/python-code-quality/SKILL.md | 21 ++ .../skills/python-code-reviewer/SKILL.md | 4 +- .../.claude/skills/python-docstrings/SKILL.md | 10 + .../.claude/skills/sdlc-workflow/SKILL.md | 23 ++- template/.claude/skills/security/SKILL.md | 7 + .../.claude/skills/tdd-test-planner/SKILL.md | 6 +- template/.claude/skills/tdd-workflow/SKILL.md | 11 + .../skills/test-quality-reviewer/SKILL.md | 8 + .../.claude/skills/type-checking/SKILL.md | 7 + template/.github/workflows/ci.yml.jinja | 4 +- template/.gitignore | 9 + template/justfile.jinja | 15 +- template/pyproject.toml.jinja | 5 +- tests/integration/test_template.py | 22 +- uv.lock | 48 ++++- 69 files changed, 1377 insertions(+), 285 deletions(-) create mode 100644 .claude/skills/maintain-skills/references/verification-script.md rename .claude/skills/{prepare_pr => prepare-pr}/SKILL.md (92%) rename .claude/skills/{prepare_pr => prepare-pr}/references/section-rules.md (100%) create mode 100644 template/.claude/skills/maintain-skills/references/verification-script.md rename template/.claude/skills/{prepare_pr => prepare-pr}/SKILL.md (92%) rename template/.claude/skills/{prepare_pr => prepare-pr}/references/section-rules.md (100%) diff --git a/.claude/hooks/pre-write-doc-file-warning.sh b/.claude/hooks/pre-write-doc-file-warning.sh index ca7e82b..1aedb43 100755 --- a/.claude/hooks/pre-write-doc-file-warning.sh +++ b/.claude/hooks/pre-write-doc-file-warning.sh @@ -11,6 +11,7 @@ # • README.md — allowed anywhere # • CLAUDE.md — allowed anywhere # • docs/**/ — any depth under any docs/ directory +# • .claude/**/ — skills, rules, commands, hooks docs # # All other .md files → BLOCKED. Recreate them under docs/. # @@ -56,6 +57,12 @@ if echo "$FILE_PATH" | grep -qE '(^|/)tasks_summary/'; then exit 0 fi +# Files under .claude/ are allowed (skills, rules, commands, hooks docs) +if echo "$FILE_PATH" | grep -qE '(^|/)\.claude/'; then + echo "$INPUT" + exit 0 +fi + # ── Violation — block the write ─────────────────────────────────────────────── echo "┌─ BLOCKED: Markdown placement violation" >&2 echo "│" >&2 diff --git a/.claude/settings.json b/.claude/settings.json index 6b0526f..2668d23 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -13,7 +13,6 @@ "Bash(git commit:*)", "Bash(git checkout:*)", "Bash(git branch:*)", - "Bash(rm -rf /tmp/test-output*)", "Bash(python3:*)", "Bash(bash .claude/hooks/*)" ], @@ -69,74 +68,74 @@ "description": "Pre-commit: scan staged .py files for secrets/debug markers before git commit" }, { - "matcher": "Bash", + "matcher": "Write|Edit|MultiEdit", "hooks": [ { "type": "command", - "command": "bash .claude/hooks/pre-bash-coverage-gate.sh" + "command": "bash .claude/hooks/pre-config-protection.sh" } ], - "description": "Coverage gate: warn before git commit if test coverage is below 85%" + "description": "Block weakening edits to ruff/basedpyright config in pyproject.toml" }, { - "matcher": "Write|Edit|MultiEdit", + "matcher": "Write|Edit", "hooks": [ { "type": "command", - "command": "bash .claude/hooks/pre-config-protection.sh" + "command": "bash .claude/hooks/pre-protect-uv-lock.sh" } ], - "description": "Block weakening edits to ruff/basedpyright config in pyproject.toml and .pre-commit-config.yaml" + "description": "Block direct edits to uv.lock — must be regenerated via uv lock or just update" }, { "matcher": "Write|Edit", "hooks": [ { "type": "command", - "command": "bash .claude/hooks/pre-protect-uv-lock.sh" + "command": "bash .claude/hooks/pre-write-src-require-test.sh" } ], - "description": "Block direct edits to uv.lock — must be regenerated via uv lock or just update" + "description": "TDD enforcement: block writing src//.py if test file does not exist (use pre-write-src-test-reminder.sh instead for warn-only — do not register both)" }, { - "matcher": "Write|Edit|MultiEdit", + "matcher": "Bash", "hooks": [ { "type": "command", - "command": "bash .claude/hooks/pre-protect-readonly-files.sh" + "command": "bash .claude/hooks/pre-bash-coverage-gate.sh" } ], - "description": "Block direct edits to protected root-level config files: env.example, justfile, LICENSE, pyproject.toml, uv.lock, copier.yml, .gitignore, .gitmessage, .pre-commit-config.yaml, .secrets.baseline" + "description": "Coverage gate: warn before git commit if test coverage is below 85%" }, { - "matcher": "Write|Edit", + "matcher": "Bash", "hooks": [ { "type": "command", - "command": "bash .claude/hooks/pre-write-src-require-test.sh" + "command": "bash .claude/hooks/pre-delete-protection.sh" } ], - "description": "TDD enforcement: block writing src//.py if test file does not exist (use pre-write-src-test-reminder.sh instead for warn-only — do not register both)" + "description": "Block rm of critical files: pyproject.toml, justfile, CLAUDE.md, .pre-commit-config.yaml, .copier-answers.yml, uv.lock, .claude/settings.json" }, { - "matcher": "Write", + "matcher": "Bash", "hooks": [ { "type": "command", - "command": "bash .claude/hooks/pre-write-doc-file-warning.sh" + "command": "bash .claude/hooks/pre-bash-branch-protection.sh" } ], - "description": "Block writing .md files outside docs/ (pre-flight — prevents the wrong location)" + "description": "Block git push to main/master branches — use feature branches and PRs" }, { "matcher": "Write", "hooks": [ { "type": "command", - "command": "bash .claude/hooks/pre-write-jinja-syntax.sh" + "command": "bash .claude/hooks/pre-write-doc-file-warning.sh" } ], - "description": "Validate Jinja2 syntax before writing new/overwritten .jinja template files" + "description": "Block writing .md files outside docs/ (pre-flight — prevents the wrong location)" }, { "matcher": "Edit|Write", @@ -161,54 +160,44 @@ "description": "Run ruff check + basedpyright after every .py edit for immediate feedback" }, { - "matcher": "Edit|Write", + "matcher": "Edit", "hooks": [ { "type": "command", - "command": "bash .claude/hooks/post-edit-refactor-test-guard.sh" + "command": "bash .claude/hooks/post-edit-markdown.sh" } ], - "description": "TDD refactor guard: remind to run tests after multiple src/ or scripts/ edits" + "description": "Post-edit fallback: warn if an existing .md file is edited outside docs/" }, { "matcher": "Edit|Write", "hooks": [ { "type": "command", - "command": "bash .claude/hooks/post-edit-jinja.sh" - } - ], - "description": "Validate Jinja2 syntax after every .jinja template edit (post-edit secondary layer)" - }, - { - "matcher": "Edit", - "hooks": [ - { - "type": "command", - "command": "bash .claude/hooks/post-edit-markdown.sh" + "command": "bash .claude/hooks/post-edit-refactor-test-guard.sh" } ], - "description": "Post-edit fallback: warn if an existing .md file is edited outside docs/" + "description": "TDD refactor guard: remind to run tests after multiple src/ edits" }, { - "matcher": "Edit|Write", + "matcher": "Bash", "hooks": [ { "type": "command", - "command": "bash .claude/hooks/post-edit-copier-migration.sh" + "command": "bash .claude/hooks/post-bash-test-coverage-reminder.sh" } ], - "description": "Remind about _migrations, _skip_if_exists, and tests after copier.yml edits" + "description": "After pytest/just test runs, surface modules below 85% coverage" }, { - "matcher": "Edit|Write", + "matcher": "Write", "hooks": [ { "type": "command", - "command": "bash .claude/hooks/post-edit-template-mirror.sh" + "command": "bash .claude/hooks/post-write-test-structure.sh" } ], - "description": "Remind to mirror template/.claude/ changes to root .claude/ (and vice versa)" + "description": "After creating test files, check for test_ functions, no unittest.TestCase, and proper pytest markers" }, { "matcher": "Bash", diff --git a/.claude/skills/bash-guide/SKILL.md b/.claude/skills/bash-guide/SKILL.md index 78e287f..9d5cc9e 100644 --- a/.claude/skills/bash-guide/SKILL.md +++ b/.claude/skills/bash-guide/SKILL.md @@ -198,6 +198,23 @@ done For the full pattern library, see [references/patterns.md](references/patterns.md). +## When to load references + +| If the task involves… | Load | +|---------------------------------------------|---------------------------------| +| Script headers, `main()` layout, comments | `references/structure.md` | +| Dual-mode logging, JSON log output | `references/logging.md` | +| Arg parsing, traps, arrays, retries | `references/patterns.md` | +| Starting a new script from scratch | `templates/script-skeleton.sh` | +| Simple script edit or small function | No reference needed — use inline | + +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When adding multiple functions to a script, write them all in + a single Edit call rather than one function at a time. +- **Read before edit:** Read the full script first to understand its structure, + existing style, and variable names before making any changes. + ## Quick reference: where to go deeper | Topic | Reference file | diff --git a/.claude/skills/config-management/SKILL.md b/.claude/skills/config-management/SKILL.md index d4560cb..bfe8cc1 100644 --- a/.claude/skills/config-management/SKILL.md +++ b/.claude/skills/config-management/SKILL.md @@ -86,6 +86,13 @@ Run all hooks manually: `just precommit` 4. Keep `justfile` recipes simple — one concern per recipe. 5. Pin all dependency versions in `pyproject.toml`. +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When modifying pyproject.toml (e.g., adding dependencies and + updating tool config), combine all changes into a single Edit call. +- **Read before edit:** Read the full config file first. Plan all sections that + need changes, then apply in one Edit call. + ## Quick reference: where to go deeper | Topic | Reference file | diff --git a/.claude/skills/cron-scheduling/SKILL.md b/.claude/skills/cron-scheduling/SKILL.md index 5fad82c..4c482a0 100644 --- a/.claude/skills/cron-scheduling/SKILL.md +++ b/.claude/skills/cron-scheduling/SKILL.md @@ -14,6 +14,13 @@ description: >- # Cron Scheduling Skill +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When setting up multiple cron jobs, write all entries in a + single `crontab -e` session or Edit call rather than one job at a time. +- **Read before edit:** Read the current crontab first (`crontab -l`) to + understand existing jobs before adding or modifying entries. + ## Quick reference: where to go deeper | Topic | Reference file | diff --git a/.claude/skills/css-guide/SKILL.md b/.claude/skills/css-guide/SKILL.md index 24f3bcf..3389888 100644 --- a/.claude/skills/css-guide/SKILL.md +++ b/.claude/skills/css-guide/SKILL.md @@ -253,6 +253,23 @@ Recommended stylelint rules: `color-named: "never"`, `color-no-invalid-hex`, `declaration-no-important`, a BEM-compatible `selector-class-pattern`, and `no-duplicate-selectors`. +## When to load references + +| If the task involves… | Load | +|------------------------------------------|-------------------------------------| +| ITCSS layers, CSS architecture design | `references/css-architecture.md` | +| ARIA, focus styles, a11y compliance | `references/accessibility.md` | +| Grid, flexbox, responsive breakpoints | `references/layout-responsive.md` | +| Starting a new project from scratch | `templates/css-tokens.css` + `templates/css-reset.css` | +| Simple CSS edits (default) | No reference needed — use inline | + +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When adding multiple CSS rules or updating a stylesheet, + combine all changes into a single Edit call per file. +- **Read before edit:** Read the full stylesheet first to understand existing + patterns, tokens, and class naming before modifying. + ## Quick reference: where to go deeper | Topic | Reference file | diff --git a/.claude/skills/html-guide/SKILL.md b/.claude/skills/html-guide/SKILL.md index 40c5efd..d9be131 100644 --- a/.claude/skills/html-guide/SKILL.md +++ b/.claude/skills/html-guide/SKILL.md @@ -285,6 +285,23 @@ Recommended `.htmlhintrc`: } ``` +## When to load references + +| If the task involves… | Load | +|---------------------------------------------|------------------------------------| +| Choosing semantic elements (article, nav) | `references/semantic-elements.md` | +| WCAG compliance, ARIA, focus management | `references/accessibility.md` | +| Starting a new HTML page | `templates/html-template.html` | +| Simple HTML edits (default) | No reference needed — use inline | + +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When restructuring an HTML file (fixing semantics, adding + ARIA attributes, updating structure), plan all changes first and apply in + a single Edit call. +- **Read before edit:** Read the full HTML file first to understand existing + structure and patterns. + ## Quick reference: where to go deeper | Topic | Reference file | diff --git a/.claude/skills/jinja-guide/SKILL.md b/.claude/skills/jinja-guide/SKILL.md index adf4362..6fc3429 100644 --- a/.claude/skills/jinja-guide/SKILL.md +++ b/.claude/skills/jinja-guide/SKILL.md @@ -254,6 +254,23 @@ conventions on top of Jinja. Auto-escape defaults differ (Flask: on for structure, and security defaults, see [references/best-practices.md](references/best-practices.md). +## When to load references + +| If the task involves… | Load | +|-------------------------------------------------|-----------------------------------| +| Operator syntax, literals, global functions | `references/syntax.md` | +| Filter usage beyond basics (A-Z list) | `references/filters-reference.md` | +| Template inheritance, include, import, macros | `references/inheritance.md` | +| Performance, security, testing best practices | `references/best-practices.md` | +| Simple template edits (default) | No reference needed — use inline | + +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When modifying multiple template files, read all target + templates first, then edit each in a single Edit call per file. +- **Read before edit:** Read the base template and child templates to understand + the inheritance chain before making block changes. + ## Quick reference: where to go deeper | Topic | Reference file | diff --git a/.claude/skills/maintain-skills/SKILL.md b/.claude/skills/maintain-skills/SKILL.md index 98a3a35..edef624 100644 --- a/.claude/skills/maintain-skills/SKILL.md +++ b/.claude/skills/maintain-skills/SKILL.md @@ -4,16 +4,18 @@ description: >- Audit, standardize, and improve existing skills in a skills directory (commonly `.claude/skills/` or `template/.claude/skills/`). Use for 'audit this skill', 'review skill quality', 'standardize skills', or any request to systematically review or - improve a SKILL.md file. Validates all seven consistency dimensions: frontmatter block + improve a SKILL.md file. Validates all nine consistency dimensions: frontmatter block scalars, title format, reference table headers, bundled-file naming, H1 headings, line - count, and bundled-asset layout. Provides step-by-step fix instructions for each - violation. Do NOT use for creating new skills from scratch — use skill-creator instead. + count, bundled-asset layout, lazy reference loading, and batch tool calls. Provides + step-by-step fix instructions for each violation. Do NOT use for creating new skills + from scratch — use skill-creator instead. --- # Maintain Skills Skill Audit, standardize, and improve existing skills. This skill provides exact rules and -commands so any model — including haiku — can maintain skills mechanically. +commands so any model — including haiku — can maintain skills mechanically. Covers nine +consistency dimensions including lazy reference loading and batch tool call guidance. Skills live under a skills root directory. The canonical path in this repo is `.claude/skills/` (Claude Code / Cowork default); older layouts used @@ -49,9 +51,9 @@ reference table (dimension 3). No orphans, regardless of directory. --- -## The seven consistency dimensions +## The nine consistency dimensions -Every skill MUST pass all seven checks. These are the primary audit criteria. +Every skill MUST pass all nine checks. These are the primary audit criteria. ### Dimension 1 — Frontmatter block scalar @@ -218,80 +220,119 @@ done ``` Output must be empty. ---- +### Dimension 8 — Lazy reference loading -## Full verification script +Skills MUST load reference files **on demand**, not eagerly. SKILL.md MUST contain a +**conditional loading table** (or decision tree) that maps task contexts to specific +references. The most common use case MUST be handled by inline guidance in SKILL.md +itself, so simple tasks require **zero** reference file loads. -Run this after ANY skill edit. All seven dimensions in one pass. Set `SKILLS_DIR` -to match your layout (`.claude/skills` or `template/.claude/skills`). +**Pattern — conditional loading table:** -```bash -SKILLS_DIR="${SKILLS_DIR:-.claude/skills}" +```markdown +## When to load references + +| If the task involves… | Load | +|--------------------------------------|------------------------------------| +| Writing async tests | `references/async-testing.md` | +| Using mocks or test doubles | `references/mocking.md` | +| Fixture scopes or factory fixtures | `references/fixtures.md` | +| Simple test writing (default) | No reference needed — use inline | +``` -echo "=== 1. Frontmatter block scalar ===" -for f in "$SKILLS_DIR"/*/SKILL.md; do - skill=$(basename "$(dirname "$f")") - scalar=$(grep 'description:' "$f" | head -1 | sed 's/.*description: *//') - echo " $skill: $scalar" -done +**Pattern — decision tree (alternative):** -echo "" -echo "=== 2. Title format ===" -for f in "$SKILLS_DIR"/*/SKILL.md; do - skill=$(basename "$(dirname "$f")") - echo " $skill: $(grep '^# ' "$f" | head -1)" -done +```markdown +## Reference loading + +├── Writing a new test file? +│ └── Use inline guidance below (AAA, naming, assertions) +├── Need fixtures beyond basic `@pytest.fixture`? +│ └── Load `references/fixtures.md` +├── Need mocking guidance? +│ └── Load `references/mocking.md` +└── Debugging flaky tests? + └── Load `references/anti-patterns.md` +``` + +**Rules:** + +- Never instruct "Load reference X" without a condition or context. +- The words "read", "load", or "consult" for a reference file MUST be preceded by + a conditional ("if", "when", "for ``"). +- Inline guidance in SKILL.md must cover the 80% case. References are for depth, + not basics. +- Skills with a single reference file: the condition can be simple ("For the full + config reference, see…") but must still exist — never "always load X first". +- Orchestrator skills (sdlc-workflow, tdd-workflow) load sub-skill SKILL.md files + at the **start of the stage that needs them**, not all upfront. Each stage's load + instruction is itself a conditional. -echo "" -echo "=== 3. Reference table header ===" +**Check:** +```bash for f in "$SKILLS_DIR"/*/SKILL.md; do skill=$(basename "$(dirname "$f")") - header=$(grep 'where to go deeper' "$f" || echo "(NOT FOUND)") - echo " $skill: $header" + # Find unconditional "load/read" instructions for reference files + hits=$(grep -n -iE '(^[0-9]+\.|^- ).*\b(load|read)\b.*references/' "$f" \ + | grep -viE '(if |when |for |only|conditional)' || true) + [ -n "$hits" ] && echo " $skill: unconditional reference loads found:" \ + && echo "$hits" done +``` +Ideally returns empty — all reference loads are conditional. -echo "" -echo "=== 4. Bundled file + folder naming (non-kebab?) ===" -bad_folders=$(ls -d "$SKILLS_DIR"/*/ 2>/dev/null | grep -E '[_A-Z]' || true) -bad_files=$(find "$SKILLS_DIR"/*/references "$SKILLS_DIR"/*/templates \ - "$SKILLS_DIR"/*/assets -type f \ - \( -name '[0-9]*' -o -name '*_*' -o -name '[A-Z]*' \) 2>/dev/null || true) -[ -z "$bad_folders$bad_files" ] && echo " None found ✓" \ - || { echo " folders: $bad_folders"; echo " files: $bad_files"; } - -echo "" -echo "=== 5. Reference H1 headings ===" -for f in "$SKILLS_DIR"/*/references/*.md; do - [ -f "$f" ] || continue - skill=$(basename "$(dirname "$(dirname "$f")")") - echo " $skill/$(basename "$f"): $(head -1 "$f")" -done +### Dimension 9 — Batch tool calls -echo "" -echo "=== 6. SKILL.md line counts ===" -wc -l "$SKILLS_DIR"/*/SKILL.md +Skills that involve editing files or running commands MUST include explicit **batch +edit guidance** instructing the executing model to minimize API calls. -echo "" -echo "=== 7. Bundled asset layout (no mixing templates/ and assets/templates/) ===" -for dir in "$SKILLS_DIR"/*/; do - skill=$(basename "$dir") - if [ -d "$dir/templates" ] && [ -d "$dir/assets/templates" ]; then - echo " $skill: MIXED LAYOUT" +**Required guidance block** — add this verbatim or adapted to the skill's domain: + +```markdown +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When making multiple changes to the same file, combine them + into a single Edit tool call where possible. Do not make incremental + single-line changes. +- **Parallel calls:** When running independent checks (e.g., lint + type-check), + make all tool calls in a single message rather than sequentially. +- **Read before edit:** Read each file once, plan all changes, then apply them + in the fewest possible Edit calls. +``` + +**Rules:** + +- Every skill that instructs the model to edit files (write code, fix issues, + apply docstrings, etc.) MUST contain batch edit guidance. +- Every skill that instructs the model to run multiple independent commands + MUST mention parallel execution. +- Orchestrator skills (sdlc-workflow, tdd-workflow) that launch Agent calls + already handle parallelism via multiple Agent invocations — add batch guidance + for within-agent edits. +- Skills that are read-only or informational (no file edits, no command runs) + are exempt. + +**Check:** +```bash +for f in "$SKILLS_DIR"/*/SKILL.md; do + skill=$(basename "$(dirname "$f")") + # Check for batch/parallel guidance + has_batch=$(grep -cilE '(batch edit|parallel call|single edit|minimize.*call)' \ + "$f" || true) + has_edits=$(grep -cilE '(edit|write|fix|apply|create file|modify)' "$f" || true) + if [ "$has_edits" -gt 0 ] && [ "$has_batch" -eq 0 ]; then + echo " $skill: edits files but has no batch guidance" fi done ``` +Ideally returns empty — all editing skills have batch guidance. + +--- -**Pass criteria:** ALL of these must be true: +## Full verification script -- Dimension 1: every skill shows `>-` -- Dimension 2: every title ends with ` Skill` -- Dimension 3: every skill has exactly one `where to go deeper` match, and every - file under `references/`, `templates/`, `assets/` is linked from that table -- Dimension 4: every folder and bundled file is kebab-case (no numbers, no - underscores, no uppercase) -- Dimension 5: every H1 heading is sentence case -- Dimension 6: every SKILL.md is under 400 lines -- Dimension 7: no skill mixes top-level `templates/` with `assets/templates/` +Run the script in [references/verification-script.md](references/verification-script.md) +after ANY skill edit. It checks all nine dimensions in one pass. --- @@ -301,11 +342,32 @@ entire skill repositories, SKILL.md structure, and writing style guidelines, see --- +## When to load references + +| If the task involves… | Load | +|---------------------------------------------|-----------------------------------| +| Step-by-step standardization of one skill | `references/fixing-skills.md` | +| Full 11-area deep audit of a skill | `references/audit-checklist.md` | +| Before/after fix examples for reference | `references/audit-examples.md` | +| Logging a completed audit session | `references/maintenance-log.md` | +| Running the 9-dimension verification check | `references/verification-script.md` | +| Quick dimension check (default) | No reference needed — use inline | + +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When fixing multiple dimensions in a single SKILL.md, combine + all fixes (frontmatter, title, references, etc.) into a single Edit call. +- **Parallel audits:** When auditing multiple skills, run dimension checks for + independent skills in parallel. +- **Read before edit:** Read the full SKILL.md once, note all violations, then + apply all fixes in the fewest Edit calls possible. + ## Quick reference: where to go deeper | Topic | Reference file | |----------------------------------------------------|----------------------------------------------------------------------| | Step-by-step fixing, auditing, and style guide | [references/fixing-skills.md](references/fixing-skills.md) | -| Full 9-area deep audit checklist | [references/audit-checklist.md](references/audit-checklist.md) | +| Full 11-area deep audit checklist | [references/audit-checklist.md](references/audit-checklist.md) | | Annotated before/after fix examples | [references/audit-examples.md](references/audit-examples.md) | | Running log of all audit sessions | [references/maintenance-log.md](references/maintenance-log.md) | +| Full verification script (all 9 dimensions) | [references/verification-script.md](references/verification-script.md) | diff --git a/.claude/skills/maintain-skills/references/audit-checklist.md b/.claude/skills/maintain-skills/references/audit-checklist.md index e8535db..89cb317 100644 --- a/.claude/skills/maintain-skills/references/audit-checklist.md +++ b/.claude/skills/maintain-skills/references/audit-checklist.md @@ -5,6 +5,8 @@ thorough reviews, repo-wide sweeps, or when unsure about a specific quality area The six consistency dimensions (in SKILL.md) are the minimum bar. This checklist goes deeper into content quality, technical accuracy, and cross-skill health. +It also covers the two efficiency dimensions (lazy loading and batch guidance) +introduced as dimensions 8 and 9. --- @@ -195,9 +197,79 @@ Quick pass — flag anything surprising for human review: --- +## Area 10 — Lazy reference loading (dimension 8) + +Verify that reference files are loaded on demand, not eagerly. + +**Audit steps:** + +1. Grep for all "load", "read", "consult" instructions targeting reference files: +```bash +grep -n -iE '\b(load|read|consult)\b.*references/' "$SKILL_DIR/SKILL.md" +``` + +2. For each match, check whether it has a conditional trigger (if/when/for): +```bash +grep -n -iE '\b(load|read|consult)\b.*references/' "$SKILL_DIR/SKILL.md" \ + | grep -viE '(if |when |for |only|conditional)' +``` +Any remaining lines are unconditional loads — flag as MED severity. + +3. Check for a conditional loading table or decision tree: +```bash +grep -c 'If the task involves\|When to load\|Reference loading' "$SKILL_DIR/SKILL.md" +``` +If zero, flag as MED — skill needs a conditional loading table. + +4. Verify the 80% case is covered inline. Read the SKILL.md body content: + - Does it contain enough guidance to handle the most common task without + loading any reference file? + - Or does every workflow step require reading a reference first? + +**Red flags:** +- "Always load X before starting" — should be "If doing X, load…" +- Stage 1 of an orchestrator loading all sub-skills upfront +- Reference table exists but no conditional loading guidance above it +- All reference files are mentioned as mandatory reads in the workflow + +--- + +## Area 11 — Batch tool call guidance (dimension 9) + +Verify that skills which edit files include batch edit guidance. + +**Audit steps:** + +1. Determine if the skill involves file editing or command running: +```bash +grep -cilE '(edit|write|fix|apply|create file|modify|run |execute)' \ + "$SKILL_DIR/SKILL.md" +``` +If zero, this dimension does not apply — skill is read-only/informational. + +2. Check for batch guidance: +```bash +grep -cilE '(batch edit|parallel call|single edit|minimize.*call|combine.*edit)' \ + "$SKILL_DIR/SKILL.md" +``` +If zero and step 1 matched, flag as MED — skill needs batch guidance. + +3. If present, verify the guidance covers all three aspects: + - **Batch edits** — combining changes to the same file + - **Parallel calls** — independent commands in one message + - **Read before edit** — plan all changes first + +**Red flags:** +- Skill has step-by-step instructions that say "edit line X… now edit line Y… + now edit line Z…" for the same file +- Multiple sequential independent commands with no mention of parallelism +- No "read the file first" instruction before a series of edits + +--- + ## Producing an audit report -After completing all 9 areas: +After completing all 11 areas: ```markdown ## Skill audit: diff --git a/.claude/skills/maintain-skills/references/fixing-skills.md b/.claude/skills/maintain-skills/references/fixing-skills.md index 9da717e..54c26f2 100644 --- a/.claude/skills/maintain-skills/references/fixing-skills.md +++ b/.claude/skills/maintain-skills/references/fixing-skills.md @@ -83,9 +83,77 @@ Add a row to the quick-reference table for each new file. If the skill has both `templates/` (top-level) and `assets/templates/`, consolidate into one. Prefer `assets/templates/` when the skill also ships other asset categories (icons, fonts, settings starters); prefer top-level `templates/` for simple skills that only ship a handful of Markdown starters. -### Step 10 — Run verification +### Step 10 — Fix lazy reference loading (dimension 8) -Run the full verification script from the main skill. ALL seven dimensions must pass. +Audit every line in SKILL.md that says "load", "read", or "consult" a reference file. +Each must have a conditional trigger. Fix unconditional loads: + +**Before (unconditional):** +```markdown +1. Load the `pytest` skill (read `.claude/skills/pytest/SKILL.md`). +2. For fixture patterns, read `.claude/skills/pytest/references/fixtures.md`. + For mocking guidance, read `.claude/skills/pytest/references/mocking.md`. +``` + +**After (conditional):** +```markdown +1. Load the `pytest` skill (read `.claude/skills/pytest/SKILL.md`). +2. If writing tests that need fixtures beyond basic `@pytest.fixture`: + read `.claude/skills/pytest/references/fixtures.md`. + If using mocks or test doubles: + read `.claude/skills/pytest/references/mocking.md`. + Otherwise: use inline guidance from the pytest skill's core principles. +``` + +For skills with many references, create a **conditional loading table**: + +```markdown +## When to load references + +| If the task involves… | Load | +|------------------------------------|---------------------------------| +| | `references/a.md` | +| | `references/b.md` | +| Simple / default case | No reference needed — use inline | +``` + +**Rules for the 80% case:** The most common use case must be covered by inline +guidance already in SKILL.md. A user doing the most typical task should never need +to load a reference file. References are for depth, advanced cases, and edge cases. + +For orchestrator skills (sdlc-workflow, tdd-workflow): ensure each stage loads its +dependencies at the start of that stage, not all upfront. Each stage's load +instruction must include "if entering this stage" as the condition. + +### Step 11 — Fix batch tool call guidance (dimension 9) + +If the skill instructs the model to edit files or run commands, add a batch guidance +section. Use the standard block or adapt it to the skill's domain: + +```markdown +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When making multiple changes to the same file, combine them + into a single Edit tool call where possible. Do not make incremental + single-line changes. +- **Parallel calls:** When running independent checks (e.g., lint + type-check), + make all tool calls in a single message rather than sequentially. +- **Read before edit:** Read each file once, plan all changes, then apply them + in the fewest possible Edit calls. +``` + +Place this section after the main workflow but before the "Quick reference" table. + +For skills that are read-only or purely informational (no file edits, no command +runs), this dimension does not apply — skip it. + +For orchestrator skills: add batch guidance for within-agent edits. The Agent-level +parallelism is already handled by multiple Agent calls — the batch guidance targets +edits within each agent's scope. + +### Step 12 — Run verification + +Run the full verification script from the main skill. ALL nine dimensions must pass. --- @@ -132,8 +200,10 @@ When restructuring a SKILL.md, use this standard order: 2. `# Skill` title 3. 1-3 line intro paragraph 4. Core content sections (main workflows, principles, checklists) -5. `## Quick reference: where to go deeper` (reference table covering every file under `references/`, `templates/`, and `assets/`) -6. Optional: `## Bundled scripts` (if `scripts/` exists) +5. Conditional reference loading table or decision tree (dimension 8) +6. Efficiency: batch edits and parallel calls (dimension 9, if applicable) +7. `## Quick reference: where to go deeper` (reference table covering every file under `references/`, `templates/`, and `assets/`) +8. Optional: `## Bundled scripts` (if `scripts/` exists) --- diff --git a/.claude/skills/maintain-skills/references/maintenance-log.md b/.claude/skills/maintain-skills/references/maintenance-log.md index 03dfe84..303c8a2 100644 --- a/.claude/skills/maintain-skills/references/maintenance-log.md +++ b/.claude/skills/maintain-skills/references/maintenance-log.md @@ -17,3 +17,5 @@ Track every audit session here. Append a row after each review. | Date | Scope | Skills Reviewed | Issues Found | Issues Fixed | Notes | |------|-------|----------------|--------------|--------------|-------| | 2026-04-09 | Repo-wide | skill-maintainer | 4H, 5M, 4L | 4H, 5M, 4L | Initial creation + full audit of own skill | +| 2026-04-16 | Repo-wide | all 24 skills | 3H, 16M, 4L | 0H, 0M, 0L | Added dimensions 8 (lazy ref loading) + 9 (batch tool calls) to standards. Plans created for all 24 skills in docs/skill-optimization-plans.md. Implementation deferred. | +| 2026-04-16 | single: prepare-pr | prepare-pr | 1H, 0M, 0L | 1H, 0M, 0L | Renamed prepare-pr → prepare-pr (dim 4: kebab-case). Updated name: frontmatter and sdlc-workflow reference. | diff --git a/.claude/skills/maintain-skills/references/verification-script.md b/.claude/skills/maintain-skills/references/verification-script.md new file mode 100644 index 0000000..18d7dc4 --- /dev/null +++ b/.claude/skills/maintain-skills/references/verification-script.md @@ -0,0 +1,100 @@ +# Verification script + +Run this after ANY skill edit. All nine dimensions in one pass. Set `SKILLS_DIR` +to match your layout (`.claude/skills` or `template/.claude/skills`). + +```bash +SKILLS_DIR="${SKILLS_DIR:-.claude/skills}" + +echo "=== 1. Frontmatter block scalar ===" +for f in "$SKILLS_DIR"/*/SKILL.md; do + skill=$(basename "$(dirname "$f")") + scalar=$(grep 'description:' "$f" | head -1 | sed 's/.*description: *//') + echo " $skill: $scalar" +done + +echo "" +echo "=== 2. Title format ===" +for f in "$SKILLS_DIR"/*/SKILL.md; do + skill=$(basename "$(dirname "$f")") + echo " $skill: $(grep '^# ' "$f" | head -1)" +done + +echo "" +echo "=== 3. Reference table header ===" +for f in "$SKILLS_DIR"/*/SKILL.md; do + skill=$(basename "$(dirname "$f")") + header=$(grep 'where to go deeper' "$f" || echo "(NOT FOUND)") + echo " $skill: $header" +done + +echo "" +echo "=== 4. Bundled file + folder naming (non-kebab?) ===" +bad_folders=$(ls -d "$SKILLS_DIR"/*/ 2>/dev/null | grep -E '[_A-Z]' || true) +bad_files=$(find "$SKILLS_DIR"/*/references "$SKILLS_DIR"/*/templates \ + "$SKILLS_DIR"/*/assets -type f \ + \( -name '[0-9]*' -o -name '*_*' -o -name '[A-Z]*' \) 2>/dev/null || true) +[ -z "$bad_folders$bad_files" ] && echo " None found ✓" \ + || { echo " folders: $bad_folders"; echo " files: $bad_files"; } + +echo "" +echo "=== 5. Reference H1 headings ===" +for f in "$SKILLS_DIR"/*/references/*.md; do + [ -f "$f" ] || continue + skill=$(basename "$(dirname "$(dirname "$f")")") + echo " $skill/$(basename "$f"): $(head -1 "$f")" +done + +echo "" +echo "=== 6. SKILL.md line counts ===" +wc -l "$SKILLS_DIR"/*/SKILL.md + +echo "" +echo "=== 7. Bundled asset layout (no mixing templates/ and assets/templates/) ===" +for dir in "$SKILLS_DIR"/*/; do + skill=$(basename "$dir") + if [ -d "$dir/templates" ] && [ -d "$dir/assets/templates" ]; then + echo " $skill: MIXED LAYOUT" + fi +done + +echo "" +echo "=== 8. Lazy reference loading (unconditional loads?) ===" +for f in "$SKILLS_DIR"/*/SKILL.md; do + skill=$(basename "$(dirname "$f")") + hits=$(grep -n -iE '(^[0-9]+\.|^- ).*\b(load|read)\b.*references/' "$f" \ + | grep -viE '(if |when |for |only|conditional)' || true) + [ -n "$hits" ] && echo " $skill: unconditional reference loads:" \ + && echo "$hits" +done + +echo "" +echo "=== 9. Batch tool call guidance ===" +for f in "$SKILLS_DIR"/*/SKILL.md; do + skill=$(basename "$(dirname "$f")") + has_batch=$(grep -cilE '(batch edit|parallel call|single edit|minimize.*call)' \ + "$f" || true) + has_edits=$(grep -cilE '(edit|write|fix|apply|create file|modify)' "$f" || true) + if [ "$has_edits" -gt 0 ] && [ "$has_batch" -eq 0 ]; then + echo " $skill: edits files but has no batch guidance" + fi +done +``` + +## Pass criteria + +ALL of these must be true: + +- Dimension 1: every skill shows `>-` +- Dimension 2: every title ends with ` Skill` +- Dimension 3: every skill has exactly one `where to go deeper` match, and every + file under `references/`, `templates/`, `assets/` is linked from that table +- Dimension 4: every folder and bundled file is kebab-case (no numbers, no + underscores, no uppercase) +- Dimension 5: every H1 heading is sentence case +- Dimension 6: every SKILL.md is under 400 lines +- Dimension 7: no skill mixes top-level `templates/` with `assets/templates/` +- Dimension 8: no unconditional reference loads — every "load/read reference" + instruction has a conditional trigger +- Dimension 9: every skill that edits files or runs commands has batch edit + and/or parallel call guidance diff --git a/.claude/skills/markdown/SKILL.md b/.claude/skills/markdown/SKILL.md index 1dbb651..20df063 100644 --- a/.claude/skills/markdown/SKILL.md +++ b/.claude/skills/markdown/SKILL.md @@ -29,9 +29,18 @@ readable, portable, accessible, and maintainable over time. | File management and doc systems | [references/file-management.md](references/file-management.md) | | Anti-patterns and cheat sheet | [references/anti-patterns-cheatsheet.md](references/anti-patterns-cheatsheet.md) | -Read the relevant reference file before working on a specific area. For a new README, -skim `document-structure.md` first. For table formatting or GFM features, check the -corresponding reference. +## When to load references + +| If the task involves… | Load | +|--------------------------------------------|--------------------------------------------| +| Front matter, document skeleton, headings | `references/document-structure.md` | +| Lists, emphasis, whitespace formatting | `references/formatting-syntax.md` | +| Code blocks, fenced blocks, links | `references/code-and-links.md` | +| Tables, images, or inline HTML | `references/tables-images-html.md` | +| GFM features, callouts, task lists | `references/extended-syntax.md` | +| Doc systems, multi-file docs, wikis | `references/file-management.md` | +| Common mistakes quick reference | `references/anti-patterns-cheatsheet.md` | +| Writing a basic README or doc (default) | No reference needed — use inline guidance | --- @@ -171,6 +180,13 @@ Strongly prefer plain Markdown over HTML. Acceptable uses: `
`, `
`, --- +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When restructuring a Markdown file (fixing headings, lists, + and links), plan all changes first, then apply them in a single Edit call. +- **Read before edit:** Read the full file once to understand its structure before + making any changes. Apply all edits in the fewest calls possible. + ## See Also - [Google Markdown Style Guide](https://google.github.io/styleguide/docguide/style.html) diff --git a/.claude/skills/prepare_pr/SKILL.md b/.claude/skills/prepare-pr/SKILL.md similarity index 92% rename from .claude/skills/prepare_pr/SKILL.md rename to .claude/skills/prepare-pr/SKILL.md index 38444e8..8afaf41 100644 --- a/.claude/skills/prepare_pr/SKILL.md +++ b/.claude/skills/prepare-pr/SKILL.md @@ -1,5 +1,5 @@ --- -name: pr-template +name: prepare-pr description: >- Enforce a strict PR (pull request) description template every time one is generated, written, or drafted. Use this skill whenever the user asks to: generate a PR, write a PR description, draft a pull request, @@ -52,8 +52,14 @@ Scan everything the user provided. Find these signals: ## STEP 3 — Fill each section -Read `references/section-rules.md` now. -It contains exact fill instructions, fallback text, and examples for every section. +If the PR template has custom sections or you need exact fill rules for each +section, read `references/section-rules.md`. For standard PRs with Summary, +Test Plan, and Breaking Changes sections, use the inline template below: + +> **Summary:** [2-3 bullets on what changed and why] +> **Test plan:** [bullets: what to test and how] +> **Breaking changes:** [None / list of breaking changes] + Work through each section **one at a time, in template order**. --- diff --git a/.claude/skills/prepare_pr/references/section-rules.md b/.claude/skills/prepare-pr/references/section-rules.md similarity index 100% rename from .claude/skills/prepare_pr/references/section-rules.md rename to .claude/skills/prepare-pr/references/section-rules.md diff --git a/.claude/skills/pytest/SKILL.md b/.claude/skills/pytest/SKILL.md index 7d88f6f..a89fb57 100644 --- a/.claude/skills/pytest/SKILL.md +++ b/.claude/skills/pytest/SKILL.md @@ -36,9 +36,19 @@ topic. | `find_slow_tests.py` | Runs pytest, identifies tests exceeding a time threshold | | `mark_slow_tests.py` | Adds `@pytest.mark.slow` to the identified slow tests | -Read the relevant reference file before working on a specific area. For a new test file, -skim `test-organization.md` and `fixtures.md` first. For debugging flaky tests, start -with `anti-patterns.md`. +## When to load references + +| If the task involves… | Load | +|------------------------------------------|-------------------------------------------------| +| Fixture scopes, yield, DI, factories | `references/fixtures.md` | +| Parametrize, custom markers, xfail | `references/parametrize-and-markers.md` | +| Mocking, monkeypatch, test doubles | `references/mocking.md` | +| Complex assertions, approx, raises | `references/assertions.md` | +| Unit vs integration vs e2e decisions | `references/test-types.md` | +| Project layout, naming, conftest | `references/test-organization.md` | +| Debugging flaky tests, common mistakes | `references/anti-patterns.md` | +| CI config, coverage, plugins | `references/ci-and-plugins.md` | +| Simple test writing (default) | No reference needed — use inline guidance below | --- @@ -136,7 +146,7 @@ Key rules for fixtures: - Use `yield` for setup + teardown in a single function. - Use `tmp_path` for filesystem operations — never hardcode `/tmp` paths. -Read [references/fixtures.md](references/fixtures.md) for scopes, autouse, factory +For advanced fixture patterns, read [references/fixtures.md](references/fixtures.md) for scopes, autouse, factory fixtures, and advanced patterns. ### Parametrize to cover multiple inputs @@ -155,7 +165,7 @@ def test_email_validation(email: str, is_valid: bool) -> None: assert validate_email(email) == is_valid ``` -Each parameter set runs as a separate test, so failures are pinpointed. Read +Each parameter set runs as a separate test, so failures are pinpointed. For advanced parametrization, read [references/parametrize-and-markers.md](references/parametrize-and-markers.md) for advanced parametrization and custom markers. @@ -177,7 +187,7 @@ def test_sends_notification_on_order(mocker): ) ``` -Read [references/mocking.md](references/mocking.md) for `monkeypatch`, `pytest-mock`, +For advanced mocking patterns, read [references/mocking.md](references/mocking.md) for `monkeypatch`, `pytest-mock`, factory patterns, and when *not* to mock. ### Test isolation is non-negotiable @@ -328,6 +338,17 @@ for CI integration patterns. --- +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When adding multiple test functions to the same file, write + them all in a single Edit tool call rather than one function at a time. +- **Parallel calls:** When running `just test` and `just coverage` independently, + make both calls in a single message. +- **Read before edit:** Read the target test file and the source module once each, + plan all test functions, then write them in one Edit call. + +--- + ## Checklist before committing tests - [ ] Each test has a clear, descriptive name. diff --git a/.claude/skills/python-code-quality/SKILL.md b/.claude/skills/python-code-quality/SKILL.md index 023c24d..e670331 100644 --- a/.claude/skills/python-code-quality/SKILL.md +++ b/.claude/skills/python-code-quality/SKILL.md @@ -73,6 +73,18 @@ ruff format --check → ruff check → bandit → semgrep → basedpyrig --- +## When to load references + +| If the task involves… | Load | +|----------------------------------------|-----------------------------------| +| Configuring or debugging ruff | `references/ruff.md` | +| Setting up or fixing pre-commit hooks | `references/pre-commit.md` | +| Type errors or basedpyright config | `references/basedpyright.md` | +| Security scan findings (bandit) | `references/bandit.md` | +| Custom semgrep rules | `references/semgrep.md` | +| Complete config file examples | `references/complete-configs.md` | +| Running `just lint`/`just fix` (default) | No reference needed — use inline | + ## Quick reference: where to go deeper Read the relevant reference file for configuration details, error code explanations, @@ -89,6 +101,15 @@ or CI integration specifics: --- +## Efficiency: batch edits and parallel calls + +- **Parallel calls:** Run independent checks (`ruff check`, `basedpyright`, + `bandit`) in parallel as separate tool calls in a single message. +- **Batch edits:** When fixing multiple lint violations in the same file, combine + all fixes into a single Edit tool call. +- **CI ordering:** Follow the fast-fail order (format → lint → bandit → semgrep → + type → test) but run independent stages in parallel where possible. + ## Adding a new tool 1. Create `references/.md` using the template below. diff --git a/.claude/skills/python-code-reviewer/SKILL.md b/.claude/skills/python-code-reviewer/SKILL.md index fddb85c..58f62d1 100644 --- a/.claude/skills/python-code-reviewer/SKILL.md +++ b/.claude/skills/python-code-reviewer/SKILL.md @@ -140,7 +140,9 @@ For the sub-items, load `references/checklist.md`. Do not duplicate checklist co ## Phase 3 — Output the Review -Load `references/output-format.md` and select the matching template: +If you need the exact output template format, load `references/output-format.md` +and select the matching template. For Quick mode reviews, use this compact format: +> **Verdict: [APPROVE/REQUEST CHANGES]** — [1-line summary]. Findings: [list]. - **Template A** — snippet / function / module (most common) - **Template B** — PR / diff review - **Template C** — full module audit (Deep mode only) diff --git a/.claude/skills/python-docstrings/SKILL.md b/.claude/skills/python-docstrings/SKILL.md index b357a9d..1c1fc5b 100644 --- a/.claude/skills/python-docstrings/SKILL.md +++ b/.claude/skills/python-docstrings/SKILL.md @@ -80,6 +80,16 @@ Docstrings are narrative text. Use proper capitalization, punctuation, and compl --- +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When adding docstrings to multiple functions in the same file, + combine all docstring additions into a single Edit tool call. Do not make one + edit per function. +- **Read before edit:** Read the entire file first, identify all constructs needing + docstrings, draft all docstrings mentally, then apply in one Edit call per file. +- **Multi-file tasks:** When auditing multiple files, read all target files first, + then edit each file once with all its docstrings in a single call. + ## Quick reference: where to go deeper | Topic | Reference file | diff --git a/.claude/skills/sdlc-workflow/SKILL.md b/.claude/skills/sdlc-workflow/SKILL.md index d243800..ddb4853 100644 --- a/.claude/skills/sdlc-workflow/SKILL.md +++ b/.claude/skills/sdlc-workflow/SKILL.md @@ -53,7 +53,7 @@ Stage Name Model Execution Sub-skills 5 SECURITY Haiku Agent (parallel) security 6 DOCUMENTATION Haiku Agent (parallel) python-docstrings, markdown 7 COMMIT + CHANGELOG Haiku Agent (sequential) (inline guidance) - 8 PULL REQUEST Haiku Agent (sequential) prepare_pr + 8 PULL REQUEST Haiku Agent (sequential) prepare-pr 9 SUMMARY Haiku Agent (sequential) (task_summary_template.md) ``` @@ -91,8 +91,11 @@ Gate: all pre-flight checks pass. No user approval needed. **Model:** Full (main model, interactive) 1. Load the `pytest` skill (read `.claude/skills/pytest/SKILL.md`). -2. For fixture patterns, read `.claude/skills/pytest/references/fixtures.md`. - For mocking guidance, read `.claude/skills/pytest/references/mocking.md`. +2. If writing tests that use fixtures beyond basic `@pytest.fixture`: + read `.claude/skills/pytest/references/fixtures.md`. + If writing tests that need mocks or monkeypatch: + read `.claude/skills/pytest/references/mocking.md`. + Otherwise: use inline guidance from the pytest skill's core principles. 3. For each acceptance criterion, draft the smallest test: - Name: `test__when_` - Structure: AAA (Arrange-Act-Assert) @@ -243,7 +246,7 @@ Report: commit hash and message. **Model:** Haiku (Agent call, sequential after stage 7) ``` -Load the prepare_pr skill (.claude/skills/prepare_pr/SKILL.md). +Load the prepare-pr skill (.claude/skills/prepare-pr/SKILL.md). 1. Read .github/PULL_REQUEST_TEMPLATE.md (if exists). 2. Extract signals from commits: git log main..HEAD, git diff main...HEAD --stat. @@ -289,6 +292,18 @@ Gate: file exists at `tasks_summary/TASK_ID_summary.md`. --- +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When applying multiple changes to the same implementation or + test file (e.g., adding docstrings to several functions), combine them into + a single Edit tool call. +- **Parallel calls:** Stages 4, 5, and 6 already run as parallel Agent calls. + Within each agent, run independent lint/type/security commands in parallel. +- **Read before edit:** In GREEN and REFACTOR stages, read each target file once, + plan all changes, then apply in the fewest Edit calls possible. + +--- + ## Error handling | Stage | On failure | diff --git a/.claude/skills/sdlc-workflow/scripts/validate_dor.py b/.claude/skills/sdlc-workflow/scripts/validate_dor.py index 31d47b8..0ee80b8 100644 --- a/.claude/skills/sdlc-workflow/scripts/validate_dor.py +++ b/.claude/skills/sdlc-workflow/scripts/validate_dor.py @@ -97,12 +97,12 @@ def main() -> None: errors = validate(sys.argv[1]) if errors: - logger.error("FAIL -- Definition of Ready not met:") + logger.error("Definition of Ready not met:") for err in errors: logger.error(f" - {err}") sys.exit(1) else: - logger.info("PASS -- Definition of Ready met") + logger.info("Definition of Ready met") sys.exit(0) diff --git a/.claude/skills/security/SKILL.md b/.claude/skills/security/SKILL.md index de623d7..8c9a5a1 100644 --- a/.claude/skills/security/SKILL.md +++ b/.claude/skills/security/SKILL.md @@ -35,6 +35,13 @@ When reviewing code, check for: | Path traversal | user_input validated against base directory; use `.resolve()` | | Error handling | Error messages don't expose internal paths or config values | +## Efficiency: batch edits and parallel calls + +- **Parallel calls:** Run `bandit` and `semgrep` scans in parallel as + independent tool calls in a single message. +- **Batch edits:** When fixing multiple security findings in the same file, + combine all fixes into a single Edit call. + ## Quick reference: where to go deeper | Topic | Reference file | diff --git a/.claude/skills/tdd-test-planner/SKILL.md b/.claude/skills/tdd-test-planner/SKILL.md index 3c032fe..229bd26 100644 --- a/.claude/skills/tdd-test-planner/SKILL.md +++ b/.claude/skills/tdd-test-planner/SKILL.md @@ -100,16 +100,14 @@ floats. #### E — Integration Points Interactions with external collaborators (DB, API, filesystem, message queue, -another module). One test per meaningful interaction. Load -`references/test-doubles.md` to choose the right test double (stub/mock/fake). +another module). One test per meaningful interaction. If the test plan requires mocks, stubs, or fakes, load `references/test-doubles.md` to choose the right test double (stub/mock/fake). --- ## Step 4 — Select pytest mechanics **Fixtures:** Use for any setup shared across 2+ tests. Fixtures used across -multiple test files belong in `conftest.py` — not inline. See -`references/pytest-patterns.md` for conftest layout. +multiple test files belong in `conftest.py` — not inline. If you need specific pytest patterns (parametrize, fixtures, markers), load `references/pytest-patterns.md` for conftest layout and other patterns. **Parametrize:** Use when the same assertion logic applies to multiple input/output pairs. Always add human-readable `ids=` so test output says diff --git a/.claude/skills/tdd-workflow/SKILL.md b/.claude/skills/tdd-workflow/SKILL.md index b000d33..18335a5 100644 --- a/.claude/skills/tdd-workflow/SKILL.md +++ b/.claude/skills/tdd-workflow/SKILL.md @@ -225,6 +225,17 @@ Never patch first. A bug without a reproducing test will return. --- +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When writing the test file in RED or the implementation in + GREEN, write the complete file content in a single Edit call rather than + adding functions one by one. +- **Read before edit:** Read the target module and existing test file once before + planning changes. Apply all edits in the fewest calls possible. +- **REFACTOR batching:** Group related refactoring changes (e.g., all naming + fixes) into a single Edit call per file. Run tests after each logical group, + not after each individual change. + ## Skill dependencies | Stage | Load first | Fallback (if not installed) | diff --git a/.claude/skills/test-quality-reviewer/SKILL.md b/.claude/skills/test-quality-reviewer/SKILL.md index df171d3..597e6a4 100644 --- a/.claude/skills/test-quality-reviewer/SKILL.md +++ b/.claude/skills/test-quality-reviewer/SKILL.md @@ -15,6 +15,14 @@ description: >- You are auditing pytest test files for quality, correctness, and improvement opportunities. Your goal: produce a structured, actionable report that helps the author build a test suite they can actually trust. +## When to load references + +| If the task involves… | Load | +|-------------------------------------------------|------------------------------------| +| Before/after examples of test improvements | `references/examples.md` | +| Advanced patterns (property testing, snapshot) | `references/advanced-patterns.md` | +| Standard test quality review (default) | No reference needed — use inline | + ## Quick reference: where to go deeper | Topic | Reference file | diff --git a/.claude/skills/type-checking/SKILL.md b/.claude/skills/type-checking/SKILL.md index ce1000f..1318a4e 100644 --- a/.claude/skills/type-checking/SKILL.md +++ b/.claude/skills/type-checking/SKILL.md @@ -50,6 +50,13 @@ See `references/basedpyright.md` for: - Avoid `# type: ignore` without specific error codes. - When suppressing, use: `# type: ignore[error-code]` with an explanation. +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When fixing multiple type errors in the same file, combine + all annotation fixes into a single Edit call. +- **Read before edit:** Read the full file first to understand the type + relationships before adding annotations. + ## Quick reference: where to go deeper | Topic | Reference file | diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 532b8a5..8f8866a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -60,4 +60,4 @@ jobs: run: uv run python scripts/check_root_template_sync.py - name: Pre-commit (all files) - run: uv run pre-commit run --all-files --verbose + run: uv run pre-commit run --all-files diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 010229e..58641fa 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -51,9 +51,9 @@ jobs: - name: Run tests run: | if [ "${{ matrix.python-version }}" = "3.11" ]; then - uv run pytest -q --cov --cov-report=xml --cov-report=term-missing + uv run pytest -q --no-testmon --cov --cov-report=xml --cov-report=term-missing -p no:cacheprovider --cache-clear else - uv run pytest -q + uv run pytest -q --no-testmon -p no:cacheprovider --cache-clear fi - name: Upload coverage XML diff --git a/.gitignore b/.gitignore index e68bb7d..76bde4f 100644 --- a/.gitignore +++ b/.gitignore @@ -222,6 +222,7 @@ mlruns/ data/ .claude/todos/ +.claude/worktrees/ .claude/.refactor-edit-count .cursor/ @@ -246,3 +247,11 @@ task_channel/ runner.sh full_runner.sh + +# ========================================================================== +# PYTEST-TESTMON +# ========================================================================== + +.testmondata +.testmondata-shm +.testmondata-wal diff --git a/CLAUDE.md b/CLAUDE.md index 0a92c6b..57d3f8a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -118,7 +118,7 @@ This runs: `fix` → `check`. `check` bundles: `uv sync --frozen`, `fmt-check`, `ruff check`, `basedpyright`, `sync-check`, `docs-check` (D-only; redundant with `ruff check` for enforcement), `test-ci-matrix` -(same pytest commands as `tests.yml` for 3.11/3.12/3.13), `pre-commit run --all-files --verbose`, `audit` (pip-audit). +(same pytest commands as `tests.yml` for 3.11/3.12/3.13), `pre-commit run --all-files`, `audit` (pip-audit). Together, `lint.yml` + `tests.yml` + `security.yml` mirror these checks on GitHub (CodeQL is GHA-only). All steps must pass before a PR is mergeable. diff --git a/assets/file_freshness.json b/assets/file_freshness.json index f99f533..245a918 100644 --- a/assets/file_freshness.json +++ b/assets/file_freshness.json @@ -50,7 +50,7 @@ { "age_days": 0, "commits_since": 4, - "file": "template/.claude/skills/prepare_pr/SKILL.md", + "file": "template/.claude/skills/prepare-pr/SKILL.md", "last_commit": "2026-04-13T13:46:53+02:00", "last_commit_sha": "011f41e68e92af7662616b8cb19ffd8ff2bd77f5", "status": "green" @@ -58,7 +58,7 @@ { "age_days": 0, "commits_since": 4, - "file": "template/.claude/skills/prepare_pr/references/section-rules.md", + "file": "template/.claude/skills/prepare-pr/references/section-rules.md", "last_commit": "2026-04-13T13:46:53+02:00", "last_commit_sha": "011f41e68e92af7662616b8cb19ffd8ff2bd77f5", "status": "green" diff --git a/assets/root-template-sync-map.yaml b/assets/root-template-sync-map.yaml index f88d2fe..24813ff 100644 --- a/assets/root-template-sync-map.yaml +++ b/assets/root-template-sync-map.yaml @@ -446,8 +446,8 @@ "template": "template/.claude/skills/markdown/SKILL.md" }, { - "root": ".claude/skills/prepare_pr/SKILL.md", - "template": "template/.claude/skills/prepare_pr/SKILL.md" + "root": ".claude/skills/prepare-pr/SKILL.md", + "template": "template/.claude/skills/prepare-pr/SKILL.md" }, { "root": ".claude/skills/pytest/SKILL.md", diff --git a/docs/repo_file_status_report.md b/docs/repo_file_status_report.md index dedc359..4fe87dc 100644 --- a/docs/repo_file_status_report.md +++ b/docs/repo_file_status_report.md @@ -19,8 +19,8 @@ _Metric: **commits**._ - `template/.claude/commands/docs-check.md.jinja` — **5** commits since last change - `template/CLAUDE.md.jinja` — **5** commits since last change - `template/src/{{ package_name }}/common/bump_version.py.jinja` — **5** commits since last change -- `template/.claude/skills/prepare_pr/SKILL.md` — **4** commits since last change -- `template/.claude/skills/prepare_pr/references/section-rules.md` — **4** commits since last change +- `template/.claude/skills/prepare-pr/SKILL.md` — **4** commits since last change +- `template/.claude/skills/prepare-pr/references/section-rules.md` — **4** commits since last change - `template/tests/conftest.py.jinja` — **4** commits since last change - `template/tests/test_imports.py.jinja` — **4** commits since last change - `template/tests/{{ package_name }}/test_core.py.jinja` — **4** commits since last change diff --git a/docs/repo_instructions.md b/docs/repo_instructions.md index 24f540e..ef51241 100644 --- a/docs/repo_instructions.md +++ b/docs/repo_instructions.md @@ -600,7 +600,7 @@ bundle). | `maintain-rules/` | | `maintain-skills/` | | `markdown/` | -| `prepare_pr/` | +| `prepare-pr/` | | `pytest/` | | `python-code-quality/` | | `python-code-reviewer/` | @@ -629,7 +629,7 @@ Run it once per directory, substituting ``. ```bash for d in bash-guide config-management cron-scheduling css-guide html-guide \ jinja-guide linting maintain-commands maintain-hooks maintain-rules \ - maintain-skills markdown prepare_pr pytest python-code-quality \ + maintain-skills markdown prepare-pr pytest python-code-quality \ python-code-reviewer python-docstrings sdlc-workflow security \ tdd-test-planner tdd-workflow test-quality-reviewer type-checking; do diff -rq --exclude='.DS_Store' --exclude='.refactor-edit-count' \ diff --git a/docs/repo_management.md b/docs/repo_management.md index bbca68e..245bc8a 100644 --- a/docs/repo_management.md +++ b/docs/repo_management.md @@ -160,7 +160,7 @@ both are **Copy** (including every `SKILL.md`, `references/*`, `templates/*`, `a Shared skills (Copy in their entirety): `bash-guide/`, `config-management/`, `cron-scheduling/`, `css-guide/`, `html-guide/`, `jinja-guide/`, `linting/`, `maintain-commands/`, `maintain-hooks/`, `maintain-rules/`, `maintain-skills/`, `markdown/`, -`prepare_pr/`, `pytest/`, `python-code-quality/`, `python-code-reviewer/`, +`prepare-pr/`, `pytest/`, `python-code-quality/`, `python-code-reviewer/`, `python-docstrings/`, `sdlc-workflow/`, `security/`, `tdd-test-planner/`, `tdd-workflow/`, `test-quality-reviewer/`, `type-checking/`. diff --git a/justfile b/justfile index d7eb170..bd87a1d 100644 --- a/justfile +++ b/justfile @@ -42,7 +42,7 @@ fix: fmt-check: @uv run ruff format --check . -# Check docstring coverage +# Check docstring coverage docs-check: @echo "=== Docstring coverage check ===" @uv run ruff check --select D . @@ -85,7 +85,7 @@ test-slow: # Run tests in parallel with minimal output test-parallel: - @uv run pytest tests/ -n auto + @uv run pytest tests/ -n auto --no-testmon # Run tests with verbose output (shows test names, INFO logs) test-verbose: @@ -97,7 +97,7 @@ test-debug: # Re-run only the tests that failed in the last run test-lf: - @uv run pytest tests/ --lf + @uv run pytest tests/ --lf --no-testmon # Stop on first test failure (fast feedback) test-first-fail: @@ -105,7 +105,7 @@ test-first-fail: # Run tests for changed (unstaged+staged) Python files only — fast incremental feedback test-changed: - @uv run pytest $(git diff --name-only -- '*.py' | sed 's/src/tests/g') + @uv run pytest --no-testmon $(git diff --name-only -- '*.py' | sed 's/src/tests/g') # Fast unit tests only — excludes slow and integration markers test-fast: @@ -117,11 +117,12 @@ test-integration: # Re-run last failed tests with maximum verbosity — fast debugging loop test-failed-verbose: - @uv run pytest --lf -vv + @uv run pytest --lf -vv --no-testmon # Run tests with coverage report (full HTML/XML/term output) coverage: @uv run pytest tests/ \ + --no-testmon \ --cov \ --cov-report=term-missing \ --cov-report=html \ @@ -129,7 +130,7 @@ coverage: # Test command matching GitHub CI (3.11 matrix leg in .github/workflows/tests.yml) test-ci: - @uv run pytest -q --cov --cov-report=xml --cov-report=term-missing + @uv run pytest -q --no-testmon --cov --cov-report=xml --cov-report=term-missing -p no:cacheprovider # Full tests.yml matrix (3.11 with coverage; 3.12/3.13 with pytest -q only). # 3.11 uses the default project .venv (same as `test-ci`). 3.12/3.13 use @@ -143,13 +144,13 @@ test-ci-matrix: echo "=== Python 3.11 + coverage (tests.yml matrix) ===" unset UV_PROJECT_ENVIRONMENT uv sync --frozen --extra dev --python 3.11 - uv run pytest -q --cov --cov-report=xml --cov-report=term-missing + uv run pytest -q --no-testmon --cov --cov-report=xml --cov-report=term-missing for py in 3.12 3.13; do echo "=== Python ${py} (tests.yml matrix) ===" suffix="${py//./}" export UV_PROJECT_ENVIRONMENT="${ROOT}/.venv-ci-${suffix}" uv sync --frozen --extra dev --python "${py}" - uv run pytest -q + uv run pytest -q --no-testmon done unset UV_PROJECT_ENVIRONMENT uv sync --frozen --extra dev --python 3.11 @@ -184,7 +185,7 @@ audit: # ------------------------------------------------------------------------- sync: _set_env - @uv sync --frozen --extra dev + @uv sync --frozen --extra dev --extra test update: @uv lock --upgrade @@ -266,14 +267,14 @@ clean: # Read-only mirror of GitHub Actions: lint.yml + tests.yml matrix + pip-audit (CodeQL/dep-review are GHA-only). check: - @uv sync --frozen --extra dev + @uv sync --frozen --extra dev --extra test @just fmt-check @uv run ruff check . @uv run basedpyright @just sync-check @just docs-check @just test-ci - @uv run pre-commit run --all-files --verbose + @uv run pre-commit run --all-files # @just audit ci: diff --git a/pyproject.toml b/pyproject.toml index ad40584..3250e53 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,20 +19,24 @@ dependencies = [] # Runtime dependencies go here [project.optional-dependencies] dev = [ - "copier>=9.11.2", - "jinja2-time>=0.2.0", - "pytest>=8.0.0", - "pytest-xdist>=3.5.0", - "pytest-cov>=6.0.0", - "coverage[toml]>=7.6.0", - "ruff>=0.8.0", - "basedpyright>=1.21.0", - "pre-commit>=4.0.0", - "typing-extensions>=4.12.0", - "detect-secrets>=1.5.0", - "commitizen>=4.0.0", + "copier>=9.11.2", + "jinja2-time>=0.2.0", + "ruff>=0.8.0", + "basedpyright>=1.21.0", + "pre-commit>=4.0.0", + "typing-extensions>=4.12.0", + "detect-secrets>=1.5.0", + "commitizen>=4.0.0", ] +test = [ + "pytest>=8.0.0", + "pytest-asyncio>=0.24.0", + "pytest-xdist>=3.5.0", + "pytest-cov>=6.0.0", + "coverage[toml]>=7.6.0", + "pytest-testmon>=2.2.0", +] [dependency-groups] changelog = [ "git-cliff>=2.6.0", @@ -135,12 +139,10 @@ addopts = [ "-q", "--strict-markers", "--strict-config", - "--cov=my_library", - "--cov-report=term-missing", "--tb=short", "--log-level=WARNING", - "-p", "no:cacheprovider", "-ra", + "--testmon", ] markers = [ "e2e: End-to-end tests across the full stack or external systems", diff --git a/template/.claude/hooks/pre-write-doc-file-warning.sh b/template/.claude/hooks/pre-write-doc-file-warning.sh index ca7e82b..1aedb43 100755 --- a/template/.claude/hooks/pre-write-doc-file-warning.sh +++ b/template/.claude/hooks/pre-write-doc-file-warning.sh @@ -11,6 +11,7 @@ # • README.md — allowed anywhere # • CLAUDE.md — allowed anywhere # • docs/**/ — any depth under any docs/ directory +# • .claude/**/ — skills, rules, commands, hooks docs # # All other .md files → BLOCKED. Recreate them under docs/. # @@ -56,6 +57,12 @@ if echo "$FILE_PATH" | grep -qE '(^|/)tasks_summary/'; then exit 0 fi +# Files under .claude/ are allowed (skills, rules, commands, hooks docs) +if echo "$FILE_PATH" | grep -qE '(^|/)\.claude/'; then + echo "$INPUT" + exit 0 +fi + # ── Violation — block the write ─────────────────────────────────────────────── echo "┌─ BLOCKED: Markdown placement violation" >&2 echo "│" >&2 diff --git a/template/.claude/skills/bash-guide/SKILL.md b/template/.claude/skills/bash-guide/SKILL.md index 78e287f..9d5cc9e 100644 --- a/template/.claude/skills/bash-guide/SKILL.md +++ b/template/.claude/skills/bash-guide/SKILL.md @@ -198,6 +198,23 @@ done For the full pattern library, see [references/patterns.md](references/patterns.md). +## When to load references + +| If the task involves… | Load | +|---------------------------------------------|---------------------------------| +| Script headers, `main()` layout, comments | `references/structure.md` | +| Dual-mode logging, JSON log output | `references/logging.md` | +| Arg parsing, traps, arrays, retries | `references/patterns.md` | +| Starting a new script from scratch | `templates/script-skeleton.sh` | +| Simple script edit or small function | No reference needed — use inline | + +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When adding multiple functions to a script, write them all in + a single Edit call rather than one function at a time. +- **Read before edit:** Read the full script first to understand its structure, + existing style, and variable names before making any changes. + ## Quick reference: where to go deeper | Topic | Reference file | diff --git a/template/.claude/skills/config-management/SKILL.md b/template/.claude/skills/config-management/SKILL.md index d4560cb..bfe8cc1 100644 --- a/template/.claude/skills/config-management/SKILL.md +++ b/template/.claude/skills/config-management/SKILL.md @@ -86,6 +86,13 @@ Run all hooks manually: `just precommit` 4. Keep `justfile` recipes simple — one concern per recipe. 5. Pin all dependency versions in `pyproject.toml`. +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When modifying pyproject.toml (e.g., adding dependencies and + updating tool config), combine all changes into a single Edit call. +- **Read before edit:** Read the full config file first. Plan all sections that + need changes, then apply in one Edit call. + ## Quick reference: where to go deeper | Topic | Reference file | diff --git a/template/.claude/skills/cron-scheduling/SKILL.md b/template/.claude/skills/cron-scheduling/SKILL.md index 5fad82c..4c482a0 100644 --- a/template/.claude/skills/cron-scheduling/SKILL.md +++ b/template/.claude/skills/cron-scheduling/SKILL.md @@ -14,6 +14,13 @@ description: >- # Cron Scheduling Skill +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When setting up multiple cron jobs, write all entries in a + single `crontab -e` session or Edit call rather than one job at a time. +- **Read before edit:** Read the current crontab first (`crontab -l`) to + understand existing jobs before adding or modifying entries. + ## Quick reference: where to go deeper | Topic | Reference file | diff --git a/template/.claude/skills/css-guide/SKILL.md b/template/.claude/skills/css-guide/SKILL.md index 24f3bcf..3389888 100644 --- a/template/.claude/skills/css-guide/SKILL.md +++ b/template/.claude/skills/css-guide/SKILL.md @@ -253,6 +253,23 @@ Recommended stylelint rules: `color-named: "never"`, `color-no-invalid-hex`, `declaration-no-important`, a BEM-compatible `selector-class-pattern`, and `no-duplicate-selectors`. +## When to load references + +| If the task involves… | Load | +|------------------------------------------|-------------------------------------| +| ITCSS layers, CSS architecture design | `references/css-architecture.md` | +| ARIA, focus styles, a11y compliance | `references/accessibility.md` | +| Grid, flexbox, responsive breakpoints | `references/layout-responsive.md` | +| Starting a new project from scratch | `templates/css-tokens.css` + `templates/css-reset.css` | +| Simple CSS edits (default) | No reference needed — use inline | + +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When adding multiple CSS rules or updating a stylesheet, + combine all changes into a single Edit call per file. +- **Read before edit:** Read the full stylesheet first to understand existing + patterns, tokens, and class naming before modifying. + ## Quick reference: where to go deeper | Topic | Reference file | diff --git a/template/.claude/skills/html-guide/SKILL.md b/template/.claude/skills/html-guide/SKILL.md index 40c5efd..d9be131 100644 --- a/template/.claude/skills/html-guide/SKILL.md +++ b/template/.claude/skills/html-guide/SKILL.md @@ -285,6 +285,23 @@ Recommended `.htmlhintrc`: } ``` +## When to load references + +| If the task involves… | Load | +|---------------------------------------------|------------------------------------| +| Choosing semantic elements (article, nav) | `references/semantic-elements.md` | +| WCAG compliance, ARIA, focus management | `references/accessibility.md` | +| Starting a new HTML page | `templates/html-template.html` | +| Simple HTML edits (default) | No reference needed — use inline | + +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When restructuring an HTML file (fixing semantics, adding + ARIA attributes, updating structure), plan all changes first and apply in + a single Edit call. +- **Read before edit:** Read the full HTML file first to understand existing + structure and patterns. + ## Quick reference: where to go deeper | Topic | Reference file | diff --git a/template/.claude/skills/jinja-guide/SKILL.md b/template/.claude/skills/jinja-guide/SKILL.md index adf4362..6fc3429 100644 --- a/template/.claude/skills/jinja-guide/SKILL.md +++ b/template/.claude/skills/jinja-guide/SKILL.md @@ -254,6 +254,23 @@ conventions on top of Jinja. Auto-escape defaults differ (Flask: on for structure, and security defaults, see [references/best-practices.md](references/best-practices.md). +## When to load references + +| If the task involves… | Load | +|-------------------------------------------------|-----------------------------------| +| Operator syntax, literals, global functions | `references/syntax.md` | +| Filter usage beyond basics (A-Z list) | `references/filters-reference.md` | +| Template inheritance, include, import, macros | `references/inheritance.md` | +| Performance, security, testing best practices | `references/best-practices.md` | +| Simple template edits (default) | No reference needed — use inline | + +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When modifying multiple template files, read all target + templates first, then edit each in a single Edit call per file. +- **Read before edit:** Read the base template and child templates to understand + the inheritance chain before making block changes. + ## Quick reference: where to go deeper | Topic | Reference file | diff --git a/template/.claude/skills/maintain-skills/SKILL.md b/template/.claude/skills/maintain-skills/SKILL.md index 98a3a35..edef624 100644 --- a/template/.claude/skills/maintain-skills/SKILL.md +++ b/template/.claude/skills/maintain-skills/SKILL.md @@ -4,16 +4,18 @@ description: >- Audit, standardize, and improve existing skills in a skills directory (commonly `.claude/skills/` or `template/.claude/skills/`). Use for 'audit this skill', 'review skill quality', 'standardize skills', or any request to systematically review or - improve a SKILL.md file. Validates all seven consistency dimensions: frontmatter block + improve a SKILL.md file. Validates all nine consistency dimensions: frontmatter block scalars, title format, reference table headers, bundled-file naming, H1 headings, line - count, and bundled-asset layout. Provides step-by-step fix instructions for each - violation. Do NOT use for creating new skills from scratch — use skill-creator instead. + count, bundled-asset layout, lazy reference loading, and batch tool calls. Provides + step-by-step fix instructions for each violation. Do NOT use for creating new skills + from scratch — use skill-creator instead. --- # Maintain Skills Skill Audit, standardize, and improve existing skills. This skill provides exact rules and -commands so any model — including haiku — can maintain skills mechanically. +commands so any model — including haiku — can maintain skills mechanically. Covers nine +consistency dimensions including lazy reference loading and batch tool call guidance. Skills live under a skills root directory. The canonical path in this repo is `.claude/skills/` (Claude Code / Cowork default); older layouts used @@ -49,9 +51,9 @@ reference table (dimension 3). No orphans, regardless of directory. --- -## The seven consistency dimensions +## The nine consistency dimensions -Every skill MUST pass all seven checks. These are the primary audit criteria. +Every skill MUST pass all nine checks. These are the primary audit criteria. ### Dimension 1 — Frontmatter block scalar @@ -218,80 +220,119 @@ done ``` Output must be empty. ---- +### Dimension 8 — Lazy reference loading -## Full verification script +Skills MUST load reference files **on demand**, not eagerly. SKILL.md MUST contain a +**conditional loading table** (or decision tree) that maps task contexts to specific +references. The most common use case MUST be handled by inline guidance in SKILL.md +itself, so simple tasks require **zero** reference file loads. -Run this after ANY skill edit. All seven dimensions in one pass. Set `SKILLS_DIR` -to match your layout (`.claude/skills` or `template/.claude/skills`). +**Pattern — conditional loading table:** -```bash -SKILLS_DIR="${SKILLS_DIR:-.claude/skills}" +```markdown +## When to load references + +| If the task involves… | Load | +|--------------------------------------|------------------------------------| +| Writing async tests | `references/async-testing.md` | +| Using mocks or test doubles | `references/mocking.md` | +| Fixture scopes or factory fixtures | `references/fixtures.md` | +| Simple test writing (default) | No reference needed — use inline | +``` -echo "=== 1. Frontmatter block scalar ===" -for f in "$SKILLS_DIR"/*/SKILL.md; do - skill=$(basename "$(dirname "$f")") - scalar=$(grep 'description:' "$f" | head -1 | sed 's/.*description: *//') - echo " $skill: $scalar" -done +**Pattern — decision tree (alternative):** -echo "" -echo "=== 2. Title format ===" -for f in "$SKILLS_DIR"/*/SKILL.md; do - skill=$(basename "$(dirname "$f")") - echo " $skill: $(grep '^# ' "$f" | head -1)" -done +```markdown +## Reference loading + +├── Writing a new test file? +│ └── Use inline guidance below (AAA, naming, assertions) +├── Need fixtures beyond basic `@pytest.fixture`? +│ └── Load `references/fixtures.md` +├── Need mocking guidance? +│ └── Load `references/mocking.md` +└── Debugging flaky tests? + └── Load `references/anti-patterns.md` +``` + +**Rules:** + +- Never instruct "Load reference X" without a condition or context. +- The words "read", "load", or "consult" for a reference file MUST be preceded by + a conditional ("if", "when", "for ``"). +- Inline guidance in SKILL.md must cover the 80% case. References are for depth, + not basics. +- Skills with a single reference file: the condition can be simple ("For the full + config reference, see…") but must still exist — never "always load X first". +- Orchestrator skills (sdlc-workflow, tdd-workflow) load sub-skill SKILL.md files + at the **start of the stage that needs them**, not all upfront. Each stage's load + instruction is itself a conditional. -echo "" -echo "=== 3. Reference table header ===" +**Check:** +```bash for f in "$SKILLS_DIR"/*/SKILL.md; do skill=$(basename "$(dirname "$f")") - header=$(grep 'where to go deeper' "$f" || echo "(NOT FOUND)") - echo " $skill: $header" + # Find unconditional "load/read" instructions for reference files + hits=$(grep -n -iE '(^[0-9]+\.|^- ).*\b(load|read)\b.*references/' "$f" \ + | grep -viE '(if |when |for |only|conditional)' || true) + [ -n "$hits" ] && echo " $skill: unconditional reference loads found:" \ + && echo "$hits" done +``` +Ideally returns empty — all reference loads are conditional. -echo "" -echo "=== 4. Bundled file + folder naming (non-kebab?) ===" -bad_folders=$(ls -d "$SKILLS_DIR"/*/ 2>/dev/null | grep -E '[_A-Z]' || true) -bad_files=$(find "$SKILLS_DIR"/*/references "$SKILLS_DIR"/*/templates \ - "$SKILLS_DIR"/*/assets -type f \ - \( -name '[0-9]*' -o -name '*_*' -o -name '[A-Z]*' \) 2>/dev/null || true) -[ -z "$bad_folders$bad_files" ] && echo " None found ✓" \ - || { echo " folders: $bad_folders"; echo " files: $bad_files"; } - -echo "" -echo "=== 5. Reference H1 headings ===" -for f in "$SKILLS_DIR"/*/references/*.md; do - [ -f "$f" ] || continue - skill=$(basename "$(dirname "$(dirname "$f")")") - echo " $skill/$(basename "$f"): $(head -1 "$f")" -done +### Dimension 9 — Batch tool calls -echo "" -echo "=== 6. SKILL.md line counts ===" -wc -l "$SKILLS_DIR"/*/SKILL.md +Skills that involve editing files or running commands MUST include explicit **batch +edit guidance** instructing the executing model to minimize API calls. -echo "" -echo "=== 7. Bundled asset layout (no mixing templates/ and assets/templates/) ===" -for dir in "$SKILLS_DIR"/*/; do - skill=$(basename "$dir") - if [ -d "$dir/templates" ] && [ -d "$dir/assets/templates" ]; then - echo " $skill: MIXED LAYOUT" +**Required guidance block** — add this verbatim or adapted to the skill's domain: + +```markdown +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When making multiple changes to the same file, combine them + into a single Edit tool call where possible. Do not make incremental + single-line changes. +- **Parallel calls:** When running independent checks (e.g., lint + type-check), + make all tool calls in a single message rather than sequentially. +- **Read before edit:** Read each file once, plan all changes, then apply them + in the fewest possible Edit calls. +``` + +**Rules:** + +- Every skill that instructs the model to edit files (write code, fix issues, + apply docstrings, etc.) MUST contain batch edit guidance. +- Every skill that instructs the model to run multiple independent commands + MUST mention parallel execution. +- Orchestrator skills (sdlc-workflow, tdd-workflow) that launch Agent calls + already handle parallelism via multiple Agent invocations — add batch guidance + for within-agent edits. +- Skills that are read-only or informational (no file edits, no command runs) + are exempt. + +**Check:** +```bash +for f in "$SKILLS_DIR"/*/SKILL.md; do + skill=$(basename "$(dirname "$f")") + # Check for batch/parallel guidance + has_batch=$(grep -cilE '(batch edit|parallel call|single edit|minimize.*call)' \ + "$f" || true) + has_edits=$(grep -cilE '(edit|write|fix|apply|create file|modify)' "$f" || true) + if [ "$has_edits" -gt 0 ] && [ "$has_batch" -eq 0 ]; then + echo " $skill: edits files but has no batch guidance" fi done ``` +Ideally returns empty — all editing skills have batch guidance. + +--- -**Pass criteria:** ALL of these must be true: +## Full verification script -- Dimension 1: every skill shows `>-` -- Dimension 2: every title ends with ` Skill` -- Dimension 3: every skill has exactly one `where to go deeper` match, and every - file under `references/`, `templates/`, `assets/` is linked from that table -- Dimension 4: every folder and bundled file is kebab-case (no numbers, no - underscores, no uppercase) -- Dimension 5: every H1 heading is sentence case -- Dimension 6: every SKILL.md is under 400 lines -- Dimension 7: no skill mixes top-level `templates/` with `assets/templates/` +Run the script in [references/verification-script.md](references/verification-script.md) +after ANY skill edit. It checks all nine dimensions in one pass. --- @@ -301,11 +342,32 @@ entire skill repositories, SKILL.md structure, and writing style guidelines, see --- +## When to load references + +| If the task involves… | Load | +|---------------------------------------------|-----------------------------------| +| Step-by-step standardization of one skill | `references/fixing-skills.md` | +| Full 11-area deep audit of a skill | `references/audit-checklist.md` | +| Before/after fix examples for reference | `references/audit-examples.md` | +| Logging a completed audit session | `references/maintenance-log.md` | +| Running the 9-dimension verification check | `references/verification-script.md` | +| Quick dimension check (default) | No reference needed — use inline | + +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When fixing multiple dimensions in a single SKILL.md, combine + all fixes (frontmatter, title, references, etc.) into a single Edit call. +- **Parallel audits:** When auditing multiple skills, run dimension checks for + independent skills in parallel. +- **Read before edit:** Read the full SKILL.md once, note all violations, then + apply all fixes in the fewest Edit calls possible. + ## Quick reference: where to go deeper | Topic | Reference file | |----------------------------------------------------|----------------------------------------------------------------------| | Step-by-step fixing, auditing, and style guide | [references/fixing-skills.md](references/fixing-skills.md) | -| Full 9-area deep audit checklist | [references/audit-checklist.md](references/audit-checklist.md) | +| Full 11-area deep audit checklist | [references/audit-checklist.md](references/audit-checklist.md) | | Annotated before/after fix examples | [references/audit-examples.md](references/audit-examples.md) | | Running log of all audit sessions | [references/maintenance-log.md](references/maintenance-log.md) | +| Full verification script (all 9 dimensions) | [references/verification-script.md](references/verification-script.md) | diff --git a/template/.claude/skills/maintain-skills/references/audit-checklist.md b/template/.claude/skills/maintain-skills/references/audit-checklist.md index e8535db..89cb317 100644 --- a/template/.claude/skills/maintain-skills/references/audit-checklist.md +++ b/template/.claude/skills/maintain-skills/references/audit-checklist.md @@ -5,6 +5,8 @@ thorough reviews, repo-wide sweeps, or when unsure about a specific quality area The six consistency dimensions (in SKILL.md) are the minimum bar. This checklist goes deeper into content quality, technical accuracy, and cross-skill health. +It also covers the two efficiency dimensions (lazy loading and batch guidance) +introduced as dimensions 8 and 9. --- @@ -195,9 +197,79 @@ Quick pass — flag anything surprising for human review: --- +## Area 10 — Lazy reference loading (dimension 8) + +Verify that reference files are loaded on demand, not eagerly. + +**Audit steps:** + +1. Grep for all "load", "read", "consult" instructions targeting reference files: +```bash +grep -n -iE '\b(load|read|consult)\b.*references/' "$SKILL_DIR/SKILL.md" +``` + +2. For each match, check whether it has a conditional trigger (if/when/for): +```bash +grep -n -iE '\b(load|read|consult)\b.*references/' "$SKILL_DIR/SKILL.md" \ + | grep -viE '(if |when |for |only|conditional)' +``` +Any remaining lines are unconditional loads — flag as MED severity. + +3. Check for a conditional loading table or decision tree: +```bash +grep -c 'If the task involves\|When to load\|Reference loading' "$SKILL_DIR/SKILL.md" +``` +If zero, flag as MED — skill needs a conditional loading table. + +4. Verify the 80% case is covered inline. Read the SKILL.md body content: + - Does it contain enough guidance to handle the most common task without + loading any reference file? + - Or does every workflow step require reading a reference first? + +**Red flags:** +- "Always load X before starting" — should be "If doing X, load…" +- Stage 1 of an orchestrator loading all sub-skills upfront +- Reference table exists but no conditional loading guidance above it +- All reference files are mentioned as mandatory reads in the workflow + +--- + +## Area 11 — Batch tool call guidance (dimension 9) + +Verify that skills which edit files include batch edit guidance. + +**Audit steps:** + +1. Determine if the skill involves file editing or command running: +```bash +grep -cilE '(edit|write|fix|apply|create file|modify|run |execute)' \ + "$SKILL_DIR/SKILL.md" +``` +If zero, this dimension does not apply — skill is read-only/informational. + +2. Check for batch guidance: +```bash +grep -cilE '(batch edit|parallel call|single edit|minimize.*call|combine.*edit)' \ + "$SKILL_DIR/SKILL.md" +``` +If zero and step 1 matched, flag as MED — skill needs batch guidance. + +3. If present, verify the guidance covers all three aspects: + - **Batch edits** — combining changes to the same file + - **Parallel calls** — independent commands in one message + - **Read before edit** — plan all changes first + +**Red flags:** +- Skill has step-by-step instructions that say "edit line X… now edit line Y… + now edit line Z…" for the same file +- Multiple sequential independent commands with no mention of parallelism +- No "read the file first" instruction before a series of edits + +--- + ## Producing an audit report -After completing all 9 areas: +After completing all 11 areas: ```markdown ## Skill audit: diff --git a/template/.claude/skills/maintain-skills/references/fixing-skills.md b/template/.claude/skills/maintain-skills/references/fixing-skills.md index 9da717e..54c26f2 100644 --- a/template/.claude/skills/maintain-skills/references/fixing-skills.md +++ b/template/.claude/skills/maintain-skills/references/fixing-skills.md @@ -83,9 +83,77 @@ Add a row to the quick-reference table for each new file. If the skill has both `templates/` (top-level) and `assets/templates/`, consolidate into one. Prefer `assets/templates/` when the skill also ships other asset categories (icons, fonts, settings starters); prefer top-level `templates/` for simple skills that only ship a handful of Markdown starters. -### Step 10 — Run verification +### Step 10 — Fix lazy reference loading (dimension 8) -Run the full verification script from the main skill. ALL seven dimensions must pass. +Audit every line in SKILL.md that says "load", "read", or "consult" a reference file. +Each must have a conditional trigger. Fix unconditional loads: + +**Before (unconditional):** +```markdown +1. Load the `pytest` skill (read `.claude/skills/pytest/SKILL.md`). +2. For fixture patterns, read `.claude/skills/pytest/references/fixtures.md`. + For mocking guidance, read `.claude/skills/pytest/references/mocking.md`. +``` + +**After (conditional):** +```markdown +1. Load the `pytest` skill (read `.claude/skills/pytest/SKILL.md`). +2. If writing tests that need fixtures beyond basic `@pytest.fixture`: + read `.claude/skills/pytest/references/fixtures.md`. + If using mocks or test doubles: + read `.claude/skills/pytest/references/mocking.md`. + Otherwise: use inline guidance from the pytest skill's core principles. +``` + +For skills with many references, create a **conditional loading table**: + +```markdown +## When to load references + +| If the task involves… | Load | +|------------------------------------|---------------------------------| +| | `references/a.md` | +| | `references/b.md` | +| Simple / default case | No reference needed — use inline | +``` + +**Rules for the 80% case:** The most common use case must be covered by inline +guidance already in SKILL.md. A user doing the most typical task should never need +to load a reference file. References are for depth, advanced cases, and edge cases. + +For orchestrator skills (sdlc-workflow, tdd-workflow): ensure each stage loads its +dependencies at the start of that stage, not all upfront. Each stage's load +instruction must include "if entering this stage" as the condition. + +### Step 11 — Fix batch tool call guidance (dimension 9) + +If the skill instructs the model to edit files or run commands, add a batch guidance +section. Use the standard block or adapt it to the skill's domain: + +```markdown +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When making multiple changes to the same file, combine them + into a single Edit tool call where possible. Do not make incremental + single-line changes. +- **Parallel calls:** When running independent checks (e.g., lint + type-check), + make all tool calls in a single message rather than sequentially. +- **Read before edit:** Read each file once, plan all changes, then apply them + in the fewest possible Edit calls. +``` + +Place this section after the main workflow but before the "Quick reference" table. + +For skills that are read-only or purely informational (no file edits, no command +runs), this dimension does not apply — skip it. + +For orchestrator skills: add batch guidance for within-agent edits. The Agent-level +parallelism is already handled by multiple Agent calls — the batch guidance targets +edits within each agent's scope. + +### Step 12 — Run verification + +Run the full verification script from the main skill. ALL nine dimensions must pass. --- @@ -132,8 +200,10 @@ When restructuring a SKILL.md, use this standard order: 2. `# Skill` title 3. 1-3 line intro paragraph 4. Core content sections (main workflows, principles, checklists) -5. `## Quick reference: where to go deeper` (reference table covering every file under `references/`, `templates/`, and `assets/`) -6. Optional: `## Bundled scripts` (if `scripts/` exists) +5. Conditional reference loading table or decision tree (dimension 8) +6. Efficiency: batch edits and parallel calls (dimension 9, if applicable) +7. `## Quick reference: where to go deeper` (reference table covering every file under `references/`, `templates/`, and `assets/`) +8. Optional: `## Bundled scripts` (if `scripts/` exists) --- diff --git a/template/.claude/skills/maintain-skills/references/maintenance-log.md b/template/.claude/skills/maintain-skills/references/maintenance-log.md index 03dfe84..303c8a2 100644 --- a/template/.claude/skills/maintain-skills/references/maintenance-log.md +++ b/template/.claude/skills/maintain-skills/references/maintenance-log.md @@ -17,3 +17,5 @@ Track every audit session here. Append a row after each review. | Date | Scope | Skills Reviewed | Issues Found | Issues Fixed | Notes | |------|-------|----------------|--------------|--------------|-------| | 2026-04-09 | Repo-wide | skill-maintainer | 4H, 5M, 4L | 4H, 5M, 4L | Initial creation + full audit of own skill | +| 2026-04-16 | Repo-wide | all 24 skills | 3H, 16M, 4L | 0H, 0M, 0L | Added dimensions 8 (lazy ref loading) + 9 (batch tool calls) to standards. Plans created for all 24 skills in docs/skill-optimization-plans.md. Implementation deferred. | +| 2026-04-16 | single: prepare-pr | prepare-pr | 1H, 0M, 0L | 1H, 0M, 0L | Renamed prepare-pr → prepare-pr (dim 4: kebab-case). Updated name: frontmatter and sdlc-workflow reference. | diff --git a/template/.claude/skills/maintain-skills/references/verification-script.md b/template/.claude/skills/maintain-skills/references/verification-script.md new file mode 100644 index 0000000..18d7dc4 --- /dev/null +++ b/template/.claude/skills/maintain-skills/references/verification-script.md @@ -0,0 +1,100 @@ +# Verification script + +Run this after ANY skill edit. All nine dimensions in one pass. Set `SKILLS_DIR` +to match your layout (`.claude/skills` or `template/.claude/skills`). + +```bash +SKILLS_DIR="${SKILLS_DIR:-.claude/skills}" + +echo "=== 1. Frontmatter block scalar ===" +for f in "$SKILLS_DIR"/*/SKILL.md; do + skill=$(basename "$(dirname "$f")") + scalar=$(grep 'description:' "$f" | head -1 | sed 's/.*description: *//') + echo " $skill: $scalar" +done + +echo "" +echo "=== 2. Title format ===" +for f in "$SKILLS_DIR"/*/SKILL.md; do + skill=$(basename "$(dirname "$f")") + echo " $skill: $(grep '^# ' "$f" | head -1)" +done + +echo "" +echo "=== 3. Reference table header ===" +for f in "$SKILLS_DIR"/*/SKILL.md; do + skill=$(basename "$(dirname "$f")") + header=$(grep 'where to go deeper' "$f" || echo "(NOT FOUND)") + echo " $skill: $header" +done + +echo "" +echo "=== 4. Bundled file + folder naming (non-kebab?) ===" +bad_folders=$(ls -d "$SKILLS_DIR"/*/ 2>/dev/null | grep -E '[_A-Z]' || true) +bad_files=$(find "$SKILLS_DIR"/*/references "$SKILLS_DIR"/*/templates \ + "$SKILLS_DIR"/*/assets -type f \ + \( -name '[0-9]*' -o -name '*_*' -o -name '[A-Z]*' \) 2>/dev/null || true) +[ -z "$bad_folders$bad_files" ] && echo " None found ✓" \ + || { echo " folders: $bad_folders"; echo " files: $bad_files"; } + +echo "" +echo "=== 5. Reference H1 headings ===" +for f in "$SKILLS_DIR"/*/references/*.md; do + [ -f "$f" ] || continue + skill=$(basename "$(dirname "$(dirname "$f")")") + echo " $skill/$(basename "$f"): $(head -1 "$f")" +done + +echo "" +echo "=== 6. SKILL.md line counts ===" +wc -l "$SKILLS_DIR"/*/SKILL.md + +echo "" +echo "=== 7. Bundled asset layout (no mixing templates/ and assets/templates/) ===" +for dir in "$SKILLS_DIR"/*/; do + skill=$(basename "$dir") + if [ -d "$dir/templates" ] && [ -d "$dir/assets/templates" ]; then + echo " $skill: MIXED LAYOUT" + fi +done + +echo "" +echo "=== 8. Lazy reference loading (unconditional loads?) ===" +for f in "$SKILLS_DIR"/*/SKILL.md; do + skill=$(basename "$(dirname "$f")") + hits=$(grep -n -iE '(^[0-9]+\.|^- ).*\b(load|read)\b.*references/' "$f" \ + | grep -viE '(if |when |for |only|conditional)' || true) + [ -n "$hits" ] && echo " $skill: unconditional reference loads:" \ + && echo "$hits" +done + +echo "" +echo "=== 9. Batch tool call guidance ===" +for f in "$SKILLS_DIR"/*/SKILL.md; do + skill=$(basename "$(dirname "$f")") + has_batch=$(grep -cilE '(batch edit|parallel call|single edit|minimize.*call)' \ + "$f" || true) + has_edits=$(grep -cilE '(edit|write|fix|apply|create file|modify)' "$f" || true) + if [ "$has_edits" -gt 0 ] && [ "$has_batch" -eq 0 ]; then + echo " $skill: edits files but has no batch guidance" + fi +done +``` + +## Pass criteria + +ALL of these must be true: + +- Dimension 1: every skill shows `>-` +- Dimension 2: every title ends with ` Skill` +- Dimension 3: every skill has exactly one `where to go deeper` match, and every + file under `references/`, `templates/`, `assets/` is linked from that table +- Dimension 4: every folder and bundled file is kebab-case (no numbers, no + underscores, no uppercase) +- Dimension 5: every H1 heading is sentence case +- Dimension 6: every SKILL.md is under 400 lines +- Dimension 7: no skill mixes top-level `templates/` with `assets/templates/` +- Dimension 8: no unconditional reference loads — every "load/read reference" + instruction has a conditional trigger +- Dimension 9: every skill that edits files or runs commands has batch edit + and/or parallel call guidance diff --git a/template/.claude/skills/markdown/SKILL.md b/template/.claude/skills/markdown/SKILL.md index 1dbb651..20df063 100644 --- a/template/.claude/skills/markdown/SKILL.md +++ b/template/.claude/skills/markdown/SKILL.md @@ -29,9 +29,18 @@ readable, portable, accessible, and maintainable over time. | File management and doc systems | [references/file-management.md](references/file-management.md) | | Anti-patterns and cheat sheet | [references/anti-patterns-cheatsheet.md](references/anti-patterns-cheatsheet.md) | -Read the relevant reference file before working on a specific area. For a new README, -skim `document-structure.md` first. For table formatting or GFM features, check the -corresponding reference. +## When to load references + +| If the task involves… | Load | +|--------------------------------------------|--------------------------------------------| +| Front matter, document skeleton, headings | `references/document-structure.md` | +| Lists, emphasis, whitespace formatting | `references/formatting-syntax.md` | +| Code blocks, fenced blocks, links | `references/code-and-links.md` | +| Tables, images, or inline HTML | `references/tables-images-html.md` | +| GFM features, callouts, task lists | `references/extended-syntax.md` | +| Doc systems, multi-file docs, wikis | `references/file-management.md` | +| Common mistakes quick reference | `references/anti-patterns-cheatsheet.md` | +| Writing a basic README or doc (default) | No reference needed — use inline guidance | --- @@ -171,6 +180,13 @@ Strongly prefer plain Markdown over HTML. Acceptable uses: `
`, `
`, --- +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When restructuring a Markdown file (fixing headings, lists, + and links), plan all changes first, then apply them in a single Edit call. +- **Read before edit:** Read the full file once to understand its structure before + making any changes. Apply all edits in the fewest calls possible. + ## See Also - [Google Markdown Style Guide](https://google.github.io/styleguide/docguide/style.html) diff --git a/template/.claude/skills/prepare_pr/SKILL.md b/template/.claude/skills/prepare-pr/SKILL.md similarity index 92% rename from template/.claude/skills/prepare_pr/SKILL.md rename to template/.claude/skills/prepare-pr/SKILL.md index 38444e8..8afaf41 100644 --- a/template/.claude/skills/prepare_pr/SKILL.md +++ b/template/.claude/skills/prepare-pr/SKILL.md @@ -1,5 +1,5 @@ --- -name: pr-template +name: prepare-pr description: >- Enforce a strict PR (pull request) description template every time one is generated, written, or drafted. Use this skill whenever the user asks to: generate a PR, write a PR description, draft a pull request, @@ -52,8 +52,14 @@ Scan everything the user provided. Find these signals: ## STEP 3 — Fill each section -Read `references/section-rules.md` now. -It contains exact fill instructions, fallback text, and examples for every section. +If the PR template has custom sections or you need exact fill rules for each +section, read `references/section-rules.md`. For standard PRs with Summary, +Test Plan, and Breaking Changes sections, use the inline template below: + +> **Summary:** [2-3 bullets on what changed and why] +> **Test plan:** [bullets: what to test and how] +> **Breaking changes:** [None / list of breaking changes] + Work through each section **one at a time, in template order**. --- diff --git a/template/.claude/skills/prepare_pr/references/section-rules.md b/template/.claude/skills/prepare-pr/references/section-rules.md similarity index 100% rename from template/.claude/skills/prepare_pr/references/section-rules.md rename to template/.claude/skills/prepare-pr/references/section-rules.md diff --git a/template/.claude/skills/pytest/SKILL.md b/template/.claude/skills/pytest/SKILL.md index 7d88f6f..a89fb57 100644 --- a/template/.claude/skills/pytest/SKILL.md +++ b/template/.claude/skills/pytest/SKILL.md @@ -36,9 +36,19 @@ topic. | `find_slow_tests.py` | Runs pytest, identifies tests exceeding a time threshold | | `mark_slow_tests.py` | Adds `@pytest.mark.slow` to the identified slow tests | -Read the relevant reference file before working on a specific area. For a new test file, -skim `test-organization.md` and `fixtures.md` first. For debugging flaky tests, start -with `anti-patterns.md`. +## When to load references + +| If the task involves… | Load | +|------------------------------------------|-------------------------------------------------| +| Fixture scopes, yield, DI, factories | `references/fixtures.md` | +| Parametrize, custom markers, xfail | `references/parametrize-and-markers.md` | +| Mocking, monkeypatch, test doubles | `references/mocking.md` | +| Complex assertions, approx, raises | `references/assertions.md` | +| Unit vs integration vs e2e decisions | `references/test-types.md` | +| Project layout, naming, conftest | `references/test-organization.md` | +| Debugging flaky tests, common mistakes | `references/anti-patterns.md` | +| CI config, coverage, plugins | `references/ci-and-plugins.md` | +| Simple test writing (default) | No reference needed — use inline guidance below | --- @@ -136,7 +146,7 @@ Key rules for fixtures: - Use `yield` for setup + teardown in a single function. - Use `tmp_path` for filesystem operations — never hardcode `/tmp` paths. -Read [references/fixtures.md](references/fixtures.md) for scopes, autouse, factory +For advanced fixture patterns, read [references/fixtures.md](references/fixtures.md) for scopes, autouse, factory fixtures, and advanced patterns. ### Parametrize to cover multiple inputs @@ -155,7 +165,7 @@ def test_email_validation(email: str, is_valid: bool) -> None: assert validate_email(email) == is_valid ``` -Each parameter set runs as a separate test, so failures are pinpointed. Read +Each parameter set runs as a separate test, so failures are pinpointed. For advanced parametrization, read [references/parametrize-and-markers.md](references/parametrize-and-markers.md) for advanced parametrization and custom markers. @@ -177,7 +187,7 @@ def test_sends_notification_on_order(mocker): ) ``` -Read [references/mocking.md](references/mocking.md) for `monkeypatch`, `pytest-mock`, +For advanced mocking patterns, read [references/mocking.md](references/mocking.md) for `monkeypatch`, `pytest-mock`, factory patterns, and when *not* to mock. ### Test isolation is non-negotiable @@ -328,6 +338,17 @@ for CI integration patterns. --- +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When adding multiple test functions to the same file, write + them all in a single Edit tool call rather than one function at a time. +- **Parallel calls:** When running `just test` and `just coverage` independently, + make both calls in a single message. +- **Read before edit:** Read the target test file and the source module once each, + plan all test functions, then write them in one Edit call. + +--- + ## Checklist before committing tests - [ ] Each test has a clear, descriptive name. diff --git a/template/.claude/skills/python-code-quality/SKILL.md b/template/.claude/skills/python-code-quality/SKILL.md index 023c24d..e670331 100644 --- a/template/.claude/skills/python-code-quality/SKILL.md +++ b/template/.claude/skills/python-code-quality/SKILL.md @@ -73,6 +73,18 @@ ruff format --check → ruff check → bandit → semgrep → basedpyrig --- +## When to load references + +| If the task involves… | Load | +|----------------------------------------|-----------------------------------| +| Configuring or debugging ruff | `references/ruff.md` | +| Setting up or fixing pre-commit hooks | `references/pre-commit.md` | +| Type errors or basedpyright config | `references/basedpyright.md` | +| Security scan findings (bandit) | `references/bandit.md` | +| Custom semgrep rules | `references/semgrep.md` | +| Complete config file examples | `references/complete-configs.md` | +| Running `just lint`/`just fix` (default) | No reference needed — use inline | + ## Quick reference: where to go deeper Read the relevant reference file for configuration details, error code explanations, @@ -89,6 +101,15 @@ or CI integration specifics: --- +## Efficiency: batch edits and parallel calls + +- **Parallel calls:** Run independent checks (`ruff check`, `basedpyright`, + `bandit`) in parallel as separate tool calls in a single message. +- **Batch edits:** When fixing multiple lint violations in the same file, combine + all fixes into a single Edit tool call. +- **CI ordering:** Follow the fast-fail order (format → lint → bandit → semgrep → + type → test) but run independent stages in parallel where possible. + ## Adding a new tool 1. Create `references/.md` using the template below. diff --git a/template/.claude/skills/python-code-reviewer/SKILL.md b/template/.claude/skills/python-code-reviewer/SKILL.md index fddb85c..58f62d1 100644 --- a/template/.claude/skills/python-code-reviewer/SKILL.md +++ b/template/.claude/skills/python-code-reviewer/SKILL.md @@ -140,7 +140,9 @@ For the sub-items, load `references/checklist.md`. Do not duplicate checklist co ## Phase 3 — Output the Review -Load `references/output-format.md` and select the matching template: +If you need the exact output template format, load `references/output-format.md` +and select the matching template. For Quick mode reviews, use this compact format: +> **Verdict: [APPROVE/REQUEST CHANGES]** — [1-line summary]. Findings: [list]. - **Template A** — snippet / function / module (most common) - **Template B** — PR / diff review - **Template C** — full module audit (Deep mode only) diff --git a/template/.claude/skills/python-docstrings/SKILL.md b/template/.claude/skills/python-docstrings/SKILL.md index b357a9d..1c1fc5b 100644 --- a/template/.claude/skills/python-docstrings/SKILL.md +++ b/template/.claude/skills/python-docstrings/SKILL.md @@ -80,6 +80,16 @@ Docstrings are narrative text. Use proper capitalization, punctuation, and compl --- +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When adding docstrings to multiple functions in the same file, + combine all docstring additions into a single Edit tool call. Do not make one + edit per function. +- **Read before edit:** Read the entire file first, identify all constructs needing + docstrings, draft all docstrings mentally, then apply in one Edit call per file. +- **Multi-file tasks:** When auditing multiple files, read all target files first, + then edit each file once with all its docstrings in a single call. + ## Quick reference: where to go deeper | Topic | Reference file | diff --git a/template/.claude/skills/sdlc-workflow/SKILL.md b/template/.claude/skills/sdlc-workflow/SKILL.md index d243800..ddb4853 100644 --- a/template/.claude/skills/sdlc-workflow/SKILL.md +++ b/template/.claude/skills/sdlc-workflow/SKILL.md @@ -53,7 +53,7 @@ Stage Name Model Execution Sub-skills 5 SECURITY Haiku Agent (parallel) security 6 DOCUMENTATION Haiku Agent (parallel) python-docstrings, markdown 7 COMMIT + CHANGELOG Haiku Agent (sequential) (inline guidance) - 8 PULL REQUEST Haiku Agent (sequential) prepare_pr + 8 PULL REQUEST Haiku Agent (sequential) prepare-pr 9 SUMMARY Haiku Agent (sequential) (task_summary_template.md) ``` @@ -91,8 +91,11 @@ Gate: all pre-flight checks pass. No user approval needed. **Model:** Full (main model, interactive) 1. Load the `pytest` skill (read `.claude/skills/pytest/SKILL.md`). -2. For fixture patterns, read `.claude/skills/pytest/references/fixtures.md`. - For mocking guidance, read `.claude/skills/pytest/references/mocking.md`. +2. If writing tests that use fixtures beyond basic `@pytest.fixture`: + read `.claude/skills/pytest/references/fixtures.md`. + If writing tests that need mocks or monkeypatch: + read `.claude/skills/pytest/references/mocking.md`. + Otherwise: use inline guidance from the pytest skill's core principles. 3. For each acceptance criterion, draft the smallest test: - Name: `test__when_` - Structure: AAA (Arrange-Act-Assert) @@ -243,7 +246,7 @@ Report: commit hash and message. **Model:** Haiku (Agent call, sequential after stage 7) ``` -Load the prepare_pr skill (.claude/skills/prepare_pr/SKILL.md). +Load the prepare-pr skill (.claude/skills/prepare-pr/SKILL.md). 1. Read .github/PULL_REQUEST_TEMPLATE.md (if exists). 2. Extract signals from commits: git log main..HEAD, git diff main...HEAD --stat. @@ -289,6 +292,18 @@ Gate: file exists at `tasks_summary/TASK_ID_summary.md`. --- +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When applying multiple changes to the same implementation or + test file (e.g., adding docstrings to several functions), combine them into + a single Edit tool call. +- **Parallel calls:** Stages 4, 5, and 6 already run as parallel Agent calls. + Within each agent, run independent lint/type/security commands in parallel. +- **Read before edit:** In GREEN and REFACTOR stages, read each target file once, + plan all changes, then apply in the fewest Edit calls possible. + +--- + ## Error handling | Stage | On failure | diff --git a/template/.claude/skills/security/SKILL.md b/template/.claude/skills/security/SKILL.md index de623d7..8c9a5a1 100644 --- a/template/.claude/skills/security/SKILL.md +++ b/template/.claude/skills/security/SKILL.md @@ -35,6 +35,13 @@ When reviewing code, check for: | Path traversal | user_input validated against base directory; use `.resolve()` | | Error handling | Error messages don't expose internal paths or config values | +## Efficiency: batch edits and parallel calls + +- **Parallel calls:** Run `bandit` and `semgrep` scans in parallel as + independent tool calls in a single message. +- **Batch edits:** When fixing multiple security findings in the same file, + combine all fixes into a single Edit call. + ## Quick reference: where to go deeper | Topic | Reference file | diff --git a/template/.claude/skills/tdd-test-planner/SKILL.md b/template/.claude/skills/tdd-test-planner/SKILL.md index 3c032fe..229bd26 100644 --- a/template/.claude/skills/tdd-test-planner/SKILL.md +++ b/template/.claude/skills/tdd-test-planner/SKILL.md @@ -100,16 +100,14 @@ floats. #### E — Integration Points Interactions with external collaborators (DB, API, filesystem, message queue, -another module). One test per meaningful interaction. Load -`references/test-doubles.md` to choose the right test double (stub/mock/fake). +another module). One test per meaningful interaction. If the test plan requires mocks, stubs, or fakes, load `references/test-doubles.md` to choose the right test double (stub/mock/fake). --- ## Step 4 — Select pytest mechanics **Fixtures:** Use for any setup shared across 2+ tests. Fixtures used across -multiple test files belong in `conftest.py` — not inline. See -`references/pytest-patterns.md` for conftest layout. +multiple test files belong in `conftest.py` — not inline. If you need specific pytest patterns (parametrize, fixtures, markers), load `references/pytest-patterns.md` for conftest layout and other patterns. **Parametrize:** Use when the same assertion logic applies to multiple input/output pairs. Always add human-readable `ids=` so test output says diff --git a/template/.claude/skills/tdd-workflow/SKILL.md b/template/.claude/skills/tdd-workflow/SKILL.md index b000d33..18335a5 100644 --- a/template/.claude/skills/tdd-workflow/SKILL.md +++ b/template/.claude/skills/tdd-workflow/SKILL.md @@ -225,6 +225,17 @@ Never patch first. A bug without a reproducing test will return. --- +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When writing the test file in RED or the implementation in + GREEN, write the complete file content in a single Edit call rather than + adding functions one by one. +- **Read before edit:** Read the target module and existing test file once before + planning changes. Apply all edits in the fewest calls possible. +- **REFACTOR batching:** Group related refactoring changes (e.g., all naming + fixes) into a single Edit call per file. Run tests after each logical group, + not after each individual change. + ## Skill dependencies | Stage | Load first | Fallback (if not installed) | diff --git a/template/.claude/skills/test-quality-reviewer/SKILL.md b/template/.claude/skills/test-quality-reviewer/SKILL.md index df171d3..597e6a4 100644 --- a/template/.claude/skills/test-quality-reviewer/SKILL.md +++ b/template/.claude/skills/test-quality-reviewer/SKILL.md @@ -15,6 +15,14 @@ description: >- You are auditing pytest test files for quality, correctness, and improvement opportunities. Your goal: produce a structured, actionable report that helps the author build a test suite they can actually trust. +## When to load references + +| If the task involves… | Load | +|-------------------------------------------------|------------------------------------| +| Before/after examples of test improvements | `references/examples.md` | +| Advanced patterns (property testing, snapshot) | `references/advanced-patterns.md` | +| Standard test quality review (default) | No reference needed — use inline | + ## Quick reference: where to go deeper | Topic | Reference file | diff --git a/template/.claude/skills/type-checking/SKILL.md b/template/.claude/skills/type-checking/SKILL.md index ce1000f..1318a4e 100644 --- a/template/.claude/skills/type-checking/SKILL.md +++ b/template/.claude/skills/type-checking/SKILL.md @@ -50,6 +50,13 @@ See `references/basedpyright.md` for: - Avoid `# type: ignore` without specific error codes. - When suppressing, use: `# type: ignore[error-code]` with an explanation. +## Efficiency: batch edits and parallel calls + +- **Batch edits:** When fixing multiple type errors in the same file, combine + all annotation fixes into a single Edit call. +- **Read before edit:** Read the full file first to understand the type + relationships before adding annotations. + ## Quick reference: where to go deeper | Topic | Reference file | diff --git a/template/.github/workflows/ci.yml.jinja b/template/.github/workflows/ci.yml.jinja index 1a4dd5d..17d7806 100644 --- a/template/.github/workflows/ci.yml.jinja +++ b/template/.github/workflows/ci.yml.jinja @@ -89,7 +89,7 @@ jobs: run: uv sync --frozen --extra dev --extra test{% if include_docs %} --extra docs{% endif %} - name: Pre-commit (all files) - run: uv run pre-commit run --all-files --verbose + run: uv run pre-commit run --all-files test: name: Test (Python {% raw %}${{ matrix.python-version }}{% endraw %}) @@ -114,7 +114,7 @@ jobs: run: uv sync --frozen --extra dev --extra test{% if include_docs %} --extra docs{% endif %} - name: Run tests - run: uv run pytest -n auto --cov --cov-report=xml --cov-report=term + run: uv run pytest -q --no-testmon --cov --cov-report=xml --cov-report=term-missing -p no:cacheprovider - name: Upload coverage to Codecov uses: codecov/codecov-action@v6 diff --git a/template/.gitignore b/template/.gitignore index e68bb7d..76bde4f 100644 --- a/template/.gitignore +++ b/template/.gitignore @@ -222,6 +222,7 @@ mlruns/ data/ .claude/todos/ +.claude/worktrees/ .claude/.refactor-edit-count .cursor/ @@ -246,3 +247,11 @@ task_channel/ runner.sh full_runner.sh + +# ========================================================================== +# PYTEST-TESTMON +# ========================================================================== + +.testmondata +.testmondata-shm +.testmondata-wal diff --git a/template/justfile.jinja b/template/justfile.jinja index 21c946e..23fc65c 100644 --- a/template/justfile.jinja +++ b/template/justfile.jinja @@ -43,7 +43,7 @@ fix: fmt-check: @uv run ruff format --check . -# Check docstring coverage +# Check docstring coverage docs-check: @echo "=== Docstring coverage check ===" @uv run ruff check --select D . @@ -86,7 +86,7 @@ test-slow: # Run tests in parallel with minimal output test-parallel: - @uv run pytest tests/ -n auto + @uv run pytest tests/ -n auto --no-testmon # Run tests with verbose output (shows test names, INFO logs) test-verbose: @@ -98,7 +98,7 @@ test-debug: # Re-run only the tests that failed in the last run test-lf: - @uv run pytest tests/ --lf + @uv run pytest tests/ --lf --no-testmon # Stop on first test failure (fast feedback) test-first-fail: @@ -106,7 +106,7 @@ test-first-fail: # Run tests for changed (unstaged+staged) Python files only — fast incremental feedback test-changed: - @uv run pytest $(git diff --name-only -- '*.py' | sed 's/src/tests/g') + @uv run pytest --no-testmon $(git diff --name-only -- '*.py' | sed 's/src/tests/g') # Fast unit tests only — excludes slow and integration markers test-fast: @@ -118,11 +118,12 @@ test-integration: # Re-run last failed tests with maximum verbosity — fast debugging loop test-failed-verbose: - @uv run pytest --lf -vv + @uv run pytest --lf -vv --no-testmon # Run tests with coverage report (full HTML/XML/term output) coverage: @uv run pytest tests/ \ + --no-testmon \ --cov={{ package_name }} \ --cov-report=term-missing \ --cov-report=html \ @@ -130,7 +131,7 @@ coverage: # Test command matching GitHub CI (.github/workflows/ci.yml test job) test-ci: - @uv run pytest -n auto --cov --cov-report=xml --cov-report=term + @uv run pytest -q --no-testmon --cov --cov-report=xml --cov-report=term-missing -p no:cacheprovider # ------------------------------------------------------------------------- # Pre-commit @@ -266,7 +267,7 @@ check: @just sync-check @just docs-check @just test-ci - @uv run pre-commit run --all-files --verbose + @uv run pre-commit run --all-files ci: @just fix diff --git a/template/pyproject.toml.jinja b/template/pyproject.toml.jinja index 35f3032..81eacc5 100644 --- a/template/pyproject.toml.jinja +++ b/template/pyproject.toml.jinja @@ -47,6 +47,7 @@ test = [ "pytest-xdist>=3.5.0", "pytest-cov>=6.0.0", "coverage[toml]>=7.6.0", + "pytest-testmon>=2.2.0", ] {% if include_docs %} @@ -175,12 +176,10 @@ addopts = [ "-q", "--strict-markers", "--strict-config", - "--cov={{ package_name }}", - "--cov-report=term-missing", "--tb=short", "--log-level=WARNING", - "-p", "no:cacheprovider", "-ra", + "--testmon", ] markers = [ "e2e: End-to-end tests across the full stack or external systems", diff --git a/tests/integration/test_template.py b/tests/integration/test_template.py index a1a8522..09aec37 100644 --- a/tests/integration/test_template.py +++ b/tests/integration/test_template.py @@ -1246,6 +1246,16 @@ def test_generated_pyproject_pytest_markers_and_asyncio(tmp_path: Path) -> None: for marker in ("e2e:", "integration:", "regression:", "slow:", "smoke:", "unit:"): assert marker in joined + # --testmon must be in addopts; --cov must NOT be (conflicts with testmon tracing) + addopts = cast( + "Sequence[str]", + require_sequence(ini_options.get("addopts"), name="pytest.ini_options.addopts"), + ) + assert "--testmon" in addopts, "addopts must enable testmon by default" + assert not any("--cov" in opt for opt in addopts), ( + "--cov must not be in addopts: it conflicts with testmon's coverage tracing" + ) + def test_generated_pre_commit_includes_detect_secrets(tmp_path: Path) -> None: """Pre-commit config in generated projects should run detect-secrets with a baseline.""" @@ -1557,14 +1567,20 @@ def test_ci_workflow_aligns_with_just_ci(tmp_path: Path) -> None: assert "needs: [lint, typecheck, precommit, test]" in workflow, ( "Aggregate check must gate on pre-commit alongside lint, typecheck, and tests" ) - assert "uv run pre-commit run --all-files --verbose" in workflow + assert "uv run pre-commit run --all-files" in workflow assert "uv run ruff format --check src tests" in workflow assert "uv run ruff check src tests" in workflow - assert "uv run pytest -n auto --cov --cov-report=xml --cov-report=term" in workflow + assert ( + "uv run pytest -q --no-testmon --cov --cov-report=xml --cov-report=term-missing -p no:cacheprovider" + in workflow + ) justfile = (test_dir / "justfile").read_text(encoding="utf-8") assert "test-ci:" in justfile - assert "pytest -n auto --cov --cov-report=xml --cov-report=term" in justfile + assert ( + "pytest -q --no-testmon --cov --cov-report=xml --cov-report=term-missing -p no:cacheprovider" + in justfile + ) assert "@just test-ci" in justfile lint_yml = (test_dir / ".github" / "workflows" / "lint.yml").read_text(encoding="utf-8") diff --git a/uv.lock b/uv.lock index 4dee646..fcc6eab 100644 --- a/uv.lock +++ b/uv.lock @@ -788,6 +788,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, ] +[[package]] +name = "pytest-asyncio" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, +] + [[package]] name = "pytest-cov" version = "7.1.0" @@ -802,6 +815,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" }, ] +[[package]] +name = "pytest-testmon" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/1d/3e4230cc67cd6205bbe03c3527500c0ccaf7f0c78b436537eac71590ee4a/pytest_testmon-2.2.0.tar.gz", hash = "sha256:01f488e955ed0e0049777bee598bf1f647dd524e06f544c31a24e68f8d775a51", size = 23108, upload-time = "2025-12-01T07:30:24.76Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/55/ebb3c2f59fb089f08d00f764830d35780fc4e4c41dffcadafa3264682b65/pytest_testmon-2.2.0-py3-none-any.whl", hash = "sha256:2604ca44a54d61a2e830d9ce828b41a837075e4ebc1f81b148add8e90d34815b", size = 25199, upload-time = "2025-12-01T07:30:23.623Z" }, +] + [[package]] name = "pytest-xdist" version = "3.8.0" @@ -850,15 +876,19 @@ dev = [ { name = "basedpyright" }, { name = "commitizen" }, { name = "copier" }, - { name = "coverage", extra = ["toml"] }, { name = "detect-secrets" }, { name = "jinja2-time" }, { name = "pre-commit" }, + { name = "ruff" }, + { name = "typing-extensions" }, +] +test = [ + { name = "coverage", extra = ["toml"] }, { name = "pytest" }, + { name = "pytest-asyncio" }, { name = "pytest-cov" }, + { name = "pytest-testmon" }, { name = "pytest-xdist" }, - { name = "ruff" }, - { name = "typing-extensions" }, ] [package.dev-dependencies] @@ -871,17 +901,19 @@ requires-dist = [ { name = "basedpyright", marker = "extra == 'dev'", specifier = ">=1.21.0" }, { name = "commitizen", marker = "extra == 'dev'", specifier = ">=4.0.0" }, { name = "copier", marker = "extra == 'dev'", specifier = ">=9.11.2" }, - { name = "coverage", extras = ["toml"], marker = "extra == 'dev'", specifier = ">=7.6.0" }, + { name = "coverage", extras = ["toml"], marker = "extra == 'test'", specifier = ">=7.6.0" }, { name = "detect-secrets", marker = "extra == 'dev'", specifier = ">=1.5.0" }, { name = "jinja2-time", marker = "extra == 'dev'", specifier = ">=0.2.0" }, { name = "pre-commit", marker = "extra == 'dev'", specifier = ">=4.0.0" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, - { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=6.0.0" }, - { name = "pytest-xdist", marker = "extra == 'dev'", specifier = ">=3.5.0" }, + { name = "pytest", marker = "extra == 'test'", specifier = ">=8.0.0" }, + { name = "pytest-asyncio", marker = "extra == 'test'", specifier = ">=0.24.0" }, + { name = "pytest-cov", marker = "extra == 'test'", specifier = ">=6.0.0" }, + { name = "pytest-testmon", marker = "extra == 'test'", specifier = ">=2.2.0" }, + { name = "pytest-xdist", marker = "extra == 'test'", specifier = ">=3.5.0" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.8.0" }, { name = "typing-extensions", marker = "extra == 'dev'", specifier = ">=4.12.0" }, ] -provides-extras = ["dev"] +provides-extras = ["dev", "test"] [package.metadata.requires-dev] changelog = [{ name = "git-cliff", specifier = ">=2.6.0" }] From 9bc2cb0227b986f4994f7a83fec42f0f1d3ac41c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Apr 2026 01:06:34 +0200 Subject: [PATCH 4/7] Fix hooks --- template/.claude/hooks/pre-write-doc-file-warning.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/template/.claude/hooks/pre-write-doc-file-warning.sh b/template/.claude/hooks/pre-write-doc-file-warning.sh index c66fd3c..1aedb43 100755 --- a/template/.claude/hooks/pre-write-doc-file-warning.sh +++ b/template/.claude/hooks/pre-write-doc-file-warning.sh @@ -57,6 +57,12 @@ if echo "$FILE_PATH" | grep -qE '(^|/)tasks_summary/'; then exit 0 fi +# Files under .claude/ are allowed (skills, rules, commands, hooks docs) +if echo "$FILE_PATH" | grep -qE '(^|/)\.claude/'; then + echo "$INPUT" + exit 0 +fi + # ── Violation — block the write ─────────────────────────────────────────────── echo "┌─ BLOCKED: Markdown placement violation" >&2 echo "│" >&2 From 95bcd231590291c4e9d6aa9ce4770e36efdd06a3 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Apr 2026 01:17:24 +0200 Subject: [PATCH 5/7] Fix ci --- .github/workflows/lint.yml | 2 +- .github/workflows/tests.yml | 2 +- justfile | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 8f8866a..59fcdc9 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -45,7 +45,7 @@ jobs: run: uv python install 3.11 - name: Install dependencies - run: uv sync --frozen --extra dev + run: uv sync --frozen --extra dev --extra test - name: Ruff format (check) run: uv run ruff format --check . diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 58641fa..60a94f8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -46,7 +46,7 @@ jobs: run: uv python install ${{ matrix.python-version }} - name: Sync dependencies - run: uv sync --frozen --extra dev + run: uv sync --frozen --extra dev --extra test - name: Run tests run: | diff --git a/justfile b/justfile index bd87a1d..b936d00 100644 --- a/justfile +++ b/justfile @@ -143,17 +143,17 @@ test-ci-matrix: uv python install 3.11 3.12 3.13 echo "=== Python 3.11 + coverage (tests.yml matrix) ===" unset UV_PROJECT_ENVIRONMENT - uv sync --frozen --extra dev --python 3.11 + uv sync --frozen --extra dev --extra test --python 3.11 uv run pytest -q --no-testmon --cov --cov-report=xml --cov-report=term-missing for py in 3.12 3.13; do echo "=== Python ${py} (tests.yml matrix) ===" suffix="${py//./}" export UV_PROJECT_ENVIRONMENT="${ROOT}/.venv-ci-${suffix}" - uv sync --frozen --extra dev --python "${py}" + uv sync --frozen --extra dev --extra test --python "${py}" uv run pytest -q --no-testmon done unset UV_PROJECT_ENVIRONMENT - uv sync --frozen --extra dev --python 3.11 + uv sync --frozen --extra dev --extra test --python 3.11 echo "✓ Restored default .venv (Python 3.11)" # ------------------------------------------------------------------------- From 027f8fde4f4d94b476ff22be5b602355be088746 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Apr 2026 01:21:25 +0200 Subject: [PATCH 6/7] Fixing workflow skill --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 60a94f8..81a04c0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -51,9 +51,9 @@ jobs: - name: Run tests run: | if [ "${{ matrix.python-version }}" = "3.11" ]; then - uv run pytest -q --no-testmon --cov --cov-report=xml --cov-report=term-missing -p no:cacheprovider --cache-clear + uv run pytest -q --no-testmon --cov --cov-report=xml --cov-report=term-missing -p no:cacheprovider else - uv run pytest -q --no-testmon -p no:cacheprovider --cache-clear + uv run pytest -q --no-testmon -p no:cacheprovider fi - name: Upload coverage XML From 6381ef165150817b846079ef49c05885fcdde421 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Apr 2026 01:31:17 +0200 Subject: [PATCH 7/7] Lint fixes --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 81a04c0..2fe438d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -51,9 +51,9 @@ jobs: - name: Run tests run: | if [ "${{ matrix.python-version }}" = "3.11" ]; then - uv run pytest -q --no-testmon --cov --cov-report=xml --cov-report=term-missing -p no:cacheprovider + uv run pytest -q --no-testmon --cov --cov-report=xml --cov-report=term-missing -p no:cacheprovider else - uv run pytest -q --no-testmon -p no:cacheprovider + uv run pytest -q --no-testmon -p no:cacheprovider fi - name: Upload coverage XML