Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
c40a158
docs: add AGENTS.md as the canonical contributor and agent guide
enricobattocchi Apr 20, 2026
9e18945
docs: add Cursor and Copilot pointers to AGENTS.md
enricobattocchi Apr 20, 2026
145458c
docs(contributing): refresh CONTRIBUTING.md
enricobattocchi Apr 20, 2026
ae075f8
docs: ask contributors and reviewers to prefer atomic commits
enricobattocchi Apr 20, 2026
4136504
docs: document GPL-2.0-or-later license compliance for contributors
enricobattocchi Apr 23, 2026
246eb3d
docs: add create-pr workflow recipe and Cursor rule pointer
enricobattocchi Apr 23, 2026
36cd504
docs: ask contributors and reviewers to keep descriptions focused
enricobattocchi Apr 23, 2026
ff4a3df
docs: make CONTRIBUTING.md the canonical source, shrink AGENTS.md
enricobattocchi Apr 23, 2026
79f0d69
docs: point tool-specific entry points at CONTRIBUTING.md first
enricobattocchi Apr 23, 2026
f18b87f
docs(contributing): add a table of contents
enricobattocchi Apr 23, 2026
dc27445
docs: correct src/generated/ guidance — gitignored, do not commit
enricobattocchi Apr 23, 2026
dc9d352
docs: align bugfix-format guidance with the PR template
enricobattocchi Apr 23, 2026
7ca0408
docs: drop svn-assets from the generated/do-not-edit lists
enricobattocchi Apr 24, 2026
1343cf9
docs: require grunt build:images when a PR adds or edits images
enricobattocchi Apr 24, 2026
d8b71f9
docs: tighten the changelog-bullet rule, relax Context/RTC wording
enricobattocchi Apr 24, 2026
7955d3b
docs: skip QA instructions for non-user-facing PRs
enricobattocchi Apr 24, 2026
bba0b0c
docs: point security reports at security.txt instead of email
enricobattocchi Apr 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .cursor/rules/agents.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
description: Yoast SEO contributor and agent instructions — always apply
alwaysApply: true
---

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.

@../../.github/CONTRIBUTING.md

@../../AGENTS.md

Additional authoritative doc:

- [`.github/PULL_REQUEST_TEMPLATE.md`](../../.github/PULL_REQUEST_TEMPLATE.md) — PR template and changelog conventions.

If anything in this file contradicts `CONTRIBUTING.md`, the PR template, or `AGENTS.md`, prefer those.
14 changes: 14 additions & 0 deletions .cursor/rules/create-pr.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
description: Yoast SEO — create a pull request workflow. Apply whenever the user asks to open, file, or update a pull request.
alwaysApply: false
---

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.

@../../docs/workflows/create-pr.md

The recipe's own references are:

- [`AGENTS.md`](../../AGENTS.md) — conventions and architecture.
- [`.github/CONTRIBUTING.md`](../../.github/CONTRIBUTING.md) — pre-push checks and coverage policy.
- [`.github/PULL_REQUEST_TEMPLATE.md`](../../.github/PULL_REQUEST_TEMPLATE.md) — changelog grammar, labels, bracket syntax, release-branch rules.
256 changes: 221 additions & 35 deletions .github/CONTRIBUTING.md

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ What do we want to achieve with this PR? Why did we write this code?
## Summary

<!--
Keep each bullet to one short sentence — put extra context in *Context* or *Relevant technical choices*, not here.
Attach one of the following labels to the PR: `changelog: bugfix`, `changelog: enhancement`, `changelog: other`, `changelog: non-user-facing`.
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 ...”
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 ....
Expand Down Expand Up @@ -47,7 +48,8 @@ which type/editor/browser should be tested in particular, multisite with subfold
### Test instructions for QA when the code is in the RC
<!--
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.
QA is our Quality Assurance team. The RC is the release candidate zip that is tested before a release
QA is our Quality Assurance team. The RC is the release candidate zip that is tested before a release.
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.
-->

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

## Innovation

Expand Down
54 changes: 54 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# GitHub Copilot review instructions

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.

## Review priorities

### PR hygiene

- Every required section of the PR template must have content — no empty bullets.
- At least one changelog bullet in the *Summary* section.
- A `changelog:` label attached (`bugfix`, `enhancement`, `other`, `non-user-facing`). Milestones are set by the merger, not the author.
- Bugfix entries: past tense, incorrect behaviour then triggering condition, no hypotheticals.
- 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>]`.
- Title under ~70 characters; Conventional Commits prefix where natural.
- 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.
- Flag *Test instructions* written as prose rather than concrete steps, and restatements of linked issues/epics that should be links instead.
- 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*.
- Release-branch PRs (`release/*`): unreleased-bug fixes use `changelog: non-user-facing` and `Fixes an unreleased bug where …`.

### Tests and coverage

- 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.
- Coverage should not decrease. If it does, the PR's *Relevant technical choices* section must explain why tests weren't feasible.
- Flag PRs touching WP-integration code that have no integration coverage and no mention of `composer test-wp-env`.

### Code style and structure

- 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\…`.
- 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.
- Concept-based folders (`src/integrations/`, `src/generators/`, `src/presenters/`, …) are still valid when extending existing features.
- `admin/` and `inc/` are **maintenance-only** — flag new features placed there.

### Safety

- 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.
- Flag any committed secrets, credentials, or API keys.
- Flag disabled CS checks, skipped tests, or bypassed CI gates without explanation.
- Breaking changes to public extension points (filters, actions, surfaces, REST routes) must be called out in the PR body.

### Assets

- 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.

### Security

- Watch for SQL injection, XSS, CSRF, unescaped output, missing nonces on state-changing requests, and capability checks that are missing or wrong.
- Sanitise at input boundary (`sanitize_text_field`, `absint`, …) and escape on output (`esc_html`, `esc_attr`, `esc_url`).

## What NOT to flag

- Pre-existing issues in unchanged lines — focus on the diff.
- Thresholded phpcs warnings in legacy code; only flag *new* errors/warnings attributable to this PR.
- Style variants that match the surrounding file.
- Cosmetic wording choices in changelog bullets that still respect the conciseness and grammar rules.
31 changes: 31 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# AGENTS.md — Yoast SEO agent delta

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).

**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**.

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.

## Behaviour rules for agents

- **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.

- **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.

- **Ask, don't guess.**
- 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.
- 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).

- **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.

- **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.

- **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.

- **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).

- **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).

- **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).

- **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.
7 changes: 7 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# CLAUDE.md

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.

@.github/CONTRIBUTING.md

@AGENTS.md
94 changes: 94 additions & 0 deletions docs/workflows/create-pr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Creating a pull request

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.

Rules and templates live elsewhere — this file is the **procedure**:

- **Changelog grammar, label rules, bracket-prefix rules, release-branch rules** → [`.github/PULL_REQUEST_TEMPLATE.md`](../../.github/PULL_REQUEST_TEMPLATE.md)
- **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)
- **Agent-specific behaviours (delegate long-running commands, verify before recommending, ask don't guess)** → [`AGENTS.md`](../../AGENTS.md)

If anything here contradicts those files, prefer them. Edits to rules belong there, not in this file.

## 0. Creator vs. merger responsibilities

Every merged PR needs a milestone, a changelog entry, and a changelog label. Split:

- **Creator provides:** a changelog entry (in the PR body) and a changelog label on the PR.
- **Creator MUST NOT set:** the milestone. That is the merger's job. Do not add a milestone via `gh pr edit --milestone`.

## 1. Gather context

Run in parallel:

- `git status`
- `git diff`
- `git log <base>..HEAD` and `git diff <base>...HEAD` — inspect **every** commit since the branch diverged, not just the tip
- Check whether the branch has an upstream and is up to date

Identify the base branch: usually `trunk`. PRs targeting `release/*` follow stricter label rules — see the PR template.

Read [`.github/PULL_REQUEST_TEMPLATE.md`](../../.github/PULL_REQUEST_TEMPLATE.md) so the PR body matches it section-for-section.

## 2. Verify checks

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.

Do not paper over failures by excluding tests, lowering CS thresholds, or adding ignore pragmas without explicit permission.

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.

## 3. Decide how many changelog bullets the PR needs

Default is **one bullet**, describing one logical change. Write more than one bullet only in these cases:

- **Case A — multiple distinct changes in the same repo and same label.** One bullet per change; no brackets.
- **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.
- **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.

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.

If you are not certain a change affects another repo (Premium, Shopify, etc.), ask the user rather than guessing.

## 4. Fill every PR-template section

Cover every section of `.github/PULL_REQUEST_TEMPLATE.md`:

- **Context** — *why* the change is being made.
- **Summary** — the bullet(s) from step 3.
- **Relevant technical choices** — non-obvious decisions, trade-offs, architectural notes. **If coverage decreased, explain here why tests weren't feasible.**
- **Test instructions (acceptance)** — step-by-step, aimed at non-technical users.
- **Relevant test scenarios** — tick the boxes that apply and explain why for each ticked box.
- **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).
- **Impact check** — parts of the plugin that may need regression testing.
- **Other environments** — tick Shopify / Google Docs boxes only when the corresponding entry is present in step 3.
- **Documentation / Quality assurance / Innovation** checkboxes — tick only those that are actually true.
- **Fixes #** — link the issue being closed, if any.

Preserve the HTML comments from the template so future editors keep the same structure.

## 5. Create and label the PR

1. Push the branch if it has no upstream: `git push -u origin <branch>`.
2. `gh pr create --title "<title>" --body "$(cat <<'EOF' ... EOF)"`.
- Title under 70 characters. Use a Conventional Commits prefix when natural (`fix: …`, `feat(dashboard): …`).
- The body is the completed template, verbatim-structured.
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.
4. **Never set the milestone.**
5. Return the PR URL.

## 6. Self-check

Before reporting the PR as created, verify:

- [ ] Pre-push checks (step 2) ran and passed — any skipped check is disclosed in the PR body.
- [ ] Coverage increased or held flat — or the *Relevant technical choices* section explains why tests weren't feasible.
- [ ] The body contains at least one changelog bullet, correctly prefixed for every affected changelog.
- [ ] Exactly one `changelog:` label is attached; Shopify / Google Docs / innovation labels match the content.
- [ ] Bugfix bullets describe the incorrect behaviour followed by the triggering condition, in past tense, per the PR template's grammar rules.
- [ ] Every PR-template section has content — no empty bullets, no leftover placeholder text.
- [ ] No milestone was set.
- [ ] 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.
- [ ] Each changelog bullet is one short sentence; extra context lives in *Context* or *Relevant technical choices*.

If any item fails, fix it (edit the body or labels with `gh pr edit`) before returning the URL.
Loading