WordPress plugin that publishes posts to AT Protocol in both app.bsky.feed.post (Bluesky) and site.standard.document / site.standard.publication (standard.site) formats via native OAuth.
Tech stack: PHP 8.2+, WordPress 6.2+, wp-env for local dev, PHPUnit for tests, Jetpack Changelogger for changelog management.
Do NOT:
- Edit WordPress core files
- Hardcode new version numbers in changelog messages
- Create a PR without running
composer lintandnpm run env-testfirst - Create a PR without a changelog entry in
.github/changelog/or a "Skip Changelog" label
atmosphere.php # Main plugin file.
includes/
├── class-*.php # Core classes (Atmosphere, API, Publisher, Backfill, Handle, Post_Types, Reaction_Sync).
├── functions.php # Helper functions.
├── content-parser/ # Pluggable content formats for site.standard.document (interface only by default).
├── oauth/ # OAuth flow (Client, DPoP, Encryption, Resolver, Nonce).
├── transformer/ # AT Protocol record transformers (Post, Document, Publication, Comment, Facet, TID).
└── wp-admin/ # Admin UI (settings page, sidebar panel).
integrations/ # Plugin-specific content-parser integrations (stubs).
templates/ # PHP template files.
assets/ # CSS and JS.
tests/
└── phpunit/ # PHPUnit tests.
# Environment
npm run env-start # Start WordPress at http://localhost:8884.
npm run env-stop # Stop WordPress environment.
# PHP tests (require wp-env running)
npm run env-test # All PHP tests.
npm run env-test -- --filter=pattern # Tests matching pattern.
# Local tests (require MySQL)
composer test # Full test suite.
vendor/bin/phpunit --filter=pattern # Matching tests.
# Code quality
composer lint # Check PHP coding standards (PHPCS).
composer lint:fix # Auto-fix PHP issues.
# Changelog
composer changelog:add # Add a changelog entry.
composer changelog:write # Write entries to CHANGELOG.md.
# Release
npm run release # Interactive release: bumps version, regenerates CHANGELOG/readme.txt, pushes a release/X.Y.Z PR.- Custom autoloader, NOT Composer. Runtime classes are loaded by
includes/class-autoloader.php(PSR-4-ish with WordPressclass-{name}.phpfilenames);composer.json'sautoloadis empty.composer install/updateonly refreshes dev dependencies (PHPUnit, PHPCS, Changelogger) and cannot break the running plugin. New classes work as soon as the file is in place — nocomposer dump-autoloadstep. - Changelog entries MUST be end-user friendly and end with punctuation. Users see these in the WordPress update screen. Describe what changed from their perspective — no jargon, class names, or method names.
Files: class-{name}.php
Namespaces: Atmosphere, Atmosphere\{OAuth,Transformer,WP_Admin}
Text domain: always 'atmosphere'.
MUST backslash-prefix all WordPress/PHP global functions in namespaced code: \get_option(), \add_action(), \apply_filters(), \strlen(), \time(), etc.
MUST use use imports for cross-namespace references — no inline \Namespace\Class.
Uses the custom Atmosphere\Autoloader in includes/class-autoloader.php, which respects WordPress filename conventions (class-foo.php, lowercase, hyphenated). composer.json declares an empty autoload block — Composer is only used for dev tooling (PHPUnit, PHPCS, Changelogger). Helper functions in includes/functions.php are loaded via a direct require_once from atmosphere.php.
Tests use namespace Atmosphere\Tests and extend WP_UnitTestCase. Use @group annotations to categorize tests (@group atmosphere, @group transformer).
Test files live in tests/phpunit/tests/ mirroring includes/ structure. Files are prefixed class-test-.
Transformers — Convert WordPress content into AT Protocol records. Extend Atmosphere\Transformer\Base. Each transformer defines transform(), get_collection(), and get_rkey(). See includes/transformer/.
OAuth — Full PKCE + DPoP + PAR native OAuth flow. Handle → DID → PDS → Auth Server resolution chain. See includes/oauth/.
API Client — DPoP-authenticated PDS requests with automatic nonce retry. See includes/class-api.php.
Publisher — Atomic batch applyWrites for both bsky post + standard.site document. See includes/class-publisher.php.
Well-known endpoints — Rewrite rules + template_redirect handlers in Atmosphere class serve /.well-known/atproto-did (domain handle verification) and /.well-known/site.standard.publication (publication AT-URI). All share the atmosphere_wellknown query var.
README.md — public-facing repo entry point (lean: intro + docs links).
readme.txt — WordPress.org plugin readme (end-user friendly description, FAQ, changelog).
docs/developer-docs.md — developer entry doc; index over the rest of docs/.
docs/development-environment.md — wp-env setup, prerequisites, troubleshooting, coverage.
docs/php-coding-standards.md — naming, escaping, error handling, performance, cron rules.
docs/php-class-structure.md — directory layout, namespaces, architectural patterns.
docs/code-linting.md — PHPCS rules and common fixes.
docs/pull-request.md — branching, pre-PR checklist, commit format, special situations.
docs/release-process.md — `npm run release`, patch releases, GitHub Release UI.
docs/translations.md — text domain, GlotPress, translator-friendly strings.
docs/content-formats.md — survey of AT Protocol `content` types for site.standard.document.
docs/org.wordpress.html.md — Lexicon for the org.wordpress.html content type.
integrations/README.md — registering custom Content_Parser implementations from third-party plugins.
.github/PULL_REQUEST_TEMPLATE.md — PR template (changelog block + testing instructions).
The skills under .agents/skills/ are quick-references that link into these docs. Update the docs when conventions change; the skills inherit the change.
npm run releaseThe release script (bin/release.js) does all of the version bookkeeping in one step:
- Runs
composer changelog:writeto roll up.github/changelog/entries intoCHANGELOG.md(the next semver version is inferred from the entries' significance). - Creates a
release/X.Y.Zbranch. - Updates the version in
atmosphere.php(header +ATMOSPHERE_VERSION),readme.txt(Stable tag), andpackage.json. - Mirrors the new changelog section into
readme.txt(same major-version history, with a link to the full GitHubCHANGELOG.md). - Replaces
@since unreleased/@deprecated unreleasedand the equivalent_deprecated_*/_doing_it_wrongliterals in PHP files with the new version. - Optionally prompts for an Upgrade Notice.
- Pushes the branch and opens a PR titled
Release X.Y.Zagainsttrunk.
The .gitattributes file controls what's excluded from git archive exports and GitHub release tarballs.
Skills are complex procedures loaded on demand. Canonical files live in .agents/skills/; .claude/skills/*/SKILL.md are 1-line stubs that point at them so both Claude Code and other agents pick up the same instructions.
| Skill | Use when… |
|---|---|
| code-style | Writing PHP code, creating classes, implementing hooks. |
| dev | Setting up environment, running tests, linting. |
| test | Writing tests, debugging failures, test patterns. |
| pr | Creating or reviewing pull requests. MUST invoke before any PR creation. |
| release | Creating releases, bumping versions, managing changelogs. |
| Agent | Trigger |
|---|---|
| code-review | Auto-invoked before PR creation to review changes. |
| spec-check | Audit against AT Protocol and standard.site Lexicon specs. |
| security-audit | Audit for OAuth bypass, SSRF, token leakage, XSS, and DPoP issues. |