Files
unsupervised-scheduler/CLAUDE.md
James Griffin 2fb2ca392d
All checks were successful
CI / Coding Standards (push) Successful in 43s
CI / PHPStan (push) Successful in 52s
CI / Tests (PHP 8.1) (push) Successful in 47s
CI / Tests (PHP 8.2) (push) Successful in 49s
CI / Tests (PHP 8.3) (push) Successful in 37s
CI / No Debug Code (push) Successful in 2s
Restructure src/ and tests/ from package-by-type to package-by-domain
All classes are now organised by domain (Availability, Booking, Auth).
Each domain package contains its value object, repository, admin controller,
REST endpoint, and any shortcode pages under a matching sub-namespace.
Cross-cutting wiring (Plugin, AdminMenu, RestRegistrar, ShortcodeRegistrar,
Schema) lives at src/ root. Tests mirror the domain structure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 16:37:30 -03:00

6.1 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Commands

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/Availability/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\)
  Availability/       — Availability slots: value object, repository, controller, REST endpoint
  Booking/            — Lessons/bookings: value object, repository, controller, REST endpoint, shortcode page
  Auth/               — Roles, capabilities, login page
  Plugin.php          — Wires all components together on plugins_loaded
  Installer.php       — Creates DB tables and roles on activation
  Schema.php          — CREATE TABLE SQL for dbDelta
  AdminMenu.php       — Registers wp-admin menu pages
  RestRegistrar.php   — Registers all REST routes under us-scheduler/v1
  ShortcodeRegistrar.php — Registers [us_booking] and [us_student_login] shortcodes
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\)
  Availability/       — Tests for src/Availability/
  Booking/            — Tests for src/Booking/
  Auth/               — Tests for src/Auth/
docs/features/        — One markdown file per feature describing data model, API, and test locations

Code is organised package-by-domain (Availability, Booking, Auth). Each domain package contains everything related to that domain: value objects, repositories, controllers, REST endpoints, and shortcode pages. Cross-cutting wiring classes (Plugin, AdminMenu, RestRegistrar, ShortcodeRegistrar, Schema) live directly under src/.

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 within their domain package. 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
Schema CREATE TABLE SQL strings for dbDelta
AdminMenu Registers wp-admin menu pages
RestRegistrar Registers all REST routes under us-scheduler/v1
ShortcodeRegistrar Registers [us_booking] and [us_student_login] shortcodes
Auth\RoleManager Registers us_instructor and us_student roles with custom caps
Auth\LoginPage Renders front-end student login form
Availability\AvailabilitySlot Immutable value object for a slot row
Availability\AvailabilityRepository CRUD for availability slots
Availability\AvailabilityController Instructor availability management page
Availability\AvailabilityEndpoint REST handlers for availability CRUD
Booking\Lesson Immutable value object for a lesson row
Booking\BookingRepository CRUD for lesson bookings
Booking\BookingEndpoint REST handlers for booking and status updates
Booking\BookingPage Renders student booking UI shell (JS takes over)
Booking\LessonController Admin and instructor lesson list pages

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 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/<feature-name>.md (data model, API, classes, test paths).
  2. Create a domain package under src/<Domain>/ containing all classes for that feature.
  3. Add template(s) under templates/ if needed.
  4. Write unit tests under tests/Unit/<Domain>/ mirroring the src/<Domain>/ 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/