agentleFS
ci4meci4me/CQRSTemplate/.claude/CLAUDE.md

CQRSTemplate

CLAUDE.md0 starsChanged 6 months agoMITView on GitHub

This project uses Serena MCP for semantic code intelligence. ALL code generated by AI agents MUST follow Serena optimization guidelines. Serena works at the SYMBOL level (classes, methods, properties), not the TEXT level (strings). Properly structured PHP code enables: - ✅ Instant symbol finding across the entire codebase - ✅ Precise code editing at symbol boundaries - ✅ Accurate refactoring across multiple files - ✅ Efficient navigation without reading entire files - ✅ 10x faster development with AI assistance ✅…

  • Reads credentials
  • Deletes or force-pushes
  • Commits and pushes

The file

Show its rules and text

We split this CLAUDE.md into the rules it gives an agent

62 rules. About 0 are common, 41 are distinctive, and 21 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈41

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 21

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  • could be automatic: a setting could enforce it
  1. Distinctive:

    Use clear class names (Single Responsibility Principle)

    line 21
  2. Distinctive:

    Write small methods (< 50 lines, prefer < 20 lines)

    line 22
  3. Distinctive:

    Keep flat structures (clear namespace hierarchy, PSR-4)

    line 24
  4. Distinctive:

    Add DocBlocks for all public APIs

    line 25
  5. Distinctive:

    All code reviews by agents must verify:

    line 139
  6. Distinctive:

    Never git push --force: Use git push --force-with-lease --force-if-includes (alias: git fpush).

    line 180
  7. Distinctive:

    Do not disable signing.

    line 188
  8. Very specific:

    AI-assisted commits: must include a Co-Authored-By: trailer.

    line 189
  9. Distinctive:

    Commit messages that don't match Conventional Commits

    line 201
  10. Distinctive:

    All status checks passing

    line 216
  11. Very specific:

    If any of those don't match, run bin/setup-hooks (or composer install, which runs it automatically via post-install).

    line 241
  12. Distinctive:

    BEFORE making ANY code changes, you MUST:

    line 258
  13. Very specific:

    Use phpstan-specialist to check current state

    line 259
  14. Very specific:

    Use slevomat-specialist to verify coding standards

    line 260
  15. Very specific:

    Use test-specialist if modifying existing features

    line 261
  16. Distinctive:

    WHEN writing code, you MUST:

    line 265
  17. Very specific:

    Use php-specialist for all PHP files

    line 266
  18. Very specific:

    Use clean-code-specialist for all methods/classes

    line 267
  19. Very specific:

    Use cqrs-specialist for commands/queries/events/handlers

    line 268
  20. Very specific:

    Use ddd-specialist for entities/value objects

    line 269
  21. Very specific:

    Use codeigniter4-specialist for migrations/controllers/routing

    line 270
  22. Very specific:

    Use test-specialist when adding or modifying features

    line 271
  23. Distinctive:

    AFTER making changes, you MUST:

    line 275
  24. Very specific:

    Run phpstan-specialist (MUST pass Level 8 with 0 errors)

    line 276
  25. Very specific:

    Run slevomat-specialist (MUST pass with 0 violations)

    line 277
  26. Very specific:

    Run test-specialist (MUST maintain 90% coverage)

    line 278
  27. Distinctive:

    Every task MUST satisfy SMART-E criteria:

    line 330
  28. Distinctive:

    When user asks: *"Do I have any unfinished tasks?"* or *"Resume my tasks"*

    line 368
  29. Very specific:

    Read .claude/planning/current/execution.json for complete state

    line 384
  30. Very specific:

    Read .claude/planning/current/tasks.json for task definitions

    line 385
  31. Very specific:

    Check todowrite_mapping to find pending tasks

    line 386
  32. Distinctive:

    Verify any claimed completions using verification_command

    line 391
  33. Distinctive:

    Update execution state as progress is made

    line 393
  34. Distinctive:

    Verify what's completed: - Check TodoWrite and execution log:

    line 402
  35. Distinctive:

    Use verification commands to confirm claimed completions

    line 405
  36. Distinctive:

    Start from that task's group

    line 409
  37. Distinctive:

    Update execution state as you progress

    line 410
  38. Distinctive:

    ALWAYS use native tools: instead of bash commands for file operations:

    line 438
  39. Distinctive:

    Do NOT ask for permission to use Read, Write, Edit, Glob, Grep tools

    line 454
  40. Distinctive:

    Do NOT wait for user confirmation to invoke specialists

    line 455
  41. Distinctive:

    Do proceed with quality fixes automatically (phpcbf, etc.)

    line 456
  42. Distinctive:

    Do execute multiple specialists in parallel without asking

    line 457
  43. Distinctive:

    Do reject non-compliant code automatically (don't ask)

    line 458
  44. Very specific:

    Use claude-code-specialist agent for guidance

    line 496
  45. Very specific:

    Follow existing patterns in .claude/agents/, .claude/skills/, .claude/commands/

    line 497
  46. Distinctive:

    Test with simple example first

    line 498
  47. Distinctive:

    Document which other specialists to collaborate with

    line 505
  48. Distinctive:

    All logs are in JSON format for AI parsing and analysis.

    line 608
  49. Distinctive:

    Use dot notation for channel names:

    line 667
  50. Distinctive:

    Use appropriate log levels:

    line 733
  51. Distinctive:

    Only replace LoggingServiceProvider with framework-specific adapter

    line 761
  52. Distinctive:

    All handlers keep their logging code

    line 762
  53. Distinctive:

    Value Objects and Entities: do NOT use DomainLogger directly.

    line 884
  54. Distinctive:

    All parameters have type declarations

    line 1033
  55. Distinctive:

    All return types declared

    line 1034
  56. Very specific:

    Use === for all comparisons

    line 1036
  57. Distinctive:

    No business logic in controllers

    line 1043
  58. Distinctive:

    All code uses guard clauses and early returns for readability.

    line 1131
  59. Very specific:

    All code uses declare(strict_types=1) and full type hints (PHPStan level 8).

    line 1134
  60. Distinctive:

    All entities support soft deletion (deleted_at timestamp).

    line 1137
  61. Distinctive:

    All DTOs are readonly classes for thread safety and clarity.

    line 1140
  62. Distinctive:

    Use Cookie domain as a reference for all new domains.

    line 1202

≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
CLAUDE.md

Project Memory - CodeIgniter 4 CQRS Template

🎯 CRITICAL: Serena-Optimized Code (MANDATORY)

This project uses Serena MCP for semantic code intelligence.

ALL code generated by AI agents MUST follow Serena optimization guidelines.

Why Serena Optimization Matters

Serena works at the SYMBOL level (classes, methods, properties), not the TEXT level (strings). Properly structured PHP code enables:

  • ✅ Instant symbol finding across the entire codebase
  • ✅ Precise code editing at symbol boundaries
  • ✅ Accurate refactoring across multiple files
  • ✅ Efficient navigation without reading entire files
  • ✅ 10x faster development with AI assistance

Mandatory Code Patterns (PHP)

✅ ALWAYS:

  • Use clear class names (Single Responsibility Principle)
  • Write small methods (< 50 lines, prefer < 20 lines)
  • Give descriptive names (PSR-12 compliant, self-documenting)
  • Keep flat structures (clear namespace hierarchy, PSR-4)
  • Add DocBlocks for all public APIs
  • One clear symbol per logical unit
  • Strict types (declare(strict_types=1))
  • Type hints for all parameters and returns
  • Readonly for Value Objects
  • Final classes by default

❌ NEVER:

  • Generic service classes (UserService with 50 methods)
  • Anonymous classes or closures as primary logic
  • Deeply nested structures
  • Mega-classes (> 300 lines)
  • Dynamic method calls ($obj->$method())
  • Static helper classes with mixed responsibilities
  • Missing DocBlocks
  • Generic names (Utils, Helper, Manager)

CQRS-Specific Symbol Patterns

Commands (Write Operations):

// ✅ GOOD - Each command is a clear, discoverable symbol
final readonly class CreateUserCommand
{
    public function __construct(
        public string $email,
        public string $name
    ) {}
}

final readonly class CreateUserCommandHandler
{
    public function handle(CreateUserCommand $command): UserId
    {
        $this->validateCommand($command);
        $user = $this->createUser($command);
        $this->saveUser($user);
        return $user->getId();
    }

    private function validateCommand(CreateUserCommand $command): void {}
    private function createUser(CreateUserCommand $command): User {}
    private function saveUser(User $user): void {}
}

Value Objects:

// ✅ GOOD - Immutable, self-validating, discoverable
final class Email
{
    private function __construct(private readonly string $value)
    {
        $this->validate();
    }

    public static function fromString(string $email): self
    {
        return new self($email);
    }

    public function getValue(): string
    {
        return $this->value;
    }

    private function validate(): void
    {
        if (!$this->isValidFormat()) {
            throw new \InvalidArgumentException('Invalid email');
        }
    }

    private function isValidFormat(): bool
    {
        return filter_var($this->value, FILTER_VALIDATE_EMAIL) !== false;
    }
}

Complete Guidelines

See: .claude/SERENA_CODE_OPTIMIZATION.md for comprehensive PHP-specific guidelines, examples, and patterns.

AI agents MUST review this document before generating code.

serena-code-generator Skill

The serena-code-generator skill is available for this project.

This skill provides templates for:

  • Value Objects (immutable, validated)
  • Commands and Command Handlers (CQRS)
  • Queries and Query Handlers (CQRS)
  • Domain Entities (DDD patterns)
  • Controllers (CodeIgniter4)

Usage: The skill activates automatically when generating new code.

serena-code-assistant Agent

The serena-code-assistant agent uses Serena MCP for semantic code navigation.

Available operations:

  • find_symbol("ClassName") → Find exact class
  • find_symbol("ClassName/methodName") → Find specific method
  • find_referencing_symbols("ClassName") → Find all usages
  • replace_symbol_body("method") → Replace method implementation
  • insert_after_symbol("method") → Add code after method

Example: "Find all usages of CreateUserCommand and add logging"

Enforcement

All code reviews by agents must verify:

  • Named classes with clear purposes
  • Methods < 50 lines (prefer < 20 lines)
  • Descriptive symbol names (PSR-12 compliant)
  • Flat namespace structure (PSR-4)
  • DocBlocks for all public APIs
  • No circular dependencies
  • Clear module boundaries
  • Strict type declarations
  • CQRS/DDD patterns followed

Serena-incompatible code will be REJECTED.

Integration with Existing Specialists

Serena optimization works WITH existing specialists:

  • php-specialist → Ensures PHP syntax + Serena patterns
  • cqrs-specialist → CQRS patterns + Serena symbols
  • ddd-specialist → DDD patterns + Serena value objects
  • clean-code-specialist → Small methods + Serena structure

🔒 Git Conventions (MANDATORY)

This project enforces git policy at THREE layers. All three must pass.

LayerWhereSkippable?
Local git hooks.githooks/--no-verify (blocked)
Claude Code PreToolUse hook.claude/hooks/git-guard.shenv-var GIT_GUARD_DISABLE=1
GitHub status checks + ruleset.github/workflows/ + rulesetserver-side, no bypass

Hard rules — all agents

  1. Use the git-specialist agent for any git operation. It enforces the rules in this section. Other specialists must delegate to it.
  2. Conventional Commits 1.0 — required. Format: type(scope)[!]: subject. Allowed types: feat | fix | docs | style | refactor | perf | test | build | ci | chore | revert. Allowed scopes are listed in .commitlintrc.json (scope-enum).
  3. Never use --no-verify. Fix the hook complaint instead.
  4. Never git push --force. Use git push --force-with-lease --force-if-includes (alias: git fpush).
  5. Never commit directly to main unless the user says the exact phrase "commit directly to main". Default flow: branch → commit → PR.
  6. Branch names: <type>/<scope>-<short-description>, kebab-case. Include the issue number when one exists: fix/order-123-negative-qty. The prepare-commit-msg hook auto-prefixes subjects from this.
  7. Signed commits: commits are SSH-signed via ~/.ssh/git_signing_ed25519. Do not disable signing.
  8. AI-assisted commits must include a Co-Authored-By: trailer.

What the layers block

The local hooks + git-guard.sh reject (with helpful hints):

  • git commit --no-verify ...
  • git push --no-verify ...
  • git push --force / git push -f
  • git reset --hard origin/<branch> (silent work loss)
  • git config --global / --system (must stay local)
  • Commits on main / master
  • Commit messages that don't match Conventional Commits
  • Staged files matching .env*, secrets/, *.pem, *.p12, *.key
  • Real secrets found by gitleaks content scan

Server-side enforcement

On every push and PR to main:

  • ci.yml — PHPStan L8, PHPCS, PHPUnit (≥90% coverage), gitleaks
  • commitlint.yml — re-validates every commit message in the PR
  • dependency-review.yml — blocks high-severity CVEs + non-allowed licenses
  • codeql.yml — SAST on Actions and JS/TS
  • scorecard.yml — weekly OpenSSF supply-chain score
  • Repository ruleset on main requires:
    • PR (no direct pushes)
    • All status checks passing
    • Linear history, no force-pushes, no deletions, signed commits

Aliases worth knowing

git lg          # pretty graph log
git recent      # branches sorted by last activity
git sync        # pull --rebase --autostash && push
git cleanup     # delete branches already merged into main
git fpush       # push --force-with-lease --force-if-includes
git aliases     # list every alias defined in this repo
git staged      # show staged diff
git wipe        # save a WIP commit then drop it (safe scratch reset)

Verify your setup

git config --local --get core.hooksPath        # → .githooks
git config --local --get commit.gpgsign        # → true
git config --local --get user.signingkey       # → ~/.ssh/git_signing_ed25519.pub
git log --show-signature -1                    # last commit signed?

If any of those don't match, run bin/setup-hooks (or composer install, which runs it automatically via post-install).

See also

  • .claude/agents/git-specialist.md — full playbook
  • .github/CONTRIBUTING.md — developer flow
  • .githooks/ — local hook scripts
  • .claude/hooks/git-guard.sh — agent-level enforcement
  • .gitmessage — commit template

Mandatory Agent Usage (Orchestrator Pattern)

Pre-Execution Rules

BEFORE making ANY code changes, you MUST:

  • Use phpstan-specialist to check current state
  • Use slevomat-specialist to verify coding standards
  • Use test-specialist if modifying existing features

Execution Rules

WHEN writing code, you MUST:

  • Use php-specialist for all PHP files
  • Use clean-code-specialist for all methods/classes
  • Use cqrs-specialist for commands/queries/events/handlers
  • Use ddd-specialist for entities/value objects
  • Use codeigniter4-specialist for migrations/controllers/routing
  • Use test-specialist when adding or modifying features

Post-Execution Rules

AFTER making changes, you MUST:

  • Run phpstan-specialist (MUST pass Level 8 with 0 errors)
  • Run slevomat-specialist (MUST pass with 0 violations)
  • Run test-specialist (MUST maintain 90% coverage)

Orchestrator Pattern - Parallel Delegation

For complex tasks, AUTOMATICALLY delegate to 2-3 specialists in PARALLEL:

  • Add new property → ddd-specialist + test-specialist + clean-code-specialist
  • Create new domain → cqrs-specialist + ddd-specialist + test-specialist
  • Refactor code → clean-code-specialist + php-specialist + test-specialist
  • Add business rule → ddd-specialist + test-specialist + phpstan-specialist
  • Fix bug → test-specialist + php-specialist + phpstan-specialist

Rejection Policy

REJECT any code changes that:

  • Fail PHPStan Level 8
  • Fail Slevomat checks with any violations
  • Reduce test coverage below 90%
  • Have methods exceeding 20 lines
  • Violate CQRS/DDD patterns
  • Lack tests for new features

Mandatory Planning Rules (Strategic Planner Skill)

CRITICAL: Always use the strategic-planner skill for ANY planning task.

Automatically invoke strategic-planner when:

  • User asks "How do I implement [feature]?"
  • User requests "Create a plan for [task]"
  • User says "Break down [complex work]"
  • User asks "What steps are needed to [goal]?"
  • User requests todo list generation
  • Task involves 5+ steps or multiple files (15+)
  • Task is HIGH or CRITICAL risk (auth, payment, data migration)
  • Task is COMPLEX or VERY COMPLEX

The skill provides:

  • 🌳 Tree-of-Thought: Explores 2-3 solution approaches for complex tasks
  • 💭 Chain-of-Thought: Shows transparent reasoning process
  • ⚛️ SMART-E Atomic Tasks: Generates precise, executable task definitions
  • 🐍 Python Validation: Atomicity validator + dependency analyzer
  • 📊 Critical Path Optimization: 25-50% time savings via parallelization
  • 🎓 Pattern Learning: Improves recommendations over time

5-Phase Planning Process:

  1. Understanding & Analysis (Chain-of-Thought)
  2. Explore Solution Space (Tree-of-Thought for complex tasks)
  3. Risk Assessment (Pre-Mortem for HIGH/CRITICAL risk)
  4. Generate SMART-E Atomic Tasks (with all 6 criteria + 5 atomicity rules)
  5. Python Validation & Optimization (critical path analysis)

SMART-E + Atomicity Requirements: Every task MUST satisfy SMART-E criteria:

  • Specific, Measurable, Achievable, Relevant, Testable, Executable

AND 5 Atomicity Rules:

  • ≤3 files | <30 min | Binary done | Independent verify | Single responsibility

Rejection Policy: REJECT and RE-PLAN if Python validator returns FAIL or detects:

  • ❌ Atomicity violations
  • ❌ Circular dependencies
  • ❌ Missing SMART-E criteria
  • ❌ No verification commands

Quick Example:

User: "Add payment system"
→ Invoke: Skill strategic-planner
→ Phase 1: CoT understanding (VERY COMPLEX, HIGH risk)
→ Phase 2: ToT exploration (Stripe vs PayPal vs Custom)
→ Phase 3: Pre-Mortem (7 risks identified, mitigated)
→ Phase 4: 47 SMART-E atomic tasks generated
→ Phase 5: Python validation (PASS, 42 min critical path, 50% time savings)
→ Present plan → User confirms → Execute with TodoWrite

Progressive Disclosure: The skill uses lightweight core documentation with detailed references:

  • Core: .claude/skills/strategic-planner/SKILL.md (~300 lines)
  • Frameworks: SMART-E, Atomicity, ToT, CoT (separate files)
  • Guides: Python scripts, TodoWrite integration
  • Examples: Simple (3 tasks) and complex (47 tasks) scenarios

See: .claude/skills/strategic-planner/SKILL.md for complete documentation

Resuming Interrupted Executions

Automatic Unfinished Task Detection:

When user asks: "Do I have any unfinished tasks?" or "Resume my tasks"

Claude Code should automatically:

  1. Check for unfinished executions:
    # Check if current symlink exists and points to active execution
    if [ -L .claude/planning/current ]; then
        STATUS=$(jq -r '.status' .claude/planning/current/execution.json 2>/dev/null)
        if [ "$STATUS" = "in_progress" ] || [ "$STATUS" = "paused" ]; then
            echo "Found unfinished execution!"
        fi
    fi
  2. Load execution context:
    • Read .claude/planning/current/execution.json for complete state
    • Read .claude/planning/current/tasks.json for task definitions
    • Check todowrite_mapping to find pending tasks
  3. Resume from next pending task:
    • Find first task with "status": "pending" in todowrite_mapping
    • Load task details from tasks.json
    • Verify any claimed completions using verification_command
    • Continue execution from current_group
    • Update execution state as progress is made

Quick Recovery (3 steps):

  1. Read execution state - Get complete context:
    cat .claude/planning/current/execution.json
  2. Verify what's completed - Check TodoWrite and execution log:
    • TodoWrite shows user-visible progress with [task_X.Y] references
    • Execution state shows completed_tasks, pending_tasks, current_group
    • Use verification commands to confirm claimed completions
  3. Resume from next task - Continue execution:
    • Find first pending task in todowrite_mapping
    • Start from that task's group
    • Update execution state as you progress

Check for unfinished executions manually:

# List all in-progress or paused executions
find .claude/planning/executions -name "execution.json" -exec sh -c \
  'jq -r "select(.status == \"in_progress\" or .status == \"paused\") | .execution_id" "$1"' _ {} \;

# Or check current execution
jq -r '.status, .plan_name, .completed_tasks, .total_tasks' .claude/planning/current/execution.json

All execution history is preserved:

  • Execution folders never deleted: .claude/planning/executions/exec-{id}/
  • Complete state in execution.json
  • Task definitions in tasks.json
  • Optional metadata in metadata.json
  • Symlink for easy access: .claude/planning/current/

Validate execution integrity:

python .claude/skills/strategic-planner/scripts/validate_execution.py exec-{id}

See: .claude/documentation/TASK-RECOVERY-GUIDE.md for detailed recovery workflows

Tool Usage for Maximum Autonomy

ALWAYS use native tools instead of bash commands for file operations:

File Operations:

  • ✅ Use Read tool to read files (NOT cat, head, tail)
  • ✅ Use Write tool to create files (NOT echo >, cat <<EOF)
  • ✅ Use Edit tool to modify files (NOT sed, awk)
  • ✅ Use Glob tool to find files (NOT find, ls)
  • ✅ Use Grep tool to search code (NOT grep, rg)

Bash Tool Reserved For:

  • ✅ Running commands: composer, php spark, vendor/bin/*
  • ✅ Database operations: mysql
  • ✅ Git operations: git status, git diff
  • ✅ Build tools: npm, webpack

Autonomous Behavior:

  • Do NOT ask for permission to use Read, Write, Edit, Glob, Grep tools
  • Do NOT wait for user confirmation to invoke specialists
  • Do proceed with quality fixes automatically (phpcbf, etc.)
  • Do execute multiple specialists in parallel without asking
  • Do reject non-compliant code automatically (don't ask)

Be PROACTIVE and AUTONOMOUS:

  • See a PHP file? → Automatically invoke php-specialist
  • See a long method? → Automatically invoke clean-code-specialist
  • See missing tests? → Automatically invoke test-specialist and create them
  • See PHPStan errors? → Automatically fix them
  • See Slevomat violations? → Run phpcbf and fix automatically

File Organization Rules (MANDATORY)

Documentation Files:

  • ❌ NEVER create .md files in root directory (summaries, reports, etc.)
  • ✅ ALWAYS update existing .md files in .claude/documentation/
  • ✅ Add new sections to existing files instead of creating new ones
  • ✅ If truly new topic, create in .claude/documentation/ only

Code Files:

  • ❌ NEVER create .php or .sh files in root directory
  • ✅ ALWAYS use temp/ folder for temporary/experimental files
  • ✅ Create temp/ folder if it doesn't exist: mkdir -p temp
  • ✅ Production code goes in proper domain directories

Examples:

# ❌ Wrong
Write(SUMMARY.md, ...)           # Root directory
Write(test-script.php, ...)      # Root directory

# ✅ Correct
Edit(.claude/documentation/COMPLETE_FILE_INVENTORY.md, ...)  # Update existing
Write(temp/experimental.php, ...)  # Temporary files

Creating New Agents, Skills, and Commands

Before creating new agent/skill/command:

  • Research Claude Code documentation for best practices
  • Use claude-code-specialist agent for guidance
  • Follow existing patterns in .claude/agents/, .claude/skills/, .claude/commands/
  • Test with simple example first

Agent Creation Guidelines:

  • YAML frontmatter with name, description, tools
  • Concise description with "Use PROACTIVELY" or "MUST BE USED"
  • Max 200 lines total (focused responsibility)
  • Include common violations & fixes section
  • Document which other specialists to collaborate with

Skill Creation Guidelines:

  • SKILL.md with YAML frontmatter
  • Supporting files in same directory
  • Multi-step workflows that reuse specialists
  • Include examples and usage documentation

Command Creation Guidelines:

  • Markdown file with clear command syntax
  • Interactive prompts if needed
  • Delegate to skills or specialists
  • Include usage examples

Use claude-code-specialist for creating new Claude Code extensions.


Architecture Quick Reference

CQRS Pattern

Commands (Write Operations)

  • Represent intent to change system state
  • Named in imperative (CreateCookie, UpdateCookie)
  • Handled by exactly ONE handler
  • May dispatch domain events
  • Located in: app/Domain/{Domain}/Commands/{CommandName}/
  • Each command has its own folder containing Command + Handler

Queries (Read Operations)

  • Represent requests for data (no state changes)
  • Named as questions (GetCookieById, GetAllCookies)
  • Handled by exactly ONE handler
  • NEVER modify state
  • Located in: app/Domain/{Domain}/Queries/{QueryName}/
  • Each query has its own folder containing Query + Handler

Events (Domain Events)

  • Represent things that happened
  • Named in past tense (CookieCreated, CookieDeleted)
  • Can have MULTIPLE listeners
  • Immutable (readonly class)
  • Located in: app/Domain/{Domain}/Events/{EventName}/
  • Each event has its own folder with Event + EventHandler

Directory Structure

app/Domain/{Domain}/
├── {Domain}ServiceProvider.php     # Auto-discovered with #[DomainServiceProvider]
├── Commands/
│   ├── Create{Entity}/
│   │   ├── Create{Entity}Command.php
│   │   └── Create{Entity}Handler.php
│   ├── Update{Entity}/
│   └── Delete{Entity}/
├── Queries/
│   ├── Get{Entity}ById/
│   │   ├── Get{Entity}ByIdQuery.php
│   │   └── Get{Entity}ByIdHandler.php
│   ├── GetAll{Entities}/
│   └── Get{Entities}Paginated/
├── Events/
│   ├── {Entity}Created/
│   │   ├── {Entity}CreatedEvent.php
│   │   └── {Entity}CreatedEventHandler.php
│   ├── {Entity}Updated/
│   └── {Entity}Deleted/
├── Entities/
│   └── {Entity}.php
└── ValueObjects/
    ├── {Entity}Name.php
    └── {Entity}Price.php

Complete File List

45+ files/touchpoints per domain:

  • See .claude/documentation/COMPLETE_FILE_INVENTORY.md for detailed list

Logging Architecture

Framework-Agnostic Logging with Monolog

This template uses Monolog for logging, ensuring the logging infrastructure survives framework changes.

Key Components:

  • LoggerFactory - PSR-3 compliant factory for creating logger instances
  • LoggingServiceProvider - CodeIgniter service provider (framework adapter)
  • Local Logs MCP Server - AI-readable log access via Model Context Protocol

Log File Locations

writable/logs/
├── app-2025-10-22.json          # Application logs (JSON format)
├── app-2025-10-23.json          # Daily rotation (30-day retention)
└── .gitkeep

All logs are in JSON format for AI parsing and analysis.

Using Logging in CQRS Handlers

Inject PSR-3 LoggerInterface in handler constructors:

use Psr\Log\LoggerInterface;

final readonly class CreateCookieHandler implements CommandHandlerInterface
{
    public function __construct(
        private CookieRepository $repository,
        private EventDispatcherInterface $eventDispatcher,
        private LoggerInterface $logger,  // ← Inject logger
    ) {}

    public function handle(CommandInterface $command): int
    {
        assert($command instanceof CreateCookieCommand);

        // Log command start with context
        $this->logger->info('Creating cookie', [
            'domain' => 'Cookie',
            'command' => 'CreateCookieCommand',
            'cookieName' => $command->name,
            'price' => $command->price,
        ]);

        try {
            $cookie = Cookie::create(/* ... */);
            $cookieId = $this->repository->save($cookie);

            // Log success
            $this->logger->info('Cookie created successfully', [
                'domain' => 'Cookie',
                'command' => 'CreateCookieCommand',
                'cookieId' => $cookieId,
            ]);

            return $cookieId;

        } catch (\Throwable $e) {
            // Log errors
            $this->logger->error('Failed to create cookie', [
                'domain' => 'Cookie',
                'command' => 'CreateCookieCommand',
                'exception' => $e->getMessage(),
                'exceptionClass' => $e::class,
            ]);

            throw $e;
        }
    }
}

Log Channel Naming Convention

Use dot notation for channel names:

  • {domain}.command.{action} - Commands (e.g., cookie.command.create)
  • {domain}.query.{action} - Queries (e.g., cookie.query.getById)
  • {domain}.event.{action} - Events (e.g., cookie.event.created)

Example:

$logger = LoggerFactory::create('cookie.command.create');

JSON Log Format

Each log entry contains:

{
  "message": "Creating cookie",
  "context": {
    "domain": "Cookie",
    "command": "CreateCookieCommand",
    "cookieName": "Chocolate Chip",
    "price": 9.99
  },
  "level": 200,
  "level_name": "INFO",
  "channel": "cookie.command.create",
  "datetime": "2025-10-22T10:30:00.000000+00:00",
  "extra": {
    "memory_usage": "2MB"
  }
}

AI Access via MCP Server

The Local Logs MCP Server enables AI assistants to read and analyze logs in real-time.

Configuration: See .claude/documentation/MCP_SERVER_CONFIG.md

AI Query Examples:

"Show me the last 50 lines from today's application log"
"Find all ERROR level logs from the Cookie domain"
"What happened with CreateCookieCommand in the last hour?"
"Show me logs related to cookie creation failures"

Testing Logging

Test logger directly:

php temp/test-logger-directly.php

Verify JSON format:

head -1 writable/logs/app-$(date +%Y-%m-%d).json | python3 -m json.tool

Check log file:

tail -f writable/logs/app-$(date +%Y-%m-%d).json

Log Levels

Use appropriate log levels:

  • DEBUG: Detailed debug information
  • INFO: Interesting events (command execution, success)
  • WARNING: Exceptional occurrences that are not errors
  • ERROR: Runtime errors that should be logged but don't require immediate action
  • CRITICAL: Critical conditions (database unavailable, etc.)

CQRS Context Processor

LoggerFactory automatically adds CQRS context when using channel naming convention:

Channel: "cookie.command.create"
→ Extracts: domain=Cookie, command=create

This makes logs queryable by domain and operation type.

Framework Independence

Why Monolog?

  • ✅ PSR-3 compliant (PHP standard)
  • ✅ Works with any framework (Laravel, Symfony, CI4, or standalone)
  • ✅ 50+ handlers (files, databases, cloud services, Slack, etc.)
  • ✅ Mature and battle-tested

What happens if you remove CodeIgniter?

  • LoggerFactory continues to work unchanged
  • Only replace LoggingServiceProvider with framework-specific adapter
  • All handlers keep their logging code

Error Codes

Standardized error codes enable:

  • Automated alerting and monitoring
  • Error categorization and metrics
  • AI-based log analysis
  • Client-side error handling

Error Code Registry (app/Domain/Cookie/ErrorCodes.php):

// Validation errors (100-199)
public const int COOKIE_VALIDATION_NAME = 101;
public const int COOKIE_VALIDATION_PRICE = 102;

// Not found errors (200-299)
public const int COOKIE_NOT_FOUND = 201;

// Business rule violations (300-399)
public const int COOKIE_BUSINESS_RULE_STOCK_NEGATIVE = 301;

// Repository errors (400-499)
public const int COOKIE_REPOSITORY_SAVE_FAILED = 401;

Usage in Exceptions:

// All exceptions now support error codes
throw ValidationException::required('name', ErrorCodes::COOKIE_VALIDATION_NAME);
throw DomainException::businessRuleViolation('Stock negative', 'details', ErrorCodes::COOKIE_BUSINESS_RULE_STOCK_NEGATIVE);

// Retrieve error code
$errorCode = $exception->getErrorCode();

Usage in Logging:

$this->logger->error('Validation failed', [
    'domain' => 'Cookie',
    'error_code' => ErrorCodes::COOKIE_VALIDATION_NAME,
    'attempted_value' => $value,
]);

Correlation IDs

Correlation IDs enable distributed request tracing across multiple handlers, repositories, and events.

Auto-Injection: The CorrelationIdProcessor automatically adds correlation_id to ALL log entries:

{
  "message": "Cookie created",
  "context": {"domain": "Cookie", "cookie_id": 123},
  "extra": {
    "correlation_id": "3fd536da-71a6-4e85-8b62-769d4c86b99b"
  }
}

Manual Usage:

use App\Infrastructure\Logging\CorrelationIdService;

// Generate new correlation ID
$correlationId = CorrelationIdService::generate();

// Get current correlation ID
$currentId = CorrelationIdService::get();

// Set specific correlation ID (e.g., from HTTP header)
CorrelationIdService::set($incomingCorrelationId);

Request Lifecycle Example:

  1. HTTP Request arrives → Generate correlation ID
  2. Command handler executes → Logs include correlation ID
  3. Event dispatched → Same correlation ID
  4. Event handler executes → Same correlation ID
  5. Repository queries → Same correlation ID

Result: Entire request traceable with single ID

Query Logging Configuration

Configuration File (app/Config/Logging.php):

public string $queryLoggingLevel = 'errors'; // 'all'|'errors'|'slow'|'sampling'
public int $slowQueryThresholdMs = 100;
public float $samplingRate = 0.01; // 1%
public bool $correlationIdEnabled = true;
public bool $businessMetricsEnabled = true;

Logging Levels:

  • all - Log every query (development only)
  • errors - Log only failed queries (recommended for production)
  • slow - Log only slow queries (>threshold)
  • sampling - Log random sample (e.g., 1%)

Implementation Pattern:

private function shouldLog(Config\Logging $config, float $duration, mixed $result): bool
{
    // Always log slow queries
    if ($duration > $config->slowQueryThresholdMs) {
        return true;
    }

    return match ($config->queryLoggingLevel) {
        'all' => true,
        'errors' => $result === null,
        'slow' => false,
        'sampling' => (mt_rand() / mt_getrandmax()) < $config->samplingRate,
        default => false
    };
}

Domain Objects Logging

Value Objects and Entities do NOT use DomainLogger directly. Validation exceptions thrown by value objects and entities are logged by the command handler's catch block. This keeps domain objects free of infrastructure dependencies.

// Value objects throw exceptions (logged by handler):
throw ValidationException::required('name', ErrorCodes::COOKIE_VALIDATION_NAME);

// Entities throw exceptions (logged by handler):
throw DomainException::businessRuleViolation(
    'Stock cannot be negative',
    sprintf('Attempted to decrease stock by %d when only %d available', $quantity, $this->stock),
    ErrorCodes::COOKIE_BUSINESS_RULE_STOCK_NEGATIVE
);

Cookie Domain: Logging Template

The Cookie domain is the reference implementation for all logging patterns. All logging features are fully implemented:

✅ Command handlers with duration tracking and error codes ✅ Query handlers with configurable logging ✅ Event handlers with PSR-3 logging ✅ Repository with database error, slow query, and business metrics logging ✅ Value objects with validation logging ✅ Entities with business rule logging ✅ Correlation IDs automatically injected ✅ Error codes in all exceptions ✅ Comprehensive test coverage

Use Cookie domain as template when creating new domains.

Testing Logging

Run comprehensive logging test suite:

php temp/test-comprehensive-logging.php

Tests verify:

  • JSON format and structure
  • Correlation IDs present
  • Error codes functional
  • Value object validation logging
  • Entity business rule logging
  • AI parsability

Related Documentation


Common Commands

Quality Checks (MUST pass before commit)

# PHPStan Level 8 (MUST return 0 errors)
composer phpstan

# Slevomat + PSR-12 (MUST return 0 violations)
composer phpcs

# All tests (MUST maintain 90% coverage)
composer test

# Run all checks together
composer check

Development

# Start dev server
php spark serve

# Run migrations
php spark migrate --all

# Run tests with readable output
vendor/bin/phpunit --testdox

# Run tests with coverage
vendor/bin/phpunit --coverage-text

# Auto-fix coding standards
vendor/bin/phpcbf

Domain Creation (AI-Optimized Workflows)

# Use slash command (fastest)
/add-domain Order

# Or request skill
Use domain-scaffolding skill to create complete Order domain

# Manual creation (not recommended)
See ADDING_DOMAINS.md for step-by-step guide

Property Addition (AI-Optimized Workflows)

# Use slash command (fastest)
/add-property Cookie flavor string

# Or request skill
Use property-addition skill to add flavor property to Cookie

# Manual modification (not recommended)
See MODIFYING_ENTITIES.md for checklist

Project Conventions (Ubiquitous Language)

Naming Standards

  • Commands: Create{Entity}Command, Update{Entity}Command, Delete{Entity}Command
  • Queries: Get{Entity}ByIdQuery, GetAll{Entities}Query, Get{Entities}PaginatedQuery
  • Events: {Entity}CreatedEvent, {Entity}UpdatedEvent, {Entity}DeletedEvent
  • Handlers: {Command/Query/Event}Handler
  • Value Objects: {Entity}{Property} (e.g., CookieName, CookiePrice)
  • Entities: {Entity} (e.g., Cookie, Order)
  • Repositories: {Entity}Repository
  • Models: {Entity}Model

Code Quality Rules (Enforced by Specialists)

Method-Level:

  • Max 20 lines per method (including braces)
  • Max 3 parameters (use DTOs/commands for more)
  • Early returns (no else after return)
  • Guard clauses for validation
  • Descriptive names (verb + noun)

Class-Level:

  • Max 200 lines per class
  • Single Responsibility Principle
  • Final by default (unless designed for extension)
  • Private by default (encapsulation)

Type Safety:

  • declare(strict_types=1) in all files
  • All parameters have type declarations
  • All return types declared
  • No mixed type unless necessary
  • Use === for all comparisons
  • Readonly value objects

CQRS/DDD:

  • Commands/Queries/Events are readonly DTOs
  • Value Objects are immutable and self-validating
  • Entities use factory methods (create/reconstitute)
  • No business logic in controllers
  • Repository pattern for data access

Auto-Discovery System

Zero-Configuration Domain Registration

This template uses native PHP attributes for automatic domain registration:

  1. Create domain folder: app/Domain/{YourDomain}/
  2. Create ServiceProvider with #[DomainServiceProvider] attribute
  3. Done! System automatically discovers and registers everything

Example:

use App\Infrastructure\Attributes\DomainServiceProvider;

#[DomainServiceProvider]  // <-- This attribute enables auto-discovery!
final class OrderServiceProvider implements DomainServiceProviderInterface
{
    // Registration methods...
}

No editing Services.php or any central configuration files!


Testing Strategy

Test Pyramid (Enforced by test-specialist)

  • 70% Unit Tests - Handlers, Value Objects, Entities, Events
  • 20% Integration Tests - Repositories with database
  • 10% Feature Tests - Complete HTTP request flow

Minimum Coverage: 90%

Run coverage: vendor/bin/phpunit --coverage-text

Test Locations

tests/
├── Unit/
│   └── Domain/{Domain}/
│       ├── ValueObjects/
│       ├── Entities/
│       ├── Commands/
│       ├── Queries/
│       └── Events/
├── Integration/
│   └── Repositories/
└── Feature/
    └── {Domain}/

Import Additional Documentation

For detailed protocols and guidelines, see:

  • Domain Creation: .claude/documentation/DOMAIN_CREATION_PROTOCOL.md
  • Property Addition: .claude/documentation/PROPERTY_ADDITION_PROTOCOL.md
  • Business Rules: .claude/documentation/BUSINESS_RULE_PROTOCOL.md
  • Testing Guidelines: .claude/documentation/TESTING_GUIDELINES.md
  • Architecture Decisions: .claude/documentation/ARCHITECTURE_DECISIONS.md
  • Complete File List: .claude/documentation/COMPLETE_FILE_INVENTORY.md
  • Git Workflow: .claude/documentation/GIT_WORKFLOW.md (hooks, signing, ruleset, releases)

Key Design Decisions

1. No Business Logic in Controllers

Controllers ONLY orchestrate commands/queries via buses. All business rules are in handlers.

2. Repository Pattern

Repositories abstract data access and return domain entities, not arrays.

3. Value Objects for Validation

Properties with validation rules (e.g., CookieName, CookiePrice) are Value Objects.

4. Early Returns, No Else Statements

All code uses guard clauses and early returns for readability.

5. Strict Type Safety

All code uses declare(strict_types=1) and full type hints (PHPStan level 8).

6. Soft Deletes

All entities support soft deletion (deleted_at timestamp).

7. Immutable Commands/Queries/Events

All DTOs are readonly classes for thread safety and clarity.


Dependencies

  • PHP: 8.3+ (CI currently tests on 8.4)
  • CodeIgniter: 4.6.3+
  • MySQL: 8.0+
  • PHPStan: Level 8
  • PHPCS: PSR-12 + Slevomat
  • PHPUnit: 12.5+
  • PCOV: For fast code coverage

Architecture Benefits

✅ Clear Separation of Concerns - Domain, Infrastructure, Application layers ✅ Testability - Easy to test in isolation with mocks ✅ Maintainability - Changes localized to specific layers ✅ Scalability - Can separate read/write databases easily ✅ AI-Friendly - Clear patterns, comprehensive documentation ✅ Type-Safe - PHPStan level 8, full type coverage ✅ Auto-Discovery - Zero configuration for new domains ✅ Quality-Enforced - Specialists prevent violations


Quick Q&A for AI Agents

Q: Where is business logic? A: In Command/Query Handlers in app/Domain/{Domain}/Commands/ and app/Domain/{Domain}/Queries/

Q: Where is database access? A: In repository implementations under app/Infrastructure/Persistence/Repositories/, backed by CodeIgniter models in app/Models/{Domain}/ or app/Infrastructure/Persistence/Models/.

Q: How do I add a new field to an entity? A: Use /add-property {Domain} {property} {type} command or see .claude/documentation/PROPERTY_ADDITION_PROTOCOL.md

Q: How do I create a new domain? A: Use /add-domain {DomainName} command or see .claude/documentation/DOMAIN_CREATION_PROTOCOL.md

Q: Where are routes defined? A: In app/Config/Routes.php

Q: How do I run tests? A: composer test or vendor/bin/phpunit

Q: How do I add validation? A: Create Value Object in app/Domain/{Domain}/ValueObjects/ with validation in constructor

Q: Which specialist should review my code? A: Use multiple specialists in parallel - the orchestrator pattern determines which ones based on the task

Q: What if PHPStan or Slevomat fail? A: Code MUST NOT be committed. Use specialists to fix violations first.


Reference Domain

Use Cookie domain as a reference for all new domains. It demonstrates:

  • Complete CQRS implementation
  • DDD patterns (entities, value objects, aggregates)
  • Comprehensive test coverage (192 tests, 100% passing)
  • PHPStan Level 8 compliance
  • Slevomat compliance
  • AI-optimized docblocks

Location: app/Domain/Cookie/


This project follows best practices for CQRS, DDD, and Clean Architecture, optimized for AI agent collaboration through specialized subagents and orchestrator patterns.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.