Add AGENTS.md, point CLAUDE.md files at it
CI / Coding Standards (pull_request) Successful in 18s
CI / Static Analysis (pull_request) Successful in 57s
CI / Tests (PHP 8.2) (pull_request) Successful in 42s
CI / Tests (PHP 8.3) (pull_request) Successful in 21s
CI / Tests (PHP 8.5) (pull_request) Successful in 49s
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.1) (pull_request) Successful in 28s
CI / Build Plugin Zip (pull_request) Skipped

This commit is contained in:
2026-09-16 12:07:37 -03:00
parent 91eb30b222
commit e07ab4be42
3 changed files with 49 additions and 41 deletions
+47
View File
@@ -0,0 +1,47 @@
# AGENTS.md
## Commands
```bash
composer install
composer test # PHPUnit — run after every code change
composer lint # PHPStan (level 10, `src/` only)
composer cs # PHPCS (WordPress standard + exclusions in phpcs.xml.dist)
composer cs:fix # auto-fix coding standards
composer build # -> dist/unsupervised-schedular-<version>.zip
./vendor/bin/phpunit tests/Unit/Offering/OfferingRepositoryTest.php
./vendor/bin/phpunit --filter testInsertReturnsId
```
CI (`.gitea/workflows/ci.yml`): `phpcs`, `phpstan`, `test` (PHP 8.1/8.2/8.3/8.5), `no-debug`. Write only PHP 8.1-compatible syntax. No `var_dump|var_export|print_r|error_log|dd|dump(` in `src/` — CI greps and fails.
## Architecture
- WordPress plugin, no front-end build (vanilla JS/CSS in `assets/`). PSR-4 `Unsupervised\Schedular\` -> `src/`.
- **Package-by-domain:** `src/<Domain>/` (Auth, Availability, Booking, GroupClass, Guardian, Offering, Payment, Policy, Registration) owns its repos, services, endpoints, pages. Cross-cutting wiring lives directly in `src/`: `Plugin`, `Installer`, `Schema`, `AdminMenu`, `RestRegistrar`, `ShortcodeRegistrar`, `BlockRegistrar`, `Val`.
- Entry: `unsupervised-schedular.php` -> `Plugin::boot()` (wires all dependencies). **Slug is `schedular`, not `scheduler`** — filename, text domain (`unsupervised-schedular`), option `us_schedular_version`, table prefix `us_`. Never "fix" the spelling.
- REST: `/wp-json/us-scheduler/v1/`, `permission_callback` uses capability checks, never role names.
- DB: custom `us_*` tables via `dbDelta`; `Schema::tables()` is the source of truth. **All `$wpdb` access inside repository classes only.**
- `src/Val.php` coerces untyped WP input (`Val::int()`, `Val::string()`, `...OrNull`, etc.). For PHPCS, `Val::int/float/bool/...` count as unslashing passthrough only — still wrap with a real sanitizer: `absint( Val::int( $_GET['id'] ?? 0 ) )`.
## Schema changes (gotcha)
- `Plugin::boot()` only re-runs `Installer`/migrations when stored `us_schedular_version !== USC_VERSION`. **Bump both the `Version:` header and `USC_VERSION` in `unsupervised-schedular.php` or the change never reaches existing sites.**
- `dbDelta` does not reliably relax column NULL-ability. Follow the existing pattern in `Plugin::boot()`: repository repair method + own `us_*` option flag (e.g. `us_questions_offering_nullable`), not the version gate.
## Tests
- Brain Monkey + Mockery, no live WP. All test classes extend `tests/Unit/TestCase.php` (handles `Monkey\setUp/tearDown`, stubs translations/escaping/`checked`/`selected`).
- Mirror layout: `tests/Unit/<Domain>/` mirrors `src/<Domain>/`.
- `Functions\when('fn')->alias(fn() => ...)` (never `returnUsing()`); `->justReturn($v)` for constants.
- Use `when()` not `expect()` for argument-dependent routing.
- No `\Mockery::type()` inside plain arrays passed to `with()` — use `\Mockery::on()` or `\Mockery::any()`.
- `$wpdb` mock needs `$mock->prefix = 'wp_'` as a property.
## Adding a feature
1. Spec first: `docs/features/<feature-name>.md` (data model, API, classes, test paths).
2. Code in `src/<Domain>/`; templates in `templates/` if needed.
3. Tests in `tests/Unit/<Domain>/`.
4. `composer test` must pass (also `composer lint` + `composer cs` before finishing).
+1 -29
View File
@@ -1,31 +1,3 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Commands
**Run `composer test` after every code change before considering a task complete.**
## Architecture
### Code organisation
**Code is organised package-by-domain.** Each domain package under `src/<Domain>/` 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
Custom database tables are created via `dbDelta` on activation; `Schema.php` holds the SQL.
All database access goes through repository classes within their domain package. No direct `$wpdb` calls outside repositories.
### 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 stub WordPress with Brain\Monkey rather than booting a real WP install. The setup and the Brain\Monkey/Mockery API gotchas are in `tests/CLAUDE.md`.
### Adding a Feature
0. **If the feature touches `Schema.php`, bump both the `Version:` header and `USC_VERSION` in `unsupervised-schedular.php`.** `Plugin::boot()` only re-runs `Installer`/`dbDelta` when the stored `us_schedular_version` differs, so a schema change without a version bump never reaches existing sites and inserts into new columns fail silently.
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.
See `AGENTS.md` — it is the single source of truth for working in this repo.
+1 -12
View File
@@ -1,14 +1,3 @@
# Writing tests
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
See `../../AGENTS.md` (Tests section) — it is the single source of truth for working in this repo.