CLAUDE.md: replace placeholder sections with real project context
The Build & Test, Architecture Overview, and Conventions & Patterns sections
were still the init template's "_Add your ... here_" stubs. Filled them in
from the code:
- No build/test tooling exists; documents the php -l quality gate and the
dev-router.php requirement for running the server locally.
- Describes the two-deployable split (WP plugin vs server/) and the
.gitattributes export-ignore rule that keeps them apart in release zips.
- Records the PHP 7.4 (plugin) / 8.1+ (server) version split that already
drifted once in 3c64dd8.
- Notes Gitea (not GitHub) as the remote, the three-file release bump, and
the open security beads to check before touching auth or the installer.
The managed beads integration block is unchanged.
This commit is contained in:
133
CLAUDE.md
133
CLAUDE.md
@@ -52,18 +52,139 @@ bd close <id> # Complete work
|
||||
|
||||
## Build & Test
|
||||
|
||||
_Add your build and test commands here_
|
||||
There is **no build step, no package manager, and no test suite**. Plain PHP on both
|
||||
sides; the only third-party code is a vendored copy of Plugin Update Checker under
|
||||
`vendor/plugin-update-checker/` (do not hand-edit it — see README for the refresh
|
||||
procedure).
|
||||
|
||||
Quality gate for a change is a syntax lint of the files you touched, **against the
|
||||
right PHP version for that half of the repo** (see Conventions):
|
||||
|
||||
```bash
|
||||
# Example:
|
||||
# npm install
|
||||
# npm test
|
||||
# Plugin — must parse on PHP 7.4
|
||||
find . -path ./vendor -prune -o -path ./server -prune -o -name '*.php' -print \
|
||||
| xargs -n1 php7.4 -l # fall back to `php -l` if 7.4 isn't installed
|
||||
|
||||
# Server — PHP 8.1+
|
||||
find server -name '*.php' -print0 | xargs -0 -n1 php -l
|
||||
```
|
||||
|
||||
Run the history server locally:
|
||||
|
||||
```bash
|
||||
cp server/config.php.example server/config.php # set api_key + db_dsn (sqlite is easiest locally)
|
||||
php -S 127.0.0.1:8765 server/dev-router.php
|
||||
curl 127.0.0.1:8765/ # → {"ok":true,...}
|
||||
```
|
||||
|
||||
Use `dev-router.php`, **not** `-t server/public`. Healthcheck ids are dotted
|
||||
`uniqid()` values and PHP's built-in server treats dotted path segments as static
|
||||
file requests, so the plain docroot form 404s on most real URLs.
|
||||
|
||||
The plugin has to be exercised inside a WordPress install — symlink the repo into
|
||||
`wp-content/plugins/att-site-healthcheck` and use *Tools → Site Healthcheck*. It
|
||||
refuses to run until `ATT_HC_API_URL` / `ATT_HC_API_KEY` are configured (wp-config
|
||||
constants win over the settings-screen options).
|
||||
|
||||
Server deploy is an rsync of `server/` — see `server/DEPLOY.md`. Migrations run
|
||||
automatically on the first request after deploy.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
_Add a brief overview of your project architecture_
|
||||
**Two independent deployables live in this one repo**, and they must not leak into
|
||||
each other:
|
||||
|
||||
1. **The WordPress plugin** — repo root, `includes/`, `vendor/`. Ships to
|
||||
`wp-content/plugins/`. Installed per engagement and uninstalled at the end.
|
||||
2. **The central history server** (`server/`) — a standalone PHP app deployed to a
|
||||
VPS. `.gitattributes` marks `server/`, `.beads/`, `.claude/`, `AGENTS.md`,
|
||||
`CLAUDE.md`, and `steps.md` as `export-ignore`, so Gitea's `archive/main.zip`
|
||||
(which *is* the plugin release artifact) excludes them. **Any new
|
||||
non-plugin file at the repo root needs an `export-ignore` line.**
|
||||
|
||||
### Plugin
|
||||
|
||||
- `att-site-healthcheck.php` — header, constants, requires, step discovery on
|
||||
`plugins_loaded`, PUC update checker wiring.
|
||||
- `includes/steps/<order>-<slug>.php` — one file per healthcheck step, each
|
||||
`return`ing an `ATT_HC_Step` subclass instance. Adding a step is dropping a file;
|
||||
reordering is renaming the numeric prefix (loaded in `natsort` order); removing is
|
||||
deleting the file. `ATT_HC_Steps` is the singleton registry and applies the
|
||||
`att_hc_steps` filter after discovery.
|
||||
- `ATT_HC_Session` — **the server is the source of truth.** The `att_hc_session` WP
|
||||
option is only a cache of the active session. Every write is write-through: API
|
||||
call first, local cache updated only on success, so the cache can never drift.
|
||||
Cross-engagement history (prior notes, previous-session diff, step counts) is
|
||||
fetched live.
|
||||
- `ATT_HC_Api` — `wp_remote_request` client; every method throws
|
||||
`ATT_HC_Api_Exception`. Refuses non-`https://` endpoints unless loopback.
|
||||
- `includes/admin-page.php` — the whole UI (renderers + `admin_post_*` handlers).
|
||||
- `includes/report.php` — Markdown report builder. Reports are never stored
|
||||
server-side; the technician downloads them.
|
||||
- `includes/recovery-bootstrap.php` / `recovery-installer.php` — step 0 detects and
|
||||
can install the companion `wp-site-recovery` plugin (detected by plugin Name +
|
||||
Author, not folder slug).
|
||||
|
||||
### Server (`server/`)
|
||||
|
||||
Front controller → `src/bootstrap.php` (hand-rolled PSR-4 autoloader for
|
||||
`AttHc\Server\`, error/exception handlers, `config.php` load) → `src/routes.php`
|
||||
→ `Router` → `Controllers/`. `Store` holds all SQL; `Db` is a PDO singleton that
|
||||
runs `Migrations::ensureRan()` on first connection.
|
||||
|
||||
- API routes take `Authorization: Bearer <api_key>` (`Auth::require`, `hash_equals`).
|
||||
`GET /` is deliberately public — the plugin pings it to check reachability.
|
||||
- `/dashboard*` is `requiresAuth: false` at the router level because it does its own
|
||||
auth: `?key=<api_key>` once, then a PHP session cookie, plus a CSRF token on POSTs.
|
||||
- Storage is PDO against **either MySQL 8.0+ or SQLite** — `config.php.example`
|
||||
defaults to MySQL because `pdo_sqlite` is missing on some Ubuntu + Sury PHP
|
||||
combinations. Keep `migrations/*.sql` portable across both (`VARCHAR(N)`, `BIGINT`,
|
||||
no `AUTO_INCREMENT`); driver-specific DDL needs dispatch in `Migrations.php`.
|
||||
|
||||
## Conventions & Patterns
|
||||
|
||||
_Add your project-specific conventions here_
|
||||
### PHP version split — the easiest mistake to make here
|
||||
|
||||
- **Plugin code (root + `includes/`) must parse on PHP 7.4.** No `str_starts_with`
|
||||
/`str_contains`/`str_ends_with`, no named arguments, no `match`, no nullsafe `?->`,
|
||||
no constructor promotion, no enums. This has been broken by drift before
|
||||
(commit `3c64dd8`) — lint against 7.4, don't just eyeball it.
|
||||
- **Server code (`server/`) targets PHP 8.1+** and freely uses `declare(strict_types=1)`,
|
||||
typed properties, `never`, named arguments, and promotion.
|
||||
|
||||
### Plugin
|
||||
|
||||
- Guard every file with `if (!defined('ABSPATH')) exit;`.
|
||||
- Admin actions go through `admin_post_*` handlers with `current_user_can('manage_options')`
|
||||
+ `wp_nonce_field()` / `check_admin_referer()`, and escape on output
|
||||
(`esc_html`/`esc_attr`).
|
||||
- A step's `id()` is a stable contract — it is the key stored in session and server
|
||||
data. Rename files freely, never rename an id once shipped.
|
||||
- Optional step behaviour hangs off `ATT_HC_Step`: `autocheck()` for findings,
|
||||
`render_extra()` + `handle_action()` for step-specific UI/POSTs.
|
||||
- API failures surface as admin notices. History/diff lookups degrade silently
|
||||
(nice-to-have); writes must not.
|
||||
|
||||
### Server
|
||||
|
||||
- Every file: `declare(strict_types=1);` and namespace `AttHc\Server\...`.
|
||||
- All SQL lives in `Store`, always via prepared statements.
|
||||
- Request input goes through `Validate::*`; responses through `Http::json` /
|
||||
`Http::error` (`{"error":…,"code":…}`).
|
||||
- **No composer.** If you think you need a dependency, vendor it or don't.
|
||||
- `server/config.php` is deployment-specific and gitignored — never commit it, and
|
||||
don't echo secrets into responses or logs.
|
||||
|
||||
### Repo / release
|
||||
|
||||
- Remote is **Gitea** (`git.h12e.com/steve/wp-healthcheck`), not GitHub — `gh` and
|
||||
PR workflows don't apply. Work lands on `main`.
|
||||
- Release = bump `Version:` in the plugin header **and** `ATT_HC_VERSION` **and**
|
||||
`version` + `last_updated` in `updates.json`, then push `main`. PUC polls
|
||||
`updates.json` and installs `archive/main.zip`, so whatever is on `main` is what
|
||||
ships. Built zips are gitignored.
|
||||
- `steps.md` is the human technician guide the step files are derived from; keep the
|
||||
two in step when step content changes.
|
||||
- Known open security issues are tracked in beads — `hc-gp3` (API key exposure),
|
||||
`hc-adg` (unpinned recovery-plugin install), `hc-ufl` (no rate limiting / unbounded
|
||||
error log). Check them before touching auth, the settings screen, or the installer.
|
||||
|
||||
Reference in New Issue
Block a user