woocommerce-paypal-payments/.claude/skills/describe-pr/guideline.md
2026-07-24 14:00:32 +02:00

10 KiB

PR description guide

Reference for drafting in step 3. The goal is to introduce a reviewer to the problem and the reasoning - never to walk through the diff.

Concision comes first

Default to the shortest draft that does the job. A reviewer skims; every extra sentence costs their attention. The most common failure is a first draft that is too long, so bias hard toward less.

Start from the minimum set of sections and add one only when it tells the reviewer something they cannot get from the code or the title. When unsure whether a section earns its place, leave it out.

The example at the bottom is near the upper bound of acceptable length, not a target to fill.

Title

Draft or refine the PR title as well as the body. The title is a clear, plain description of the change for a reviewer - what this PR does. It is not the changelog line and may be more technical or specific than the changelog.

The title must always end with the Jira number in parentheses: the Jira ID with its letter prefix removed. PCP-6288 becomes (6288). Get the number from the Jira ID argument or the branch name ([A-Z]+-\d+). If no Jira ID can be found anywhere, ask the user for the number rather than guessing or dropping the suffix.

Example: Restore the "Proceed to PayPal" button label on block checkout (6288).

Changelog line

The first line of the description, above the # Description title, is always a changelog line. It is mandatory. Exact format:

*Changelog:* `Type - the entry #PR`

The entry inside the backticks follows the project's readme.txt changelog format, Type - short summary #PR:

  • Type is one of New (a brand-new capability), Enhancement (improves existing behavior), or Fix (corrects a defect). Derive it from the PR: a defect PR (🐛 The problem) is Fix; a new-spec PR (🎯 The goal) is New or Enhancement.
  • Summary is very short and written for a regular plugin user, not a developer. Describe the observable outcome or benefit, never the mechanism, class, method, or file. Plain English.
  • #PR is the resolved PR number. Append extra PRs as , #1234. Append (author @handle) only for an external contributor.
  • Use a plain hyphen -.

No user-facing change? For a PR that touches only internal tooling, CI, tests, build, or docs - nothing a plugin user would notice and nothing that lands in readme.txt - do not invent an entry. Write *Changelog:* (none - <short reason>) instead, e.g. *Changelog:* (none - internal Claude tooling). Never dress up an internal change as a user-facing one just to fill the line.

Entry
Good (user-facing) Fix - Google Pay now correctly updates WooCommerce shipping address #3798
Good Enhancement - Improved error message for reCAPTCHA v2 challenge failures #4342
Bad (developer voice) Fix - AddressFactory::from_wc_order billing address #4279
Bad (describes the code, not the user impact) Fix - Do not increment step directly #4086

Heading catalog

Every section is optional, and omitting is the default. This is a menu, not a form to fill in. Include a heading only when it adds something a reviewer cannot get elsewhere. Most PRs land on two or three sections; the full set is rare and reserved for a genuinely subtle change.

*Changelog:* `Type - the entry #PR`   mandatory first line, above the title
# Description
## 🐛 The problem      a defect: the symptom in plain terms, not the stack trace
## 🎯 The goal         no defect: a spec that changed or scope not covered until now
## 💡 Why it happens   the root cause that explains the symptom or gap
## ✅ The fix          what the change does conceptually and where it lives; never a code walkthrough
## 🧪 Tests            what the change now covers and what it deliberately does not; reasoning, not a test-by-test list
## 🔍 What to review   fragile assumptions, blast radius, what tests can't catch; be honest about weak points
## 🧪 How to verify    numbered steps that exercise the change
## 🖼️ Visual proof     user-supplied before/after media; only when the user opts in (see below)

The problem and The goal are alternatives - pick one. Use 🐛 The problem when something was broken; use 🎯 The goal when nothing was defective and the change implements a new or revised spec.

Visual proof is never drafted automatically. Offer it in step 4, and only when the user opts in insert a placeholder block they fill in manually (see below).

Tests and How to verify are distinct: Tests explains what coverage the change adds (and what it leaves uncovered, and why); How to verify lists the concrete steps a reviewer runs.

The catalog is framed for bug fixes. For other PR types (test additions, refactors, features, chores), keep the headings that carry meaning and drop the rest. A test-addition PR, for example, often needs only # Description, 🔍 What to review, and 🧪 How to verify - forcing "Why it happens" / "The fix" onto it produces filler.

Tone and content rules

  • Reasoning over mechanics. Explain the problem and the why; let the diff explain the how.
  • Do not summarize the changes file-by-file. If a reviewer wants the mechanics, they read the code.
  • Be candid in "What to review" - surface the one fragile assumption, the load-order dependency, the thing not covered by tests. This is the most valuable section.
  • Keep the user's voice and the emoji headings. Drop headings that would only contain filler.
  • The PR description is public and must stand on its own: Never reference a Jira issue ID, Slack threads, developer names, or other sensitive details that should not be public.
  • Write evergreen. Describe the end state the reviewer sees when this merges, not transient or in-progress status ("currently failing", "WIP", "will be green once X"). If something is genuinely temporary, it does not belong in the description.
  • "How to verify" lists actions, not narration. Give the reviewer steps that exercise this change, using the project's real documented commands (check CLAUDE.md / AGENTS.md / package.json; never invent an invocation). Skip preconditions that already hold and environment boilerplate. Do not state expected pass/fail results - a test is expected to pass; saying so adds nothing.

Writing style

  • Paragraphs: 1-3 sentences max. Break at natural pauses.
  • Language: plain English. No filler phrases ("it is worth noting", "in order to").
  • Inline code: backticks for file paths, class names, method names, hook names, config keys, JS globals.
  • Bold: one use per section max, for the single most important term or outcome.
  • Lists: prefer bullets to prose when there are 3+ parallel items.
  • Avoid AI lingo like "awkward", "scrutinize", "delve".
  • No em-dashes, direct and active tone.
  • No soft wrapping. Write each paragraph and each bullet as one continuous line, however long. Insert a line break only for a functional reason: a new paragraph, or a new list item. GitHub renders manual breaks as hard line breaks in the description.

Visual proof

For UI changes a before/after helps, but the skill cannot capture media. In step 4, after the draft, ask the user whether to include a Visual proof section. Only if they opt in, append this placeholder for them to fill in manually after the PR is updated - never fabricate or attach media:

## 🖼️ Visual proof

| Before | After |
|---|---|
| _screenshot or video_ | _screenshot or video_ |

Reference example

A subtle fix that justifies four body sections - this is roughly the longest a description should get, not the norm. Note the mandatory changelog first line, a reviewer-focused title ending in the Jira number, and how the body explains the problem and reasoning, names the single fragile assumption under "What to review", and never narrates the code. A routine PR would trim this to one or two sections:

Title: Restore the "Proceed to PayPal" button label on block checkout (6288)

*Changelog:* `Fix - "Proceed to PayPal" button label restored on block checkout #4441`

# Description

## 🐛 The problem

On block checkout the PayPal place-order button is expected to read **"Proceed to PayPal"**. However, when a subscription product is in the cart, the button instead reads "Sign up now".

## 💡 Why it happens

WooCommerce Subscriptions registers a `placeOrderButtonLabel` checkout filter that *unconditionally rewrites the label* to its own text. PayPal only supplied its label through `registerPaymentMethod`, with no competing filter - so WCS always won.

## ✅ The fix

PayPal now registers its own `placeOrderButtonLabel` filter that restores "Proceed to PayPal" **only when PayPal is the active gateway**, and passes the value through untouched otherwise. One file: `modules/ppcp-blocks/resources/js/checkout-block.js`.

## 🔍 What to review

- **Load-order dependency.** Both filters run; the last one to register wins, and WCS's overwrites whatever it receives. This fix relies on PayPal's filter registering **after** WCS's, which holds because `checkout-block.js` enqueues later. If that ordering ever changes, the bug returns silently.
- **Not unit-tested by design.** Correctness depends on filter execution order and the runtime presence of `window.wp.data`, neither of which a unit test can exercise. Verified manually instead (steps below).

## 🧪 How to verify

1. With **Save PayPal and Venmo** enabled, add a subscription product to the cart and open **Block Checkout**.
2. Select the PayPal gateway → button reads **"Proceed to PayPal"** (not "Sign up now").
3. Select a different gateway → its normal label is unaffected.