From 63ee2486957dab7b624c04422ddff1cb8b648989 Mon Sep 17 00:00:00 2001 From: Kydoimos Date: Wed, 16 Sep 2026 11:58:19 -0300 Subject: [PATCH] Add AGENTS.md, point CLAUDE.md files at it Co-authored-by: opencode/muse-spark-1.3-contributor-free --- AGENTS.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 30 +----------------------------- tests/CLAUDE.md | 13 +------------ 3 files changed, 49 insertions(+), 41 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..3894deb --- /dev/null +++ b/AGENTS.md @@ -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-.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//` (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//` mirrors `src//`. + - `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/.md` (data model, API, classes, test paths). +2. Code in `src//`; templates in `templates/` if needed. +3. Tests in `tests/Unit//`. +4. `composer test` must pass (also `composer lint` + `composer cs` before finishing). diff --git a/CLAUDE.md b/CLAUDE.md index 6065365..84c7124 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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//` 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/.md` (data model, API, classes, test paths). -2. Create a domain package under `src//` containing all classes for that feature. -3. Add template(s) under `templates/` if needed. -4. Write unit tests under `tests/Unit//` mirroring the `src//` 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. diff --git a/tests/CLAUDE.md b/tests/CLAUDE.md index 9ec5d83..587301c 100644 --- a/tests/CLAUDE.md +++ b/tests/CLAUDE.md @@ -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.