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.
191 lines
8.7 KiB
Markdown
191 lines
8.7 KiB
Markdown
# Project Instructions for AI Agents
|
|
|
|
This file provides instructions and context for AI coding agents working on this project.
|
|
|
|
<!-- BEGIN BEADS INTEGRATION v:1 profile:minimal hash:ca08a54f -->
|
|
## Beads Issue Tracker
|
|
|
|
This project uses **bd (beads)** for issue tracking. Run `bd prime` to see full workflow context and commands.
|
|
|
|
### Quick Reference
|
|
|
|
```bash
|
|
bd ready # Find available work
|
|
bd show <id> # View issue details
|
|
bd update <id> --claim # Claim work
|
|
bd close <id> # Complete work
|
|
```
|
|
|
|
### Rules
|
|
|
|
- Use `bd` for ALL task tracking — do NOT use TodoWrite, TaskCreate, or markdown TODO lists
|
|
- Run `bd prime` for detailed command reference and session close protocol
|
|
- Use `bd remember` for persistent knowledge — do NOT use MEMORY.md files
|
|
|
|
## Session Completion
|
|
|
|
**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until `git push` succeeds.
|
|
|
|
**MANDATORY WORKFLOW:**
|
|
|
|
1. **File issues for remaining work** - Create issues for anything that needs follow-up
|
|
2. **Run quality gates** (if code changed) - Tests, linters, builds
|
|
3. **Update issue status** - Close finished work, update in-progress items
|
|
4. **PUSH TO REMOTE** - This is MANDATORY:
|
|
```bash
|
|
git pull --rebase
|
|
bd dolt push
|
|
git push
|
|
git status # MUST show "up to date with origin"
|
|
```
|
|
5. **Clean up** - Clear stashes, prune remote branches
|
|
6. **Verify** - All changes committed AND pushed
|
|
7. **Hand off** - Provide context for next session
|
|
|
|
**CRITICAL RULES:**
|
|
- Work is NOT complete until `git push` succeeds
|
|
- NEVER stop before pushing - that leaves work stranded locally
|
|
- NEVER say "ready to push when you are" - YOU must push
|
|
- If push fails, resolve and retry until it succeeds
|
|
<!-- END BEADS INTEGRATION -->
|
|
|
|
|
|
## Build & Test
|
|
|
|
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
|
|
# 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
|
|
|
|
**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
|
|
|
|
### 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.
|