# AjaxPress — Release Guideline

How to release a new version of AjaxPress to wordpress.org. Follow the checklist top-to-bottom; every box is a verifiable action.

## Project release model

- **`develop`** — active development branch.
- **`master`** — release branch. PRs merge here from `develop` once tested.
- **Tags** — created on `master` after merge. Pushing any tag triggers `.github/workflows/deploy-org.yml`, which uses `10up/action-wordpress-plugin-deploy@stable` to publish the tagged commit to WordPress.org SVN under the slug `ajaxpress`.
- **`trunk`** branch — separate from version releases. Pushing to `trunk` runs `update-assets.yml` and updates only the `readme.txt` and `assets/` on wordpress.org SVN (not a full release).
- **WPCS** — `.github/workflows/wpcs.yml` runs PHP CodeSniffer (^3) on every push to `develop`/`master` and on PRs targeting them. Must pass before merge.
- **Never** push to wordpress.org SVN directly. Always go through a tag push.

## Pre-flight artifact fixes

- [ ] **A1.** All version refs aligned to `<new-version>`:
  - [ ] `ajaxpress.php` plugin header `Version:`
  - [ ] `ajaxpress.php` `AJAXPRESS_VERSION` constant
  - [ ] `package.json` `version`
  - [ ] `readme.txt` `Stable tag:`
- [ ] **A2.** `changelog.txt` has a `= <new-version> =` section listing every user-visible change.
- [ ] **A3.** `readme.txt` has the same `= <new-version> =` section under `== Changelog ==`.
- [ ] **A4.** `readme.txt` has an `= <new-version> =` entry under `== Upgrade Notice ==` (one short sentence on why to upgrade).
- [ ] **A5.** Spell-check / grammar-check changelog and upgrade notice — these are public on the wordpress.org plugin page.
- [ ] **A6.** Bump `Tested up to` in `readme.txt` if WordPress has a newer stable since the last release.

## Build & code health

- [ ] **B1.** Clean rebuild: `npx vite build --config ./src/frontend/vite.config.js`. Bundle outputs to `public/js/ajaxpress.min.js` and `public/css/ajaxpress.min.css`.
- [ ] **B2.** Sanity grep on the bundle for the headline change of this release (e.g., a new function name or event name) — confirms the source change is actually in the minified output.
- [ ] **B3.** `git status` is clean except expected build outputs (`public/js/ajaxpress.min.js{,.map}`, `public/css/ajaxpress.min.css`).
- [ ] **B4.** Run WPCS locally: `composer install` then `vendor/bin/phpcs .` exits clean. (Or accept the upcoming CI run.)
- [ ] **B5.** No accidentally-committed credentials, debug code, or `.env` files: `git diff master..develop` reads cleanly.

## End-to-end test (local Local-by-Flywheel site)

Run on a fresh Chrome tab against the local dev site (typical: `http://localhost:<port>/`). Yoast SEO (or another schema plugin) should be active so JSON-LD/canonical/OG sync is exercised.

- [ ] **C1.** Cold-load a post — parent `<head>` has exactly 1 `script[type="application/ld+json"]` matching the URL.
- [ ] **C2.** AJAX-navigate through 4+ pages including a revisit. JSON-LD count stays at 1, content matches each URL.
- [ ] **C3.** `link[rel="canonical"]`, `meta[property="og:url"]`, `meta[name="twitter:title"]` all update per page, exactly one of each.
- [ ] **C4.** Iframe doc has `meta[name="robots"]` containing `noindex,nofollow` with the `data-ajaxpress-iframe-only` marker.
- [ ] **C5.** Parent `<head>` does NOT contain `noindex` (the marker prevents it from being synced over from the iframe).
- [ ] **C6.** `document.addEventListener('ajaxpress:ready', …)` fires once per nav with `{ url, title }`. No duplicates.
- [ ] **C7.** Browser back/forward through 3+ pages — URL, title, head metadata all track correctly.
- [ ] **C8.** Console: no errors, no warnings throughout all tests.
- [ ] **C9.** Every fix from previous releases still works (don't regress shipped fixes — check the last 2–3 changelog entries' bullets).
- [ ] **C10.** Disable AjaxPress and confirm the page is unchanged from how it would render without the plugin (sanity that AjaxPress isn't corrupting downstream output).

## Pre-tag CI gate

- [ ] **D1.** Push develop: `git push origin develop`.
- [ ] **D2.** `Testing WPCS 3` workflow on develop is green.
- [ ] **D3.** Open PR `develop → master`. CI runs again on the PR. Must be green.
- [ ] **D4.** Self-review the PR diff. Each commit message accurately describes its change. No surprises.
- [ ] **D5.** Merge PR. Note the final `master` SHA.

## Tag and release

- [ ] **E1.** Pull `master` locally: `git fetch origin && git checkout master && git pull origin master`.
- [ ] **E2.** Verify the merge commit contains the release work: re-run B2's sanity grep against `master`.
- [ ] **E3.** Annotated tag from `master`: `git tag -a v<new-version> -m "Release <new-version> — <one-line summary>"`.
- [ ] **E4.** Push tag: `git push origin v<new-version>`. (Never push the tag before the merge — the action deploys whatever is at the tagged commit.)
- [ ] **E5.** Watch `Deploy to WordPress.org` action complete successfully. Failures here usually mean SVN credentials or `readme.txt` syntax issues.

## Post-release verification

- [ ] **F1.** `https://wordpress.org/plugins/ajaxpress/` shows `<new-version>` (allow a few minutes for the wordpress.org cache).
- [ ] **F2.** `https://plugins.svn.wordpress.org/ajaxpress/tags/<new-version>/` exists.
- [ ] **F3.** Plugin page renders the new changelog and upgrade notice correctly.
- [ ] **F4.** If the release fixes a SEO/schema issue: run Google's Rich Results Test against a real production page running `<new-version>`.
- [ ] **F5.** Optional: create a GitHub Release on the tag with the same changelog bullets for users who watch the repo.

## Rollback / hotfix plan

If post-release verification fails:

- [ ] **G1.** Diagnose the regression — do not skip this. Bypassing leads to repeat releases.
- [ ] **G2.** Fix forward with a `<new-version+1>` patch on `develop`. Re-run this checklist from B onward.
- [ ] **G3.** **Do not modify SVN directly.** Always release via tag push, even for hotfixes.
- [ ] **G4.** If a critical regression is live and the hotfix isn't ready in minutes: add a short "known issue" line to `readme.txt`'s Upgrade Notice and push to `trunk` (which runs `update-assets.yml` to update SVN's readme without changing code).
