Skip to content

Commit 4f218d5

Browse files
Merge pull request #509 from Yoast/add-agent-documentation
docs: add AI agent documentation and contributor guide
2 parents 94ff5ae + 178c752 commit 4f218d5

7 files changed

Lines changed: 379 additions & 0 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: Duplicate Post 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: Duplicate Post — 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, and section structure.

.github/CONTRIBUTING.md

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
# Contribution Guidelines
2+
3+
Thanks for taking the time to contribute to Yoast Duplicate Post! Before filing a bug report, feature request, or pull request, please read the guidelines below.
4+
5+
This file is the canonical contributor guide for this repository. It is written for both humans and AI coding tools. The repo-root [`AGENTS.md`](../AGENTS.md) adds a small set of behaviours specific to AI agents on top of the rules here; the [`PULL_REQUEST_TEMPLATE.md`](./PULL_REQUEST_TEMPLATE.md) carries the detailed changelog and label rules.
6+
7+
## Contents
8+
9+
- [How to use GitHub](#how-to-use-github)
10+
- [Security issues](#security-issues)
11+
- [I have found a bug](#i-have-found-a-bug)
12+
- [I have a feature request](#i-have-a-feature-request)
13+
- [I want to create a patch](#i-want-to-create-a-patch)
14+
- [License and copyright](#license-and-copyright)
15+
- [Supported environment](#supported-environment)
16+
- [Repository layout](#repository-layout)
17+
- [Where to put new code](#where-to-put-new-code)
18+
- [Legacy procedural files](#legacy-procedural-files)
19+
- [No dependency-injection container](#no-dependency-injection-container)
20+
- [PHP workflow](#php-workflow)
21+
- [JavaScript workflow](#javascript-workflow)
22+
- [Testing](#testing)
23+
- [Code style](#code-style)
24+
- [Opening a pull request](#opening-a-pull-request)
25+
- [Before you push or open/update a PR](#before-you-push-or-openupdate-a-pr)
26+
- [Changelog entry and label](#changelog-entry-and-label)
27+
- [Submitting an issue you have found](#submitting-an-issue-you-have-found)
28+
- [Additional resources](#additional-resources)
29+
30+
## How to use GitHub
31+
32+
We use GitHub exclusively for well-documented bugs, feature requests, and code contributions. Communication is always done in English.
33+
34+
For support with Duplicate Post, use the [support forum](https://wordpress.org/support/plugin/duplicate-post) on WordPress.org.
35+
36+
## Security issues
37+
38+
Please do **not** report security issues on GitHub. Follow our [security program](https://yoast.com/security-program/) instead — see [`yoast.com/security.txt`](https://yoast.com/security.txt) for the canonical contact details — so we can handle them quickly and responsibly.
39+
40+
## I have found a bug
41+
42+
Before opening a new issue, please:
43+
44+
* update to the latest versions of WordPress and Duplicate Post.
45+
* search for duplicate issues to avoid filing the same report twice. If an open issue already exists, please comment on it.
46+
* check for plugin and theme conflicts, and include your findings.
47+
* check for JavaScript errors in your browser's console and include any output.
48+
* pick the matching GitHub issue form (Bug report, Feature request, Task) and fill in every section.
49+
* include everything needed to understand and reproduce the problem — screenshots, clear reproduction steps, plugin and theme versions, and any relevant logs — but stay focused. A tight, reproducible report is easier to triage than a long narrative with unrelated context.
50+
51+
## I have a feature request
52+
53+
Before opening a new issue:
54+
55+
* search for duplicate issues to avoid filing the same request twice. If an open request already exists, please add your thoughts there.
56+
* pick the Feature request issue form and explain *why* you think this feature is worth considering.
57+
58+
## I want to create a patch
59+
60+
Community patches, localizations, bug reports, and contributions are very welcome.
61+
62+
### License and copyright
63+
64+
Duplicate Post is licensed under [GPL-2.0-or-later](../LICENSE). By opening a pull request you confirm that your contribution is offered under the same license. Before contributing code, make sure that:
65+
66+
- You wrote the code yourself, or you have the right to relicense it under GPL-2.0-or-later.
67+
- You have not copied code from sources whose license is incompatible with GPL-2.0-or-later (for example proprietary code, CC-licensed snippets that restrict commercial use, or GPL-3.0-only code).
68+
- If you have reused code from a GPL-2.0-compatible source (MIT, BSD, public domain, etc.), you have preserved the original copyright notice and license header, and noted the provenance in the commit message.
69+
- You have not included code whose licensing status is unclear — including AI-generated code whose training or output terms you have not verified as compatible.
70+
71+
If you are unsure whether a piece of code is safe to include, ask in the issue or PR before opening it for review.
72+
73+
### Supported environment
74+
75+
* PHP: the minimum version is the `Requires PHP` header in [`readme.txt`](../readme.txt) (also pinned in [`composer.json`](../composer.json)).
76+
* WordPress: the supported range is the `Requires at least` and `Tested up to` headers in [`readme.txt`](../readme.txt).
77+
* The plugin is a single PHP plugin with a small JavaScript bundle (block-editor integration) built through webpack and Grunt.
78+
79+
### Repository layout
80+
81+
The top-level paths you will touch (or explicitly avoid):
82+
83+
| Path | Purpose | Editable? |
84+
| --- | --- | --- |
85+
| `src/` | Namespaced PHP (`Yoast\WP\Duplicate_Post\`). All new backend work lives here. | Yes |
86+
| `js/src/` | Source for the block-editor JavaScript. | Yes |
87+
| `tests/` | PHPUnit tests (`tests/Unit`, `tests/WP`). | Yes |
88+
| `config/` | Grunt, webpack, wp-env, composer actions, build scripts. | Yes, with care |
89+
| `compat/` | Compatibility shims for third-party plugins. | Yes, with care |
90+
| `admin-functions.php`, `common-functions.php`, `options.php` | Legacy procedural PHP. | **Maintenance only** — see below |
91+
| `duplicate-post.php` | Plugin bootstrap and manual service wiring. | With care |
92+
| `vendor/`, `node_modules/` | Composer / Yarn dependencies. | Never hand-edit |
93+
| `js/dist/`, `artifact/`, `languages/` | Generated or distribution artifacts. | Never hand-edit — regenerate via Grunt |
94+
| `readme.txt`, `changelog.md` | wordpress.org readme and the generated changelog. | See [Changelog entry and label](#changelog-entry-and-label) |
95+
96+
### Where to put new code
97+
98+
`src/` is classmap-autoloaded under the `Yoast\WP\Duplicate_Post\` namespace. Code is grouped by its role in the plugin rather than by onion layers:
99+
100+
```
101+
src/
102+
├── admin/ Admin screens, settings, and their views.
103+
├── handlers/ Request handlers (bulk, REST, save-post, links, …).
104+
├── ui/ User-facing surfaces (metabox, block editor, columns, row actions, …).
105+
├── watchers/ Hook listeners that react to post lifecycle events.
106+
└── *.php Shared services (post-duplicator, post-republisher, permissions-helper, utils, …).
107+
```
108+
109+
When you extend an existing feature, keep the new code in the matching folder and follow the surrounding patterns. New files are a last resort — prefer extending an existing class. PHP files are kebab-case (`link-handler.php`); class names follow Yoast's convention of snake_case with underscores (e.g. `Link_Handler`).
110+
111+
#### Legacy procedural files
112+
113+
`admin-functions.php`, `common-functions.php`, and `options.php` contain pre-namespace procedural code. Treat them as **maintenance-only**: fix bugs and keep them compatible, but do not add new features there. When a legacy function needs significant changes, consider extracting the affected responsibility into a class under `src/`.
114+
115+
### No dependency-injection container
116+
117+
Unlike Yoast SEO, Duplicate Post does **not** use a compiled DI container. Services are instantiated and wired by hand in the `duplicate-post.php` bootstrap. There is no `compile-di` step — when you add a service, wire it explicitly in the bootstrap and pass its dependencies through the constructor.
118+
119+
### PHP workflow
120+
121+
All commands are run from the repo root.
122+
123+
| Command | What it does |
124+
| --- | --- |
125+
| `composer update` | Install PHP dependencies. `composer.lock` is not committed in this repo, so use `update` rather than `install`. |
126+
| `composer lint` | PHP parse-error check across the repo. |
127+
| `composer check-cs` | Run phpcs with the Yoast ruleset (errors only, no warnings). |
128+
| `composer check-branch-cs` | Run phpcs against the files changed on the current branch. |
129+
| `composer check-staged-cs` | Run phpcs against staged files. |
130+
| `composer fix-cs` | Auto-fix fixable phpcs violations. |
131+
| `composer test` | Run PHPUnit unit tests (no WP, no coverage). |
132+
| `composer test-wp` | Run the WP integration tests against a local WP test install. |
133+
| `composer test-wp-env` | Run the WP integration tests inside the `wp-env` Docker environment (preferred locally). |
134+
| `composer coverage` / `coverage-wp-env` | The test commands above, with coverage. |
135+
136+
Coding standards are enforced by the Yoast Coding Standard (`yoast/yoastcs`) — a superset of the WordPress Coding Standards — plus parallel-lint for syntax. Run `composer check-branch-cs` before opening a PR.
137+
138+
### JavaScript workflow
139+
140+
The block-editor JavaScript is built through Grunt (which drives webpack). `package.json` has no npm scripts; use Grunt directly.
141+
142+
| Command | What it does |
143+
| --- | --- |
144+
| `yarn install` | Install JS build dependencies. |
145+
| `grunt` / `grunt build` | Build the block-editor JS bundle (webpack development build). |
146+
| `grunt release` | Production JS build (used by the release/artifact pipeline). |
147+
| `grunt build:images` | Optimise the wp.org store assets in `svn-assets/` (banner, icons, screenshots) via imagemin. Run manually only when you change those assets — it is **not** part of `grunt build` or the release pipeline. |
148+
149+
### Testing
150+
151+
- **Every PR that changes PHP behaviour should ship unit tests.** Place them under `tests/Unit/…`, mirroring the path of the class under test.
152+
- **Integration tests** (those that boot WordPress) live under `tests/WP/…` and run via `composer test-wp-env`, which starts an isolated WP in Docker through `@wordpress/env`.
153+
- Test one method per test class where practical; share setup via abstract base classes or traits (examples already exist under `tests/Unit`).
154+
- If you cannot run the integration tests in your environment, say so explicitly in the PR description rather than skipping them silently.
155+
156+
### Code style
157+
158+
- Follow the existing code. When two styles look plausible, match the file you are editing.
159+
- PHP: Yoast CS (`yoast/yoastcs`), configured in [`.phpcs.xml.dist`](../.phpcs.xml.dist). Namespaces live under `Yoast\WP\Duplicate_Post\…`.
160+
- The CS check enforces an error/warning **threshold** (see the `check-cs-thresholds` composer script). Do not raise the threshold to make a violation pass — fix the violation.
161+
- Comments: document **why**, not **what**. End every inline comment with a full stop.
162+
- Don't add features, scaffolding, or abstractions the task doesn't need.
163+
164+
### Opening a pull request
165+
166+
1. Create your branch from `trunk`. `trunk` is the active development branch and the default branch on GitHub — every PR should target it unless a maintainer asks otherwise. When the work tracks a GitHub issue, name your branch `<issue-number>-<short-description>` (e.g. `210-scheduled-republish`).
167+
2. Make your changes.
168+
3. Follow the [Yoast Coding Standards](https://github.com/Yoast/yoastcs).
169+
4. Document any new functions, actions, and filters following the [PHP inline-documentation standards](https://make.wordpress.org/core/handbook/best-practices/inline-documentation-standards/php/).
170+
5. Write tests. We expect every PR that changes PHP behaviour to ship unit tests.
171+
6. Use the [Conventional Commits](https://www.conventionalcommits.org/) format for commit messages (e.g. `fix: …`, `feat(ui): …`). **Prefer atomic commits** — each commit should represent a single logical change. If your PR mixes unrelated changes, split them into separate commits or ideally separate PRs.
172+
7. Push your branch and open a pull request against `trunk`. **Use the pull request template** at [`.github/PULL_REQUEST_TEMPLATE.md`](./PULL_REQUEST_TEMPLATE.md) and fill in every section — even for small changes.
173+
8. **Keep the PR description focused.** Fill every required section of the template with what the reviewer actually needs. *Test instructions* should be concrete steps, not essays. Link to the issue rather than restating it.
174+
175+
#### Before you push or open/update a PR
176+
177+
Run these checks locally and make sure each one is clean. CI runs the same checks.
178+
179+
* `composer test` — the unit test suite must pass.
180+
* `composer test-wp-env` — the WordPress integration tests (Docker via `@wordpress/env`) must pass if your change touches code that has or needs WP integration coverage.
181+
* `composer check-branch-cs` — must report **no new errors or warnings** introduced by your branch. Use `composer fix-cs` to auto-fix what it can, and address the rest by hand.
182+
* `composer lint` — PHP parse-error check.
183+
* For changes under `js/`: run `grunt build` and confirm the bundle builds.
184+
* Only if you changed a wp.org store asset under `svn-assets/`: run `grunt build:images` and commit the optimised output. (It is not run by `grunt build` or the release pipeline, so it will not happen automatically.)
185+
186+
If a check fails or you need to skip one (e.g. you can't run Docker locally for `test-wp-env`), say so explicitly in the PR description so reviewers know what still needs validating.
187+
188+
### Changelog entry and label
189+
190+
Every PR needs a **changelog entry** in the Summary section of the PR body and a **changelog label** on the PR itself:
191+
192+
* Write one bullet describing the change in present tense, 3rd person singular, ending with a full stop. For bugfixes, describe the incorrect behaviour followed by the condition that triggered it, in clear past tense (e.g. `Fixes a bug where X happened when Y`). See [`PULL_REQUEST_TEMPLATE.md`](./PULL_REQUEST_TEMPLATE.md) for the full grammar.
193+
* Keep each bullet to one short sentence. Extra context belongs in *Context* or *Relevant technical choices*, not in the bullet.
194+
* Attach one of: `changelog: bugfix`, `changelog: enhancement`, `changelog: other`, `changelog: non-user-facing`.
195+
* If the change also affects another Yoast repo, add an extra bullet prefixed with `[<repo-name>]`.
196+
* The release `changelog.md` is generated from merged PRs by [`.github/scripts/generate-changelog.sh`](./scripts/generate-changelog.sh) — do not hand-write release sections into it; the PR body's bullet and label are the source.
197+
198+
Milestones are set by the maintainer who merges your PR.
199+
200+
### Submitting an issue you have found
201+
202+
Make sure your problem doesn't already have a ticket by searching [the existing issues](https://github.com/Yoast/duplicate-post/issues). If you can't find anything matching, please [open a new issue](https://github.com/Yoast/duplicate-post/issues/new/choose).
203+
204+
## Additional resources
205+
206+
* [Yoast developer portal](https://developer.yoast.com/)
207+
* [General GitHub documentation](https://docs.github.com/)
208+
* [GitHub Pull Request documentation](https://docs.github.com/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request)

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,8 @@ phpunit-wp.xml
6767
# Temp files
6868
############
6969
/.tmp
70+
71+
############
72+
# AI
73+
############
74+
/.claude/

0 commit comments

Comments
 (0)