# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Commands ```bash composer install # Install all dependencies composer test # Run the full test suite (required after every change) composer lint # PHPStan static analysis composer cs # PHPCS coding standards check composer cs:fix # Auto-fix coding standards # Run a single test file ./vendor/bin/phpunit tests/Unit/Data/AvailabilityRepositoryTest.php # Run a single test by name ./vendor/bin/phpunit --filter testInsertCallsWpdbInsertAndReturnsId ``` **Run `composer test` after every code change before considering a task complete.** ## Architecture ### Plugin Bootstrap `unsupervised-schedular.php` defines constants (`USC_VERSION`, `USC_PLUGIN_DIR`, `USC_PLUGIN_URL`), registers activation/deactivation hooks, then calls `Plugin::boot()` on `plugins_loaded`. No logic lives in the root file. ### Directory Structure ``` src/ — All plugin PHP (PSR-4 namespace: Unsupervised\Schedular\) templates/ — PHP view files included by controllers/shortcodes assets/ — CSS and JS (vanilla JS, no build step) tests/Unit/ — PHPUnit unit tests (PSR-4: Unsupervised\Schedular\Tests\) docs/features/— One markdown file per feature describing data model, API, and test locations ``` ### Data Storage Two custom database tables (created via `dbDelta` on activation): - `{prefix}us_availability` — instructor availability windows - `{prefix}us_lessons` — booked lessons All database access goes through repository classes in `src/Data/`. No direct `$wpdb` calls outside repositories. ### Key Classes | Class | Responsibility | |---|---| | `Plugin` | Wires all components together on `plugins_loaded` | | `Installer` | Creates DB tables and roles on activation | | `Roles\RoleManager` | Registers `us_instructor` and `us_student` roles with custom caps | | `Data\AvailabilityRepository` | CRUD for availability slots | | `Data\BookingRepository` | CRUD for lesson bookings | | `Model\AvailabilitySlot` | Immutable value object for a slot row | | `Model\Lesson` | Immutable value object for a lesson row | | `Admin\AdminMenu` | Registers wp-admin menu pages | | `Admin\AvailabilityController` | Instructor availability management page | | `Admin\LessonController` | Admin and instructor lesson list pages | | `Api\RestRegistrar` | Registers all REST routes under `us-scheduler/v1` | | `Api\AvailabilityEndpoint` | REST handlers for availability CRUD | | `Api\BookingEndpoint` | REST handlers for booking and status updates | | `Frontend\ShortcodeRegistrar` | Registers `[us_booking]` and `[us_student_login]` shortcodes | | `Frontend\BookingPage` | Renders student booking UI shell (JS takes over) | | `Frontend\LoginPage` | Renders front-end student login form | ### REST API Namespace All endpoints live under `/wp-json/us-scheduler/v1/`. Permissions are enforced via `permission_callback` using capability checks (`manage_availability`, `book_lesson`), never role name checks. ### Testing Approach Tests use [Brain\Monkey](https://brain-wp.github.io/BrainMonkey/) to stub WordPress functions without a full WP installation, and Mockery to mock `$wpdb` and other dependencies. All test classes extend `tests/Unit/TestCase.php`, which handles `Monkey\setUp()` / `Monkey\tearDown()` and stubs all WP translation/escape functions automatically. **Brain\Monkey API notes:** - `Functions\when('fn')->alias(fn() => ...)` — stub with a closure (NOT `returnUsing()`) - `Functions\when('fn')->justReturn($val)` — stub returning a fixed value - `Functions\expect('fn')->once()->with(...)` — assert call count and arguments - Use `Functions\when()` (not `Functions\expect()`) when you need argument-routing (e.g. `get_role` returning different values per argument) to avoid chaining ambiguity - Mockery matchers (e.g. `\Mockery::type()`) inside plain PHP arrays do not work with `with()` — use `\Mockery::on(fn($arr) => ...)` or `\Mockery::any()` instead - When mocking `$wpdb`, set `$mock->prefix = 'wp_'` explicitly — it is a public property, not a method ### Adding a Feature 1. Write the feature doc in `docs/features/.md` (data model, API, classes, test paths). 2. Implement the classes under `src/`. 3. Add template(s) under `templates/` if needed. 4. Write unit tests under `tests/Unit/` mirroring the `src/` directory structure. 5. Run `composer test` — all tests must pass before the feature is complete. ### CI Gitea Actions (`.gitea/workflows/ci.yml`) runs on every push and pull request: - **lint** — PHPCS WordPress coding standards - **static-analysis** — PHPStan level 6 - **test** — PHPUnit on PHP 8.1, 8.2, 8.3 - **no-debug** — rejects commits with `var_dump`, `error_log`, etc. in `src/`