From 0c91ac43bf2816727281f362faa426b5ac58c69f Mon Sep 17 00:00:00 2001 From: Gustavo Freze Date: Fri, 26 Jun 2026 19:29:20 -0300 Subject: [PATCH 1/4] chore: Add tiny-blocks agent rules, skills, and hooks. Introduce the .claude conventions framework with path-scoped rules, the create, consume, and commit-message skills, and PostToolUse hooks for member ordering and prose punctuation. Replace the previous single CLAUDE.md with a short project index. --- .claude/CLAUDE.md | 32 - .claude/hooks/php-ordering-conformance.py | 607 ++++++++++ .../php-prose-punctuation-conformance.py | 176 +++ .claude/rules/github-workflows.md | 78 -- .claude/rules/php-library-architecture.md | 147 +++ .claude/rules/php-library-code-style.md | 1020 +++++++++++++++-- .claude/rules/php-library-documentation.md | 217 +++- .claude/rules/php-library-github-workflows.md | 104 ++ .claude/rules/php-library-modeling.md | 353 ++++-- .claude/rules/php-library-testing.md | 382 +++++- .claude/rules/php-library-tooling.md | 138 +++ .claude/settings.json | 249 ++++ .claude/skills/commit-message/SKILL.md | 119 ++ .claude/skills/tiny-blocks-consume/SKILL.md | 68 ++ .../tiny-blocks-consume/references/catalog.md | 32 + .../scripts/refresh-catalog.py | 102 ++ .claude/skills/tiny-blocks-create/SKILL.md | 158 +++ .../assets/config/.editorconfig | 19 + .../assets/config/.gitattributes | 19 + .../assets/config/.gitignore | 28 + .../tiny-blocks-create/assets/config/Makefile | 74 ++ .../assets/config/composer.json | 70 ++ .../assets/config/infection.json.dist | 23 + .../assets/config/phpcs.xml | 7 + .../assets/config/phpstan.neon.dist | 6 + .../assets/config/phpunit.xml | 39 + .../assets/docs/SECURITY.md | 12 + .../github/ISSUE_TEMPLATE/bug_report.md | 29 + .../github/ISSUE_TEMPLATE/feature_request.md | 17 + .../assets/github/PULL_REQUEST_TEMPLATE.md | 16 + .../assets/github/workflows/ci.yml | 105 ++ CLAUDE.md | 61 + 32 files changed, 4099 insertions(+), 408 deletions(-) delete mode 100644 .claude/CLAUDE.md create mode 100644 .claude/hooks/php-ordering-conformance.py create mode 100644 .claude/hooks/php-prose-punctuation-conformance.py delete mode 100644 .claude/rules/github-workflows.md create mode 100644 .claude/rules/php-library-architecture.md create mode 100644 .claude/rules/php-library-github-workflows.md create mode 100644 .claude/rules/php-library-tooling.md create mode 100644 .claude/settings.json create mode 100644 .claude/skills/commit-message/SKILL.md create mode 100644 .claude/skills/tiny-blocks-consume/SKILL.md create mode 100644 .claude/skills/tiny-blocks-consume/references/catalog.md create mode 100644 .claude/skills/tiny-blocks-consume/scripts/refresh-catalog.py create mode 100644 .claude/skills/tiny-blocks-create/SKILL.md create mode 100644 .claude/skills/tiny-blocks-create/assets/config/.editorconfig create mode 100644 .claude/skills/tiny-blocks-create/assets/config/.gitattributes create mode 100644 .claude/skills/tiny-blocks-create/assets/config/.gitignore create mode 100644 .claude/skills/tiny-blocks-create/assets/config/Makefile create mode 100644 .claude/skills/tiny-blocks-create/assets/config/composer.json create mode 100644 .claude/skills/tiny-blocks-create/assets/config/infection.json.dist create mode 100644 .claude/skills/tiny-blocks-create/assets/config/phpcs.xml create mode 100644 .claude/skills/tiny-blocks-create/assets/config/phpstan.neon.dist create mode 100644 .claude/skills/tiny-blocks-create/assets/config/phpunit.xml create mode 100644 .claude/skills/tiny-blocks-create/assets/docs/SECURITY.md create mode 100644 .claude/skills/tiny-blocks-create/assets/github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .claude/skills/tiny-blocks-create/assets/github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .claude/skills/tiny-blocks-create/assets/github/PULL_REQUEST_TEMPLATE.md create mode 100644 .claude/skills/tiny-blocks-create/assets/github/workflows/ci.yml create mode 100644 CLAUDE.md diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md deleted file mode 100644 index 11885b0..0000000 --- a/.claude/CLAUDE.md +++ /dev/null @@ -1,32 +0,0 @@ -# Project - -PHP library (tiny-blocks ecosystem). Self-contained package: immutable models, zero infrastructure -dependencies in core, small public surface area. Public API at `src/` root; implementation details -under `src/Internal/`. - -## Rules - -All coding standards, architecture, naming, testing, and documentation conventions -are defined in `rules/`. Read the applicable rule files before generating any code or documentation. - -## Commands - -- `make test` — run tests with coverage. -- `make mutation-test` — run mutation testing (Infection). -- `make review` — run lint. -- `make help` — list all available commands. - -## Post-change validation - -After any code change, run `make review`, `make test`, and `make mutation-test`. -If any fails, iterate on the fix while respecting all project rules until all pass. -Never deliver code that breaks lint, tests, or leaves surviving mutants. - -## File formatting - -Every file produced or modified must: - -- Use **LF** line endings. Never CRLF. -- Have no trailing whitespace on any line. -- End with a single trailing newline. -- Have no consecutive blank lines (max one blank line between blocks). diff --git a/.claude/hooks/php-ordering-conformance.py b/.claude/hooks/php-ordering-conformance.py new file mode 100644 index 0000000..21a3ec0 --- /dev/null +++ b/.claude/hooks/php-ordering-conformance.py @@ -0,0 +1,607 @@ +#!/usr/bin/env python3 +"""PHP ordering conformance hook for tiny-blocks PHP libraries. + +Self-contained PostToolUse hook on Edit|Write|MultiEdit. Verifies the deterministic +ordering conventions for PHP declarations: + +- Parameter ordering: declaration parameters (constructors, factories, methods, + property promotion) in three tiers, required parameters first, then defaulted + parameters, then a variadic, each tier by identifier length ascending, + alphabetical tie-breaker, semantic pairs preserved. A PHPUnit test method fed by + a data provider is exempt, its parameters are the columns of its data set. +- Member ordering: constants, enum cases, constructor, static methods, instance + methods, in that group order, each group length-ascending with alphabetical + tie-breaker. PHPUnit test classes instead order methods as lifecycle hooks (in + execution order), then other methods, then data providers. + +The analysis is pure (FileUnit in, Violation out) and runs in three passes over +well-formed PHP: a lexical pass blanks every comment, string, and heredoc/nowdoc +body (LITERALS), a structural pass maps every bracket to its pair (bracket_spans); +extraction assigns tokens of interest to their containers by flat walks. Control +flow uses guard clauses only and nesting never exceeds two levels. Reports +violations to stderr and exits 2 to prompt Claude, exits 0 silently if no violations +or the file is out of scope. +""" + +import json +import re +import sys +from dataclasses import dataclass +from enum import Enum +from functools import cached_property +from pathlib import Path +from typing import Final + +# --- Configuration ---------------------------------------------------------- + +# In-scope files: PHP sources under src/ or tests/. +SCOPE_PATTERN: Final = re.compile(r"(^|/)(src|tests)/.+\.php$") + +# Semantic pairs (exhaustive). Natural order wins between +# the two members when both appear in the same parameter list. +SEMANTIC_PAIRS: Final = ( + ("start", "end"), + ("from", "to"), + ("startAt", "endAt"), + ("createdAt", "updatedAt"), + ("before", "after"), + ("min", "max"), +) + +# Each member maps to (first, second, position). Both members keep their natural +# order only when both are present, sorting as a unit at the lead member's key. +PAIR_MEMBER: Final = { + member: (first, second, position) + for first, second in SEMANTIC_PAIRS + for position, member in enumerate((first, second)) +} + +MODIFIERS: Final = ("abstract", "final", "private", "protected", "public", "static") + +# The lexical grammar: every PHP construct that must not be scanned as code. +# Alternatives are ordered, the heredoc label closes via backreference. +LITERALS: Final = re.compile( + r""" + /\*.*?\*/ # block comment + | //[^\n]* # line comment + | \#(?!\[)[^\n]* # hash comment, never a #[ attribute + | <<<[ \t]*(?P['"]?)(?P