-
Notifications
You must be signed in to change notification settings - Fork 9
Add AI agent onboarding and Copilot coding agent configuration #344
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
obenland
wants to merge
23
commits into
trunk
Choose a base branch
from
add/agents-md
base: trunk
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
10d130c
Add AGENTS.md and CLAUDE.md for AI coding agent onboarding
obenland bd3c996
Fix inaccuracies in AGENTS.md from audit
obenland 8e5a212
Clarify wp-env and lifecycle script descriptions in AGENTS.md
obenland 768f82a
Add .claude/settings.local.json to .gitignore
obenland c54a209
Add Copilot agent configuration for autonomous issue resolution
obenland 329b84d
Build: Only trigger copilot-setup-steps on manual dispatch
obenland fbb93d7
Build: Cache dependencies in copilot-setup-steps
obenland 2a30056
Fix inaccuracies flagged in Copilot code review
obenland 52afc72
Update .nvmrc to Node 24, use node-version-file in copilot setup
obenland a006129
Use canonical PHP lint command and cross-reference agent instruction …
obenland 77332ff
Update AGENTS.md
obenland 61e7eda
Build: Harden Docker image caching in copilot setup
obenland 2ae40d6
Remove PHPDoc instruction, pin Playwright to 1.58.2
obenland 4771fbb
Build: Remove Playwright install from copilot setup
obenland 7a6d6ae
Add dedicated wp-env config for Copilot agents
obenland 4462ef3
Make E2E verification mandatory for all Copilot PRs
obenland 9af270f
Add explicit WordPress core ref to copilot wp-env config
obenland c449064
Use GitHub repo refs in main .wp-env.json, remove separate copilot co…
obenland 0de0f0a
Remove invalid comment from .wp-env.json
obenland 720db4b
Fix bbPress GitHub repo ref in wp-env config
obenland f1fad6c
Keep WebAuthn provider as zip download in wp-env config
obenland 533012b
Use GitHub release zip for WebAuthn provider, drop explicit core ref
obenland dd72175
Use GitHub archive zip for WordPress core in wp-env config
obenland File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| # WordPress.org Two-Factor Plugin — Copilot Agent Instructions | ||
|
|
||
| > See also `AGENTS.md` in the repository root for additional context shared across all AI agents. | ||
|
|
||
| ## Project Overview | ||
|
|
||
| This is a **security-critical** WordPress plugin that customizes the [Two-Factor](https://github.com/WordPress/two-factor) plugin for WordPress.org. It enforces 2FA on privileged accounts (committers, deputies, theme authors, WordCamp organizers), strips capabilities from users who haven't enabled 2FA, and provides a React-based settings UI with REST API endpoints. | ||
|
|
||
| Key subsystems: provider management (WebAuthn/TOTP/Backup Codes), capability enforcement, session revalidation ("sudo mode"), encrypted TOTP secrets, and a block-based settings interface. | ||
|
|
||
| ## Working on Issues | ||
|
|
||
| ### Before Writing Code | ||
|
|
||
| 1. **Read the issue thoroughly.** Understand the problem, reproduction steps, and acceptance criteria. | ||
| 2. **Explore the relevant code.** Read the files involved — don't guess at implementations. | ||
| 3. **Understand _why_ the code is the way it is.** Check `git log` and `git blame` for the files you'll change. Read commit messages and linked PRs/issues to understand the decisions that led to the current design. Security-related decisions are especially important — do not undo them without understanding the rationale. | ||
| 4. **Check for related tests.** Look in `tests/` for existing test coverage of the area you're modifying. | ||
| 5. **Map the dependency chain.** This plugin depends on the Two-Factor core plugin, the WebAuthn provider plugin, bbPress, Gutenberg, and wporg-mu-plugins. Understand which dependencies are involved in your change. | ||
|
|
||
| ### Writing Code | ||
|
|
||
| Follow **WordPress coding standards** strictly: | ||
| - PHP: tabs for indentation, Yoda conditions (`'value' === $var`), snake_case functions, braces on same line. | ||
| - JS/React: tabs for indentation, follow wp-scripts/eslint conventions. | ||
| - When in doubt, match the existing file's style and adhere to the WordPress coding standards above. | ||
|
|
||
| **Architecture rules:** | ||
| - The main plugin file (`wporg-two-factor.php`) uses the `WordPressdotorg\Two_Factor` namespace. | ||
| - Settings UI lives in `settings/` — it's a `@wordpress/scripts` block package with its own `package.json`. | ||
| - REST API endpoints are in `settings/rest-api.php`. | ||
| - Session revalidation is in `revalidation/index.php`. | ||
| - Custom providers: `class-encrypted-totp-provider.php` (TOTP with encryption), `class-wporg-webauthn-provider.php` (WebAuthn with caching). | ||
|
|
||
| **Security considerations:** | ||
| - This is a security plugin. Every change must be defensively coded. | ||
| - Never weaken capability checks, 2FA enforcement, or session validation. | ||
| - TOTP secrets are encrypted at rest — maintain this guarantee. | ||
| - User input must be sanitized, output must be escaped. | ||
| - Do not introduce OWASP Top 10 vulnerabilities. | ||
|
|
||
| ### Writing Tests | ||
|
|
||
| Every PR **must** include tests for the changes. This is a security plugin — untested code is unacceptable. | ||
|
|
||
| **PHP unit tests:** | ||
| - Location: `tests/` directory, files prefixed with `test-`. | ||
| - Framework: PHPUnit 9.6 with WordPress test utilities (`WP_UnitTestCase`). | ||
| - Run: `npm test` (runs PHPUnit inside wp-env). | ||
| - Coverage target: 100% for meaningful, testable code. Use `@codeCoverageIgnore` pragmatically (as configured in `phpunit.xml.dist`) to exclude non-behavioral glue, unreachable or environment-specific paths, but never to hide untested business logic. | ||
| - **NEVER use `remove_all_filters()` or `remove_all_actions()` in tests** — it removes production callbacks. Always add/remove specific callbacks by reference. | ||
| - Test classes extend `WP_UnitTestCase`. Use `wpSetUpBeforeClass` for expensive setup, `tear_down` for cleanup. | ||
| - The test bootstrap (`tests/bootstrap.php`) mocks WordPress.org-specific functions. | ||
|
|
||
| **JavaScript tests:** | ||
| - Location: `settings/src/tests/`. | ||
| - Framework: Jest via `@wordpress/scripts`. | ||
| - Run: `npm run test:js`. | ||
| - Uses `@testing-library/react` for component tests. | ||
|
|
||
| **End-to-end tests with Playwright:** | ||
| - Use the Playwright MCP server to interact with the local WordPress site in the browser. | ||
| - wp-env provides two instances: | ||
| - **Dev instance:** `http://localhost:8888` — use this for Playwright browser testing. | ||
| - **Test instance:** `http://localhost:8889` — used by PHPUnit (no browser testing here). | ||
| - **Login credentials:** username `admin`, password `password` (wp-env defaults). | ||
| - **Login URL:** `http://localhost:8888/wp-login.php`. Log in before testing authenticated flows. | ||
| - The dev environment has a **Dummy 2FA provider** enabled (see `.wp-env/mu-plugins/mu-plugin.php`), which allows completing 2FA login without real authenticator hardware. Use this for Playwright flows that require passing the 2FA prompt. | ||
| - The `admin` user is configured as a super admin and "special user" in the mu-plugin, so 2FA enforcement applies to them. | ||
| - This is a **multisite** installation with bbPress, Gutenberg, and the Two-Factor plugins active network-wide. | ||
| - Test real user flows: enabling 2FA, verifying enforcement, checking the settings UI, revalidation prompts. | ||
| - Take screenshots to verify visual state when relevant. | ||
| - The user profile / 2FA settings page is at `http://localhost:8888/support/users/admin/edit/account/` (bbPress user edit page). | ||
|
|
||
| ### Validating Changes | ||
|
|
||
| Before opening a PR, verify your changes pass all checks: | ||
|
|
||
| 1. **PHP tests:** `npm test` | ||
| 2. **JS tests:** `npm run test:js` | ||
| 3. **JS linting:** `npm run lint:js` | ||
| 4. **PHP linting:** `npx wp-env run cli --env-cwd=wp-content/plugins/wporg-two-factor composer lint` | ||
| 5. **E2E verification (mandatory):** Use Playwright to verify the change works in the browser. This is required for every PR — even if the change seems low-risk, open the affected pages and confirm there are no regressions. For UI changes, take screenshots to document the result. | ||
|
|
||
| If any check fails, fix the issue — do not skip or ignore failures. | ||
|
|
||
| ## Project Layout | ||
|
|
||
| ``` | ||
| wporg-two-factor.php # Main plugin entry point (namespace: WordPressdotorg\Two_Factor) | ||
| class-encrypted-totp-provider.php # TOTP provider with at-rest encryption | ||
| class-wporg-webauthn-provider.php # WebAuthn provider with caching | ||
| stats.php # 2FA adoption analytics | ||
| settings/ | ||
| settings.php # Settings page registration, replaces core 2FA UI | ||
| rest-api.php # REST endpoints for TOTP setup, provider status, passwords | ||
| package.json # @wordpress/scripts block package | ||
| src/ # React components for settings UI | ||
| components/ # UI components (TOTP, passwords, backup codes, WebAuthn) | ||
| tests/ # Jest tests for React components | ||
| revalidation/ | ||
| index.php # Session revalidation / "sudo mode" system | ||
| tests/ | ||
| bootstrap.php # Test bootstrap with WordPress.org mocks | ||
| test-wporg-two-factor.php # Main PHP test suite | ||
| settings/ | ||
| test-rest-api.php # REST API endpoint tests | ||
| .wp-env.json # wp-env configuration — plugins MUST use GitHub repo refs (not zip downloads) for Copilot agents | ||
| .wp-env/ | ||
| after-start.sh # Lifecycle script: composer install, plugin activation, bbPress config | ||
| mu-plugins/ # Mock mu-plugins for local development | ||
| ``` | ||
|
|
||
| ## Build & Test Commands | ||
|
|
||
| | Task | Command | | ||
| |---|---| | ||
| | Start dev environment | `npx wp-env start` | | ||
| | Run PHP tests | `npm test` | | ||
| | Run JS tests | `npm run test:js` | | ||
| | Lint JS | `npm run lint:js` | | ||
| | Lint PHP | `npx wp-env run cli --env-cwd=wp-content/plugins/wporg-two-factor composer lint` | | ||
| | Build settings block | `npm run build --workspaces` | | ||
| | WP-CLI in dev env | `npx wp-env run cli wp <command>` | | ||
|
|
||
| ## Commit Message Style | ||
|
|
||
| Follow the existing style visible in `git log`. Use a short imperative subject line with a category prefix when appropriate (e.g., "WebAuthN:", "Revalidation:", "Tests:", "Build:"). Keep messages concise and focused on _why_, not _what_. | ||
|
|
||
| ## Key Dependencies | ||
|
|
||
| - **Two-Factor** (`WordPress/two-factor`): Core 2FA framework — provides `Two_Factor_Core`, `Two_Factor_Totp`, `Two_Factor_Backup_Codes`. | ||
| - **Two-Factor WebAuthn** (`two-factor-provider-webauthn`): WebAuthn provider — provides `TwoFactor_Provider_WebAuthn`. | ||
| - **bbPress**: Forum plugin — user profiles integrate with 2FA settings. | ||
| - **wporg-mu-plugins**: WordPress.org shared utilities including encryption functions. | ||
| - **Gutenberg**: Block editor — settings UI is a block. | ||
|
|
||
| ## What NOT to Do | ||
|
|
||
| - Do not refactor code you weren't asked to change. | ||
| - Do not add features beyond what the issue requests. | ||
| - Do not weaken security checks or enforcement for convenience. | ||
| - Do not skip or disable pre-commit hooks or linting. | ||
| - Do not introduce new dependencies without strong justification. | ||
| - Do not change CI/CD workflows unless the issue specifically requires it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| --- | ||
| applyTo: "settings/**/*.{js,jsx,ts,tsx}" | ||
| --- | ||
|
|
||
| JavaScript/React conventions for the settings UI: | ||
|
|
||
| - This is a `@wordpress/scripts` block package. Use WordPress JS coding standards. | ||
| - Tabs for indentation. | ||
| - Import WordPress packages from `@wordpress/*` (e.g., `@wordpress/element`, `@wordpress/api-fetch`, `@wordpress/i18n`). | ||
| - Use `apiFetch` for REST API calls, not raw `fetch`. | ||
| - Translatable strings use `__()`, `_n()`, `sprintf()` from `@wordpress/i18n`. | ||
| - Jest tests live in `settings/src/tests/` and use `@testing-library/react`. | ||
| - Run JS tests with `npm run test:js`, lint with `npm run lint:js`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| --- | ||
| applyTo: "**/*.php" | ||
| --- | ||
|
|
||
| Follow WordPress PHP coding standards and WordPress documentation standards: | ||
| - Tabs for indentation, never spaces. | ||
| - Snake_case for function and variable names. | ||
| - Opening braces on the same line as the statement. | ||
| - Space inside parentheses: `if ( $condition )`, `function_call( $arg )`. | ||
| - Use strict type comparisons (`===`, `!==`) unless there's a specific reason not to. | ||
| - Sanitize all input, escape all output. Use `sanitize_*()`, `esc_html()`, `esc_attr()`, `wp_kses()` as appropriate. | ||
| - Prefix functions and hooks with the plugin namespace or use the `WordPressdotorg\Two_Factor` namespace. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| --- | ||
| applyTo: "tests/**" | ||
| --- | ||
|
|
||
| Testing rules for this security plugin: | ||
|
|
||
| - Every code change requires corresponding test coverage. | ||
| - Test classes extend `WP_UnitTestCase`. | ||
| - Use `wpSetUpBeforeClass( WP_UnitTest_Factory $factory )` for expensive setup shared across tests. | ||
| - Use `tear_down()` to reset globals and state after each test. | ||
| - NEVER use `remove_all_filters()` or `remove_all_actions()`. Always save callbacks to a variable and remove the specific callback. | ||
| - Use `@covers` annotations on every test method to track coverage. | ||
| - Test both positive and negative cases — especially for security enforcement (e.g., verify that capability stripping works AND that it doesn't affect users who have 2FA enabled). | ||
| - The bootstrap file (`tests/bootstrap.php`) provides mock WordPress.org functions. Check what's available before creating new mocks. | ||
| - Run tests with `npm test` which executes PHPUnit inside the wp-env Docker container. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| name: "Copilot Setup Steps" | ||
|
|
||
| on: workflow_dispatch | ||
|
|
||
| jobs: | ||
| copilot-setup-steps: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| timeout-minutes: 30 | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version-file: '.nvmrc' | ||
| cache: 'npm' | ||
|
|
||
| - name: Setup PHP | ||
| uses: shivammathur/setup-php@v2 | ||
| with: | ||
| php-version: '8.3' | ||
| tools: composer:v2 | ||
|
|
||
| - name: Cache node_modules | ||
| uses: actions/cache@v4 | ||
| id: cache-node-modules | ||
| with: | ||
| path: | | ||
| node_modules | ||
| settings/node_modules | ||
| key: node-modules-${{ hashFiles('package-lock.json', 'settings/package-lock.json') }} | ||
|
|
||
| - name: Install NPM dependencies | ||
| if: steps.cache-node-modules.outputs.cache-hit != 'true' | ||
| run: npm install | ||
|
|
||
| - name: Cache Docker images | ||
| uses: actions/cache@v4 | ||
| id: cache-docker | ||
| with: | ||
| path: /tmp/wp-env-docker | ||
| key: docker-wp-env-${{ hashFiles('.wp-env.json') }} | ||
|
|
||
| - name: Load cached Docker images | ||
| if: steps.cache-docker.outputs.cache-hit == 'true' | ||
| run: | | ||
obenland marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| shopt -s nullglob | ||
| for f in /tmp/wp-env-docker/*.tar; do | ||
| docker load < "$f" | ||
| done | ||
|
|
||
| # Docker is pre-installed on ubuntu-latest runners. | ||
| # wp-env uses Docker to run WordPress + MySQL containers. | ||
| - name: Start wp-env | ||
| run: npx wp-env start --xdebug=coverage | ||
|
|
||
| - name: Save Docker images to cache | ||
| if: steps.cache-docker.outputs.cache-hit != 'true' | ||
| run: | | ||
| mkdir -p /tmp/wp-env-docker | ||
| images=$(docker ps -a --format '{{.Image}} {{.Names}}' | awk '$2 ~ /^wp-env/ {print $1}' | sort -u) | ||
| for image in $images; do | ||
| filename=$(echo "$image" | tr '/:' '_') | ||
| docker save "$image" -o "/tmp/wp-env-docker/${filename}.tar" | ||
| done | ||
|
|
||
| - name: Verify environment is healthy | ||
| run: | | ||
| # Verify WordPress is responding | ||
| curl -sf http://localhost:8888/ > /dev/null | ||
| curl -sf http://localhost:8889/ > /dev/null | ||
| # Run test suites to confirm everything works | ||
| npx wp-env run tests-cli --env-cwd=wp-content/plugins/wporg-two-factor vendor/bin/phpunit | ||
| npm run test:js | ||
| npm run lint:js | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,3 +3,4 @@ tests/.phpunit.result.cache | |
| tests/coverage | ||
| vendor | ||
| node_modules | ||
| .claude/settings.local.json | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| 18 | ||
| 24 | ||
obenland marked this conversation as resolved.
Show resolved
Hide resolved
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.