From cd63ef339f92b7cc73d6fb364588ca6e63e67d32 Mon Sep 17 00:00:00 2001 From: Marijn Doeve Date: Wed, 1 Jul 2026 17:47:45 +0200 Subject: [PATCH] Add CLAUDE.md, replace Makefile with Justfile, remove .junie - Add CLAUDE.md with project overview, commands, architecture, and domain entity docs - Remove Makefile in favour of the existing Justfile - Remove .junie/AGENTS.md (knowledge transferred to CLAUDE.md) - Update .gitignore: drop .junie/ entries, add .claude/settings.local.json - Minor doc fixes in config/reference.php (typo, type correction) --- .gitignore | 6 +- .junie/AGENTS.md | 82 --------------- CLAUDE.md | 232 +++++++++++++++++++++++++++++++++++++++++++ Makefile | 16 --- config/reference.php | 8 +- 5 files changed, 238 insertions(+), 106 deletions(-) delete mode 100644 .junie/AGENTS.md create mode 100644 CLAUDE.md delete mode 100644 Makefile diff --git a/.gitignore b/.gitignore index e7b351e..c3d49a6 100644 --- a/.gitignore +++ b/.gitignore @@ -72,10 +72,8 @@ Icon /.idea/ /.vscode/ -# Junie -!/.junie/ -/.junie/memory/ -/.junie/plans/ +# Claude Code +/.claude/settings.local.json # Windows Thumbs.db diff --git a/.junie/AGENTS.md b/.junie/AGENTS.md deleted file mode 100644 index 94ba767..0000000 --- a/.junie/AGENTS.md +++ /dev/null @@ -1,82 +0,0 @@ -# Agent Guide: Tijd Voor De Test (Tvdt) - -This document provides essential context and instructions for AI agents working on the **Tijd Voor De Test** project. - -## Project Overview -A web application for managing "Wie is de Mol?" style tests, including seasons, quizzes, candidates, and eliminations. - -- **Namespace**: `Tvdt` -- **PHP Version**: 8.5+ -- **Framework**: Symfony 8.0 - -## Tech Stack -- **Server**: FrankenPHP (Caddy-based PHP server) -- **Database**: PostgreSQL -- **Frontend**: Symfony Asset Mapper (no Node.js/Webpack), Stimulus, Turbo -- **Styling**: Sass (via `symfonycasts/sass-bundle`) -- **Persistence**: Doctrine ORM 3.x - -## Core Domain Entities -- **Season**: Groups quizzes and candidates for a specific period. -- **SeasonSettings**: Configuration for a season. -- **Quiz**: A test within a season containing multiple questions. -- **Question**: Questions belonging to a quiz. -- **Answer**: Possible answers for a question. -- **Candidate**: A participant in the season. -- **QuizCandidate**: Represents a candidate's attempt at a specific quiz (tracking start/end time). -- **GivenAnswer**: The specific answer a candidate selected during a quiz. -- **Elimination**: Records of red/green screens and forced results. -- **User**: Administrative accounts for managing the system. - -## Development Workflow -The project uses `just` as the primary task runner. Always prefer `just` commands over manual docker calls. - -### Common Commands -- `just up`: Start the environment. -- `just down`: Stop the environment. -- `just shell`: Enter the PHP container. -- `just migrate`: Run database migrations. -- `just fixtures`: Load development fixtures. -- `just fix-cs`: Run `php-cs-fixer` and `twig-cs-fixer`. -- `just phpstan`: Run static analysis. -- `just rector`: Run Rector for automated refactorings. -- `just reload-tests`: Reset the test database and load test fixtures. - -## Coding Standards -- **PSR-12**: Follow standard PHP coding styles. -- **Strict Typing**: Use strict types in all PHP files. -- **Doctrine ORM 3**: Be aware of ORM 3 changes (e.g., lazy loading behavior, attribute-based mapping). -- **Symfony 8**: Use modern Symfony features (Attributes, Type-hinting). -- **Safe Functions**: Use `thecodingmachine/safe` for standard PHP functions that throw exceptions instead of returning false. - -## Testing -- **Framework**: PHPUnit -- **Bundle**: `dama/doctrine-test-bundle` is used to wrap tests in transactions. -- **Location**: `tests/` directory mirroring `src/`. -- **Execution**: Run via `bin/phpunit` inside the container or `just reload-tests` to prepare the environment. - -## Frontend Development -- JavaScript is managed via **Import Maps**. -- Stimulus controllers are located in `assets/controllers/`. -- CSS/Sass is in `assets/styles/`. -- Assets are compiled on-the-fly or mapped; do not look for a `node_modules` folder. - -## Key Components -### Controllers -- **Backoffice**: Located in `src/Controller/Backoffice`, handles season and quiz management. -- **Quiz**: `src/Controller/QuizController` handles the candidate-facing side of quizzes. -- **Elimination**: `src/Controller/EliminationController` handles elimination screens. - -### Services -- **QuizSpreadsheetService**: Handles importing quizzes from XLSX files. - -### Base Classes & Enums -- **AbstractController**: Base class for all controllers, containing common regexes and flash helpers. -- **FlashType Enum**: Used for consistent flash messaging (`FlashType::Success`, `FlashType::Danger`, etc.). - -## Key Files -- `composer.json`: Dependency management. -- `importmap.php`: JavaScript module mapping. -- `Justfile`: Automation shortcuts. -- `config/`: Application configuration. -- `templates/`: Twig templates. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..c4e0c8d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,232 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +**Tijd voor de test** is a PHP/Symfony 8.1 application for managing quizzes in the style of **Wie is de Mol?** (WIDM) — a Dutch TV show where contestants try to identify a saboteur ("de Mol") among them. At the end of each episode, participants take a quiz about the Mol's identity and actions; the candidate with the least correct answers is eliminated. This app replicates that quiz format with: +- Test creation with variable question counts +- Season management with active test controls +- Candidate answer tracking with automatic timing +- Elimination tracking with joker adjustments +- Backoffice management for quiz administration and statistics + +Tech Stack: +- **Framework**: Symfony 8.1 +- **PHP**: 8.5+ +- **Database**: PostgreSQL 16 +- **ORM**: Doctrine +- **Server**: FrankenPHP with Caddy +- **Container**: Docker Compose +- **Frontend**: Twig templates with SASS (via asset mapper) +- **Testing**: PHPUnit 13 with DAMA Doctrine test bundle + +## Build & Development Commands + +All commands assume Docker is running. The project uses a [Justfile](https://just.systems) as the primary interface. + +### Essential Commands + +```bash +just up # Start Docker services (PHP, PostgreSQL) +just stop # Stop services +just down # Stop and remove containers/orphans +just shell # Interactive shell inside the PHP container +just shell-run # Shell in a fresh one-off container +``` + +### Database + +```bash +just migrate # Run Doctrine migrations (starts services first) +just fixtures # Load dev fixtures (truncates first) +just reload-tests # Drop/recreate test DB, migrate, load test fixtures +``` + +### Testing + +```bash +just test # Run full PHPUnit suite +just test tests/Path/To/TestFile.php # Run a specific test file +just test --coverage-html var/coverage # Generate HTML coverage report +``` + +### Code Quality & Linting + +```bash +just fix-cs # Auto-fix PHP-CS-Fixer + Twig-CS-Fixer +just phpstan # PHPStan static analysis (level 9) +just phpstan --no-progress # Without progress output +just rector # Apply Rector modernizations +just rector --dry-run # Preview Rector changes +``` + +### Other + +```bash +just translations # Extract/update nl translation strings +just clean # Nuke containers (volumes) + all generated files (prompts for confirmation) +just trust-cert # Trust the local Caddy TLS certificate (macOS) +just exec # Run any command inside the PHP container +``` + +All code quality checks run in CI/CD (.github/workflows/ci.yml) and should pass before merging. + +## Project Structure + +``` +src/ + Controller/ # HTTP request handlers (attribute-routed) + Backoffice/ # Admin panel controllers + Entity/ # Doctrine ORM entities + Repository/ # Database queries + Service/ # Business logic + Command/ # CLI commands + Form/ # Symfony form types + Dto/ # Data transfer objects + Enum/ # Enumerations (FlashType, etc.) + Exception/ # Custom exceptions + Factory/ # Object factories + Helpers/ # Utility functions + Security/ # Auth and voter classes + Voter/ # Authorization voters + DataFixtures/ # Test data loaders + +config/ + packages/ # Symfony bundle configurations + routes/ # Route definitions + services.yaml # Service container configuration + routes.yaml # Main route entry point + +templates/ + backoffice/ # Admin UI templates + quiz/ # Public quiz UI templates + base.html.twig # Main layout + +tests/ + Command/ # Command tests + Controller/ # Controller/integration tests + Repository/ # Repository tests + Security/ # Auth tests + Helpers/ # Utility tests + bootstrap.php # PHPUnit bootstrap with test container setup +``` + +## Core Domain Entities + +- **Season**: Groups quizzes and candidates for a specific period, with a linked `SeasonSettings`. +- **Quiz**: A test within a season containing multiple `Question`s, each with multiple `Answer`s. +- **Candidate**: A participant in the season. +- **QuizCandidate**: Represents a candidate's attempt at a specific quiz (tracks start/end time). +- **GivenAnswer**: The specific answer a candidate selected during a quiz. +- **Elimination**: Records red/green screens and forced results with joker adjustments. +- **User**: Administrative accounts for managing the system. + +## Architecture Notes + +### Routing +- Routes are **attribute-based** (PHP 8 attributes in controller methods) +- Configured in `config/routes/attributes.yaml` for automatic discovery +- Main entry point: `config/routes.yaml` + +### Service Container & Dependency Injection +- Services in `src/` are automatically registered via PSR-4 namespace `Tvdt\` +- Exclusions: Entity, DependencyInjection, Kernel classes +- Autowiring and autoconfiguration enabled by default +- Service definitions in `config/services.yaml` + +### Database & Migrations +- PostgreSQL-based with Doctrine ORM +- Migrations in `migrations/` at project root, namespace `DoctrineMigrations` (intentionally not autoloaded); generate with `bin/console make:migration` +- Test fixtures in `src/DataFixtures/` (loaded with `--group=test`) +- Test database configured separately via `.env.test` + +### Testing Infrastructure +- **PHPUnit 13** with DAMA Doctrine Test Bundle for transaction rollback +- Bootstrap: `tests/bootstrap.php` loads env vars and autoloader; `tests/symfony-container.php` boots the test kernel/container (used by Rector) +- Symfony test utilities (BrowserKit, CSS selectors) available +- Coverage excluded from: `src/DataFixtures/` +- Test environment: `APP_ENV=test` (set in phpunit.dist.xml) + +### Code Style & Standards +- **PHP-CS-Fixer**: Symfony ruleset + risky rules enabled + - Strict types declaration required + - Trailing commas in multiline structures + - No else-only blocks +- **Rector**: Aggressive modernization with all attribute sets + prepared sets (dead code, code quality, Doctrine, Symfony, PHPUnit) +- **PHPStan**: Level 8 with extensions for Doctrine and Symfony +- **Twig-CS-Fixer**: Template style enforcement +- **Safe functions**: Use `thecodingmachine/safe` wrappers for standard PHP functions that return `false` on failure — they throw exceptions instead + +### Environment Configuration +- `.env` - Local development defaults (uncommitted in .env.local) +- `.env.dev` - Development overrides +- `.env.test` - Test environment configuration +- Production uses `composer dump-env prod` for compiled configuration +- Key variables: + - `APP_ENV` - Environment (dev/test/prod) + - `DATABASE_URL` - PostgreSQL connection string + - `MAILER_SENDER` - From address for emails + +### Frontend Build +- Asset mapper (no Node.js/Webpack) for JS/CSS bundling; JS modules declared in `importmap.php` +- **Stimulus** controllers in `assets/controllers/`, **Turbo** for SPA-like navigation +- Sass sources in `assets/styles/`, compiled via `bin/console sass:build` +- Production: Assets precompiled during Docker build +- Development: Watch mode enabled in FrankenPHP container + +## CI/CD Pipeline + +GitHub Actions workflow (`.github/workflows/ci.yml`): + +1. **Linting**: Dockerfile (hadolint), Twig templates +2. **Code Quality**: + - PHP-CS-Fixer style check + - Twig-CS-Fixer style check + - PHPStan static analysis + - Rector dry-run +3. **Integration Tests**: + - Docker image build and start services + - Database creation and migration + - Fixture loading + - Full PHPUnit test suite with JUnit XML output + - Doctrine schema validation +4. **Build & Deploy** (on tags or main, disabled currently): + - Docker image push to GitHub Container Registry + - Sentry release creation + - Portainer webhook trigger for production deployment + +Runs on all pushes to main and pull requests. Concurrency cancels old runs on new commits. + +## Important Files & Conventions + +- **Kernel**: `src/Kernel.php` - Symfony kernel class +- **AbstractController**: Base class for all controllers — defines route parameter regexes (`SEASON_CODE_REGEX`, `CANDIDATE_HASH_REGEX`) and flash helpers +- **Flash Messages**: Use `FlashType` enum instead of string literals +- **QuizSpreadsheetService**: Handles importing quizzes from XLSX files +- **Rector container**: `tests/symfony-container.php` — boots a test kernel so Rector can resolve Symfony service types +- **.gitignore**: Excludes var/, vendor/, .env.local, .phpunit.cache +- **Dockerfile**: Multi-stage build with dev/prod separation, FrankenPHP-based +- **Docker Compose**: PHP service with Caddy, PostgreSQL database, persistent volumes + +## Security & Authorization + +- Doctrine extensions enabled (timestamps, slugs, etc.) +- Voter-based authorization in `src/Security/Voter/` +- User entity with security encoding configured +- CSRF protection enabled +- Email verification available via SymfonyCasts bundle + +## Composer Scripts + +Auto-executed scripts on install/update: +- `cache:clear` - Symfony cache clear +- `assets:install` - Copy public assets +- `importmap:install` - JS import map setup + +## Notes for Future Work + +- The backoffice elimination logic is in `Controller/Backoffice/PrepareEliminationController.php` +- Quiz timing logic starts on candidate start click and stops on final answer selection +- Background music feature noted but not yet implemented (requirements only) +- Statistics functionality is marked TBD in README diff --git a/Makefile b/Makefile deleted file mode 100644 index 618c977..0000000 --- a/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -.DEFAULT_GOAL := help - -.PHONY: up -up: ## Start application - @docker compose up -d - -stop: ## Stop application - @docker compose stop - -.PHONY: shell -shell: ## Start a shell inside the container - @docker compose exec php bash - -.PHONY: help -help: - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-10s\033[0m %s\n", $$1, $$2}' diff --git a/config/reference.php b/config/reference.php index e0ea2b9..0fe774d 100644 --- a/config/reference.php +++ b/config/reference.php @@ -127,7 +127,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param; * } * @psalm-type ServicesConfig = array{ * _defaults?: DefaultsType, - * _instanceof?: InstanceofType, + * _instanceof?: array, * ... * } * @psalm-type ExtensionType = array @@ -727,7 +727,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param; * servicename?: scalar|Param|null, // Overrules dbname parameter if given and used as SERVICE_NAME or SID connection parameter for Oracle depending on the service parameter. * sessionMode?: scalar|Param|null, // The session mode to use for the oci8 driver * server?: scalar|Param|null, // The name of a running database server to connect to for SQL Anywhere. - * default_dbname?: scalar|Param|null, // Override the default database (postgres) to connect to for PostgreSQL connexion. + * default_dbname?: scalar|Param|null, // Override the default database (postgres) to connect to for PostgreSQL connection. * sslmode?: scalar|Param|null, // Determines whether or with what priority a SSL TCP/IP connection will be negotiated with the server for PostgreSQL. * sslrootcert?: scalar|Param|null, // The name of a file containing SSL certificate authority (CA) certificate(s). If the file exists, the server's certificate will be verified to be signed by one of these authorities. * sslcert?: scalar|Param|null, // The path to the SSL client certificate file for PostgreSQL. @@ -773,7 +773,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param; * servicename?: scalar|Param|null, // Overrules dbname parameter if given and used as SERVICE_NAME or SID connection parameter for Oracle depending on the service parameter. * sessionMode?: scalar|Param|null, // The session mode to use for the oci8 driver * server?: scalar|Param|null, // The name of a running database server to connect to for SQL Anywhere. - * default_dbname?: scalar|Param|null, // Override the default database (postgres) to connect to for PostgreSQL connexion. + * default_dbname?: scalar|Param|null, // Override the default database (postgres) to connect to for PostgreSQL connection. * sslmode?: scalar|Param|null, // Determines whether or with what priority a SSL TCP/IP connection will be negotiated with the server for PostgreSQL. * sslrootcert?: scalar|Param|null, // The name of a file containing SSL certificate authority (CA) certificate(s). If the file exists, the server's certificate will be verified to be signed by one of these authorities. * sslcert?: scalar|Param|null, // The path to the SSL client certificate file for PostgreSQL. @@ -852,7 +852,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param; * lock_path?: scalar|Param|null, // Default: "%kernel.cache_dir%/doctrine/orm/slc/filelock" * lock_lifetime?: scalar|Param|null, // Default: 60 * type?: scalar|Param|null, // Default: "default" - * lifetime?: scalar|Param|null, // Default: 0 + * lifetime?: scalar|Param|null, // Default: null * service?: scalar|Param|null, * name?: scalar|Param|null, * }>,