Skip to content

Commit 777600d

Browse files
Merge pull request #23195 from Yoast/docs/agents-md-contributor-guide
docs: introduce a contributor guide for humans and AI coding tools
2 parents 1b94ba5 + bba0b0c commit 777600d

8 files changed

Lines changed: 441 additions & 37 deletions

File tree

.cursor/rules/agents.mdc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
description: Yoast SEO contributor and agent instructions — always apply
3+
alwaysApply: true
4+
---
5+
6+
The canonical contributor guide for this repository is [`.github/CONTRIBUTING.md`](../../.github/CONTRIBUTING.md). It covers architecture, tooling, workflows, testing, code style, commits, and PR conventions. [`AGENTS.md`](../../AGENTS.md) adds a short set of behaviours specific to AI coding agents on top. Cursor should treat `CONTRIBUTING.md` as the primary source of truth and `AGENTS.md` as the agent-behaviour delta.
7+
8+
@../../.github/CONTRIBUTING.md
9+
10+
@../../AGENTS.md
11+
12+
Additional authoritative doc:
13+
14+
- [`.github/PULL_REQUEST_TEMPLATE.md`](../../.github/PULL_REQUEST_TEMPLATE.md) — PR template and changelog conventions.
15+
16+
If anything in this file contradicts `CONTRIBUTING.md`, the PR template, or `AGENTS.md`, prefer those.

.cursor/rules/create-pr.mdc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
description: Yoast SEO — create a pull request workflow. Apply whenever the user asks to open, file, or update a pull request.
3+
alwaysApply: false
4+
---
5+
6+
When the user asks to create, open, or update a pull request, follow the procedure in [`docs/workflows/create-pr.md`](../../docs/workflows/create-pr.md) exactly. That file is the canonical workflow shared across tools — do not reinvent it from context.
7+
8+
@../../docs/workflows/create-pr.md
9+
10+
The recipe's own references are:
11+
12+
- [`AGENTS.md`](../../AGENTS.md) — conventions and architecture.
13+
- [`.github/CONTRIBUTING.md`](../../.github/CONTRIBUTING.md) — pre-push checks and coverage policy.
14+
- [`.github/PULL_REQUEST_TEMPLATE.md`](../../.github/PULL_REQUEST_TEMPLATE.md) — changelog grammar, labels, bracket syntax, release-branch rules.

.github/CONTRIBUTING.md

Lines changed: 221 additions & 35 deletions
Large diffs are not rendered by default.

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ What do we want to achieve with this PR? Why did we write this code?
88
## Summary
99

1010
<!--
11+
Keep each bullet to one short sentence — put extra context in *Context* or *Relevant technical choices*, not here.
1112
Attach one of the following labels to the PR: `changelog: bugfix`, `changelog: enhancement`, `changelog: other`, `changelog: non-user-facing`.
1213
If the changelog item is a bugfix, describe the incorrect behaviour that occurred, followed by the condition that triggered it. Use clear, past tense language and avoid hypothetical or nested conditionals. Example structure: “Fixes a bug where... happened when/was caused by ...”
1314
If the changelog item is meant for the changelog of another add-on, start your changelog item with the name of that add-on's repo between square brackets, for example: * [wordpress-seo-premium] Fixes a bug where ....
@@ -47,7 +48,8 @@ which type/editor/browser should be tested in particular, multisite with subfold
4748
### Test instructions for QA when the code is in the RC
4849
<!--
4950
Sometimes some steps from the test instructions for the acceptance test aren't relevant anymore once the code has been merged or the feature is complete. If that is the case, do not check the checkbox below.
50-
QA is our Quality Assurance team. The RC is the release candidate zip that is tested before a release
51+
QA is our Quality Assurance team. The RC is the release candidate zip that is tested before a release.
52+
For non-user-facing PRs (documentation, pure refactors, internal tooling), write "Not applicable" in the steps below and leave the checkbox unticked — QA verifies user-visible behaviour at RC time, not internal changes.
5153
-->
5254

5355
* [ ] QA should use the same steps as above.
@@ -85,7 +87,7 @@ This PR affects the following parts of the plugin, which may require extra testi
8587
* [ ] If any part of the code is behind a feature flag, my test instructions also cover cases where the feature flag is switched off.
8688
* [ ] I have written this PR in accordance with my team's definition of done.
8789
* [ ] I have checked that the base branch is correctly set.
88-
* [ ] I have run `grunt build:images` and commited the results, if my PR introduces new images or SVGs.
90+
* [ ] I have run `grunt build:images` and committed the results, if my PR introduces or edits images or SVGs.
8991

9092
## Innovation
9193

.github/copilot-instructions.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# GitHub Copilot review instructions
2+
3+
Used here for **pull request reviews**. Canonical rules: [`CONTRIBUTING.md`](./CONTRIBUTING.md) and [`PULL_REQUEST_TEMPLATE.md`](./PULL_REQUEST_TEMPLATE.md). [`AGENTS.md`](../AGENTS.md) adds agent-behaviour guidance. If anything below contradicts these, prefer them.
4+
5+
## Review priorities
6+
7+
### PR hygiene
8+
9+
- Every required section of the PR template must have content — no empty bullets.
10+
- At least one changelog bullet in the *Summary* section.
11+
- A `changelog:` label attached (`bugfix`, `enhancement`, `other`, `non-user-facing`). Milestones are set by the merger, not the author.
12+
- Bugfix entries: past tense, incorrect behaviour then triggering condition, no hypotheticals.
13+
- Multiple changelog bullets only when (a) the PR has two logically distinct changes, (b) types differ — then use `[repo type]` override on the divergent bullets, or (c) the change must land in multiple repos/packages, in which case each extra bullet is prefixed with `[<repo-or-package>]`.
14+
- Title under ~70 characters; Conventional Commits prefix where natural.
15+
- Prefer atomic commits: if the PR mixes unrelated changes in a single commit (e.g. bugfix + refactor + tooling), suggest splitting into separate commits (or separate PRs) when practical.
16+
- Flag *Test instructions* written as prose rather than concrete steps, and restatements of linked issues/epics that should be links instead.
17+
- Flag changelog bullets that run more than one sentence, or stack multiple independent clauses with commas, colons, or semicolons. Long-form explanation belongs in *Context* / *Relevant technical choices*.
18+
- Release-branch PRs (`release/*`): unreleased-bug fixes use `changelog: non-user-facing` and `Fixes an unreleased bug where …`.
19+
20+
### Tests and coverage
21+
22+
- New or changed PHP classes/methods should ship unit tests under `tests/Unit/…` mirroring the source path. One test method per tested method; shared setup via abstract base classes or traits.
23+
- Coverage should not decrease. If it does, the PR's *Relevant technical choices* section must explain why tests weren't feasible.
24+
- Flag PRs touching WP-integration code that have no integration coverage and no mention of `composer test-wp-env`.
25+
26+
### Code style and structure
27+
28+
- PHP must pass **Yoast Coding Standards** (`yoast/yoastcs`, a WPCS superset) — flag violations `composer check-branch-cs` would catch. Classes use snake_case with underscores (e.g. `Introductions_Collector`); namespaces under `Yoast\WP\SEO\…`.
29+
- New self-contained features go under `src/<feature>/{domain,application,infrastructure,user-interface}/`. Dependencies point inward only — domain must not reference WP functions or infrastructure.
30+
- Concept-based folders (`src/integrations/`, `src/generators/`, `src/presenters/`, …) are still valid when extending existing features.
31+
- `admin/` and `inc/` are **maintenance-only** — flag new features placed there.
32+
33+
### Safety
34+
35+
- Flag any file under `src/generated/`, `vendor/`, `vendor_prefixed/`, `build/`, `js/dist/`, `css/dist/`, `artifact/`, `languages/`, or `node_modules/` appearing in the diff — these paths are generated or vendored and gitignored; they must not be committed.
36+
- Flag any committed secrets, credentials, or API keys.
37+
- Flag disabled CS checks, skipped tests, or bypassed CI gates without explanation.
38+
- Breaking changes to public extension points (filters, actions, surfaces, REST routes) must be called out in the PR body.
39+
40+
### Assets
41+
42+
- If the diff adds or edits image files (`.png`, `.jpg`, `.gif`, `.svg` — primarily under `images/` or `svn-assets/`), the author must run `grunt build:images` (or plain `grunt build`) and commit the resulting optimised files. The *Quality assurance* checkbox "I have run `grunt build:images` and committed the results…" in the PR template is the canonical signal — if image files are in the diff and that checkbox is unticked, flag it. The release pipeline fails on images that haven't been pre-optimised.
43+
44+
### Security
45+
46+
- Watch for SQL injection, XSS, CSRF, unescaped output, missing nonces on state-changing requests, and capability checks that are missing or wrong.
47+
- Sanitise at input boundary (`sanitize_text_field`, `absint`, …) and escape on output (`esc_html`, `esc_attr`, `esc_url`).
48+
49+
## What NOT to flag
50+
51+
- Pre-existing issues in unchanged lines — focus on the diff.
52+
- Thresholded phpcs warnings in legacy code; only flag *new* errors/warnings attributable to this PR.
53+
- Style variants that match the surrounding file.
54+
- Cosmetic wording choices in changelog bullets that still respect the conciseness and grammar rules.

AGENTS.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# AGENTS.md — Yoast SEO agent delta
2+
3+
This file follows the [AGENTS.md](https://agents.md/) convention (supported natively by OpenAI Codex, Cursor, Aider, Zed, Copilot, Gemini CLI, Windsurf, JetBrains Junie, and others; Claude Code reads it via a thin `CLAUDE.md` pointer).
4+
5+
**Read [`.github/CONTRIBUTING.md`](./.github/CONTRIBUTING.md) first.** It is the canonical source of truth for everything in this repository: architecture, repository layout, code placement, dependency injection, build and test commands, coding standards, commits, branches, the PR opening procedure, changelog rules, license, and security. This file does not repeat any of that — it only lists the behaviours specific to working in this repo as an **AI coding agent**.
6+
7+
If a rule appears to conflict between this file and `CONTRIBUTING.md` or [`.github/PULL_REQUEST_TEMPLATE.md`](./.github/PULL_REQUEST_TEMPLATE.md), prefer those. Edit the source, not this delta.
8+
9+
## Behaviour rules for agents
10+
11+
- **Delegate long-running commands.** Do not run `composer`, `yarn`, or `grunt` in the main session — the output clutters context. If a build-runner-style agent is available, delegate to it and have it return PASS/FAIL plus failing-case details only. When no such agent is available, summarise the output rather than inlining it.
12+
13+
- **Verify before you recommend.** Before citing a file path, function name, class, flag, or any other code identifier from memory, confirm it still exists — `ls`, `grep`, or a file read. Memory is a snapshot; the tree may have moved.
14+
15+
- **Ask, don't guess.**
16+
- If a change might affect Premium, Shopify, the Google Docs extension, another JS package, or any other repo but the diff does not prove it, ask before adding a cross-repo changelog entry or label.
17+
- If the licensing status of a piece of reused or AI-generated code is unclear, ask before including it. See [CONTRIBUTING.md → "License and copyright"](./.github/CONTRIBUTING.md#license-and-copyright).
18+
19+
- **Prefer editing over creating.** Before adding a new file, a new abstraction, or a new directory, search for where similar functionality already lives and extend that instead. New files are the last resort.
20+
21+
- **Don't paper over failures.** If a pre-push check, test, or coding-standard rule fails, fix it or flag it. Do not skip tests, lower CS thresholds, add ignore pragmas, bypass CI gates, or untick quality-assurance boxes on the PR template without explicit permission.
22+
23+
- **Respect the creator / merger split on PRs.** When opening a PR, never set the milestone and never add the `community-patch` label. Both are the merger's or automation's job. The full procedure lives in [`docs/workflows/create-pr.md`](./docs/workflows/create-pr.md) — follow it instead of reinventing the steps.
24+
25+
- **Don't hand-edit generated or vendored files.** `src/generated/`, `vendor/`, `vendor_prefixed/`, `build/`, `js/dist/`, `css/dist/`, `artifact/`, `languages/`, `node_modules/` — regenerate via the appropriate tooling. The full list and the tool for each is in [CONTRIBUTING.md → "Repository layout"](./.github/CONTRIBUTING.md#repository-layout).
26+
27+
- **Run `grunt build:images` when you touch images.** Adding or editing any `.png`, `.jpg`, `.gif`, or `.svg` file — primarily under `images/` or `svn-assets/` — requires running `grunt build:images` (or plain `grunt build`, which includes it) and committing the optimised output before the PR is opened or updated. The release pipeline re-runs `imagemin` and fails if the committed images aren't already optimised. Full detail in [CONTRIBUTING.md → "Before you push or open/update a PR"](./.github/CONTRIBUTING.md#before-you-push-or-openupdate-a-pr).
28+
29+
- **Keep changelog bullets to one short sentence.** Extra context goes in *Context* or *Relevant technical choices*, not in the bullet. See [CONTRIBUTING.md → "Changelog entry and label"](./.github/CONTRIBUTING.md#changelog-entry-and-label).
30+
31+
- **Default to CONTRIBUTING.md.** Anything not listed in this delta is in `CONTRIBUTING.md`, the PR template, or `src/README.md`. Read those before making assumptions.

CLAUDE.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# CLAUDE.md
2+
3+
The canonical contributor guide for this repository is [`.github/CONTRIBUTING.md`](./.github/CONTRIBUTING.md) — it covers architecture, tooling, workflows, testing, code style, commits, and PR conventions. [`AGENTS.md`](./AGENTS.md) layers a short set of behaviours specific to AI coding agents on top. Both are pulled into context below so Claude Code sees them on every session.
4+
5+
@.github/CONTRIBUTING.md
6+
7+
@AGENTS.md

docs/workflows/create-pr.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Creating a pull request
2+
3+
This is the canonical workflow for opening a pull request on this repository. It is written to be followed by both humans and AI coding agents. Tool-specific entry points — the Claude Code skill, the Cursor rule, any future Copilot chat mode — should be thin pointers to this file so every tool follows the same procedure.
4+
5+
Rules and templates live elsewhere — this file is the **procedure**:
6+
7+
- **Changelog grammar, label rules, bracket-prefix rules, release-branch rules**[`.github/PULL_REQUEST_TEMPLATE.md`](../../.github/PULL_REQUEST_TEMPLATE.md)
8+
- **Architecture, repository layout, placement rules, dependency injection, build/test commands, Yoast CS ruleset, testing policy, coverage policy, commit style, branching, pre-push checks**[`.github/CONTRIBUTING.md`](../../.github/CONTRIBUTING.md)
9+
- **Agent-specific behaviours (delegate long-running commands, verify before recommending, ask don't guess)**[`AGENTS.md`](../../AGENTS.md)
10+
11+
If anything here contradicts those files, prefer them. Edits to rules belong there, not in this file.
12+
13+
## 0. Creator vs. merger responsibilities
14+
15+
Every merged PR needs a milestone, a changelog entry, and a changelog label. Split:
16+
17+
- **Creator provides:** a changelog entry (in the PR body) and a changelog label on the PR.
18+
- **Creator MUST NOT set:** the milestone. That is the merger's job. Do not add a milestone via `gh pr edit --milestone`.
19+
20+
## 1. Gather context
21+
22+
Run in parallel:
23+
24+
- `git status`
25+
- `git diff`
26+
- `git log <base>..HEAD` and `git diff <base>...HEAD` — inspect **every** commit since the branch diverged, not just the tip
27+
- Check whether the branch has an upstream and is up to date
28+
29+
Identify the base branch: usually `trunk`. PRs targeting `release/*` follow stricter label rules — see the PR template.
30+
31+
Read [`.github/PULL_REQUEST_TEMPLATE.md`](../../.github/PULL_REQUEST_TEMPLATE.md) so the PR body matches it section-for-section.
32+
33+
## 2. Verify checks
34+
35+
Follow the pre-push checklist in [`.github/CONTRIBUTING.md` — "Before you push or open/update a PR"](../../.github/CONTRIBUTING.md). All required checks must pass **before** calling `gh pr create`. Do not silently skip a check; if something cannot be run locally (e.g. `composer test-wp-env` without Docker), disclose it in the PR body's *Test instructions* section.
36+
37+
Do not paper over failures by excluding tests, lowering CS thresholds, or adding ignore pragmas without explicit permission.
38+
39+
If a `build-runner`-style agent is available in your tool, delegate the actual command execution there so the main session stays focused on the workflow.
40+
41+
## 3. Decide how many changelog bullets the PR needs
42+
43+
Default is **one bullet**, describing one logical change. Write more than one bullet only in these cases:
44+
45+
- **Case A — multiple distinct changes in the same repo and same label.** One bullet per change; no brackets.
46+
- **Case B — bullets with different changelog types.** Add a `[repo type]` override only on the bullets whose type *differs* from the PR label; bullets that match the PR label stay unprefixed.
47+
- **Case C — impact on multiple repos or packages.** One bullet per affected changelog, each prefixed with `[<repo-or-package>]`. Routing prefix is mandatory for non-Free targets.
48+
49+
Grammar, bracket syntax, semver hints, bugfix template, and the release-branch label rules all live in the PR template — follow the template, do not restate the rules here or in the PR body.
50+
51+
If you are not certain a change affects another repo (Premium, Shopify, etc.), ask the user rather than guessing.
52+
53+
## 4. Fill every PR-template section
54+
55+
Cover every section of `.github/PULL_REQUEST_TEMPLATE.md`:
56+
57+
- **Context***why* the change is being made.
58+
- **Summary** — the bullet(s) from step 3.
59+
- **Relevant technical choices** — non-obvious decisions, trade-offs, architectural notes. **If coverage decreased, explain here why tests weren't feasible.**
60+
- **Test instructions (acceptance)** — step-by-step, aimed at non-technical users.
61+
- **Relevant test scenarios** — tick the boxes that apply and explain why for each ticked box.
62+
- **QA instructions** — for non-user-facing PRs (docs, pure refactors, tooling), leave the checkbox unticked and write "Not applicable". Otherwise tick "same steps as above" when acceptance and QA match, or write separate steps (or link to the epic).
63+
- **Impact check** — parts of the plugin that may need regression testing.
64+
- **Other environments** — tick Shopify / Google Docs boxes only when the corresponding entry is present in step 3.
65+
- **Documentation / Quality assurance / Innovation** checkboxes — tick only those that are actually true.
66+
- **Fixes #** — link the issue being closed, if any.
67+
68+
Preserve the HTML comments from the template so future editors keep the same structure.
69+
70+
## 5. Create and label the PR
71+
72+
1. Push the branch if it has no upstream: `git push -u origin <branch>`.
73+
2. `gh pr create --title "<title>" --body "$(cat <<'EOF' ... EOF)"`.
74+
- Title under 70 characters. Use a Conventional Commits prefix when natural (`fix: …`, `feat(dashboard): …`).
75+
- The body is the completed template, verbatim-structured.
76+
3. Apply the changelog label immediately: `gh pr edit <number> --add-label "changelog: <type>"`. Attach Shopify / Google Docs / innovation labels in the same call only when the corresponding condition holds.
77+
4. **Never set the milestone.**
78+
5. Return the PR URL.
79+
80+
## 6. Self-check
81+
82+
Before reporting the PR as created, verify:
83+
84+
- [ ] Pre-push checks (step 2) ran and passed — any skipped check is disclosed in the PR body.
85+
- [ ] Coverage increased or held flat — or the *Relevant technical choices* section explains why tests weren't feasible.
86+
- [ ] The body contains at least one changelog bullet, correctly prefixed for every affected changelog.
87+
- [ ] Exactly one `changelog:` label is attached; Shopify / Google Docs / innovation labels match the content.
88+
- [ ] Bugfix bullets describe the incorrect behaviour followed by the triggering condition, in past tense, per the PR template's grammar rules.
89+
- [ ] Every PR-template section has content — no empty bullets, no leftover placeholder text.
90+
- [ ] No milestone was set.
91+
- [ ] If the branch adds or edits any image or SVG, `grunt build:images` was run and its optimised output committed. Tick the matching *Quality assurance* checkbox in the PR body.
92+
- [ ] Each changelog bullet is one short sentence; extra context lives in *Context* or *Relevant technical choices*.
93+
94+
If any item fails, fix it (edit the body or labels with `gh pr edit`) before returning the URL.

0 commit comments

Comments
 (0)