Decentraland Godot Explorer — PR descriptions
There are two kinds of PR here and each has its own shape:
| Kind |
Base ← head |
Title |
Shape |
| Development PR |
main ← feature branch |
conventional commit (feat: / fix: / chore: / refactor:) |
§2 |
| Release Candidate |
release ← release-X.Y.Z |
Release Candidate X.Y.Z |
§3 |
Both follow the same principle (§1). Read it first; it decides what goes in and what stays out.
1. The principle: the author owns the understanding
The team's guideline for anything with your name on it — messages, PRs, docs, issues — is the
AG scale (AG-0 raw AI output … AG-5 you wrote it, AI helped). The target is AG-3 to AG-5:
the author has read every sentence, changed what didn't sound right, and can explain any part of
it if asked. AI can replace knowledge, but it can't replace our understanding.
For PRs this means:
- What and why come first, in plain language. Whoever reads
## What and ## Why — a reviewer,
QA, someone on another team, someone reading the changelog in three months — understands what
changed and why without reading anything else. If they still have to guess, the work was moved
from the author to them.
- Short beats complete. More text is not more clarity. Leave things out rather than pack
them in. Implementation detail goes in a collapsed block, or nowhere.
- No raw AI output. Do not paste a model's summary of the diff. Reviewers read a description
to validate the approach and decide what to trust, not to re-derive the change from a wall of
bullets. Text that restates the diff file by file, narrates the work ("I've implemented…",
"This PR introduces a comprehensive…"), or hedges with filler is a rewrite request.
- Tell the people affected before merging, not after. If the change touches something
another team, service, SDK or workflow depends on (shared UI components, the auth flow, the
comms protocol, the asset pipeline, the mobile-bff contract, the Unity-parity behaviours, CI
workflows), the PR is not the notification. Post a short message in the relevant shared channel
—
#ext-foundation for cross-org — before investing in the full change when possible:
"I'm planning to merge X, it changes Y and may affect Z. Any concerns?" Say in the PR that
you did.
- QA runs the test plan by hand on a phone. Every case must be executable cold by someone
who has never seen the code. The full rules are in
REVIEW.md §4 → Writing test steps QA can
execute; the short form is in §2.3 below.
You (Claude) are drafting on the author's behalf. A draft is AG-2 until the author has read it.
Always print the full body in your reply so the author reads it before it goes out, and never
describe a change you have not read the diff for.
2. Development PR (base main)
2.1 Shape
## What
<What is different after this merges — for the player, the creator, the reviewer or the
build. 1–3 sentences, plain language. Name at most one or two identifiers.>
## Why
<The bug, the request, the measurement, the parity gap. 1–3 sentences. Include what the
change deliberately does NOT do when a reader might assume otherwise.>
Closes #<issue>
## Details
<details>
<summary>Expand</summary>
<Only what a reviewer needs beyond What/Why: root cause, approach and trade-offs,
screenshots/video, per-file notes when non-obvious. Short paragraphs or a tight bullet
list. Omit the whole section when What/Why say it all.>
</details>
## Test plan
<See 2.3. Either "No QA needed — no behavior change" or numbered cases.>
Optional extras, in this order, only when they add clarity: a Heads-up line right after
## Why naming who was told and where ("Heads-up posted in #ext-foundation — touches the shared
ModalShell"); a Changes list (file → what it does) inside Details; a Future plans or
Known gaps line at the end.
2.2 Writing What and Why
## What and ## Why are the deliverable. Everything else is optional. Write them so that
reading only those two sections answers three questions:
- What is different for the player, the creator, the reviewer or the build after this merges.
- Why — the bug, the request, the measurement, the parity gap. One clause is usually enough
("…so screenshots were almost never attached", "…26.5% completion vs 92%").
- What it does not do, when a reader might reasonably assume otherwise ("migrating the
five existing modals is left as a follow-up").
Rules of thumb:
- 1–3 sentences each. If they need more, the PR probably needs splitting, or the extra belongs
in Details. Together they must fit on one screen with the Test plan headline visible.
- Name at most one or two identifiers; describe the rest in words. Code goes in Details.
- No narration of the work, no adjectives about the work ("comprehensive", "robust", "clean").
- State behaviour changes that ride along outside the feature's scope — reviewers and QA need
those most, and they are the easiest thing to lose in a long description.
- If the change is a port from the Unity Foundation Client, say so and name the reference.
Good What/Why (adapted from #2779):
What — Replaces the "Report a Bug" Google Form deep link in Settings with a native in-app
bug report flow that files Intercom tickets through the Decentraland intercom-proxy. The
form pre-fills a screenshot of the game and lands in the same Intercom buckets as the Unity
Explorer client.
Why — The old flow bounced the player out to an external browser and required a Google
sign-in to attach an image, so screenshots were almost never included.
Not a What (rewrite it): "This PR introduces a comprehensive refactor of the bug reporting
system. Key changes include: a new BugReportService class…" — the reader learns the shape of
the diff and nothing about what a player gets or why anyone wanted it.
2.3 Test plan
Follow REVIEW.md §4 exactly. In short:
- No behaviour change (build tooling, CI, metadata, logging level, pure refactor, docs):
write
No QA needed — no behavior change and, if useful, one optional verification line
("CI: Static checks + Clippy green"). Do not invent cases.
- Behaviour change: one block per case. Setup line only when the required state is
non-obvious (specific wearables, second account, guest vs signed-in, a deeplink or flag).
Steps numbered, one user action per line, starting from opening the app, with concrete
on-screen names and values. Expected result observable enough to mark pass/fail without
reading code. A Regression line whenever shared code was touched. Platform only when the
case is iOS- or Android-specific. Device, build download and TestFlight/Firebase install are
assumed — never spend steps on them.
- Each Expected/Regression line is a
- [ ] checkbox so QA can tick it.
- What the author verified themselves (headless test, fmt/clippy, a device run) can go in
Details as ticked
- [x] items. Keep the QA section for what QA still has to do.
2.4 Procedure
- Read the actual diff:
git diff origin/main...HEAD --stat then the files that matter. Do not
write from the branch name or the commit messages alone.
- Find the issue it closes (
gh issue view N) so the why is the real one.
- Decide whether the change can affect another team/service/SDK/workflow. If yes, draft the
Slack heads-up message for the author alongside the PR body and add the Heads-up line.
- Write the body to the scratchpad and print it in full in your reply.
- Open it only when asked, always against
origin (decentraland/godot-explorer), never fork:git push -u origin <branch>
gh pr create -R decentraland/godot-explorer --base main \
--title "<type>: <summary>" --body-file <scratchpad>/pr-body.md
To rewrite an existing PR's body: gh pr edit <N> -R decentraland/godot-explorer --body-file ….
- Before handing over, run the checklist below on your own draft.
2.5 Self-check before it goes out
3. Release Candidate PR (base release)
An RC promotes main (or a cherry-picked subset) into release. Its description is the
release's changelog and QA sheet — QA works from it directly, and it is what people read when
asking "what shipped in 1.13.1?". It is built from the promoted PRs' own descriptions, which
is why §2 matters: a bad dev PR What/Why makes a bad release line.
3.1 Shape
## Release Candidate X.Y.Z
<Lead: what is promoted (main sha or the cherry-picked set), whether it is a clean
promotion or not, what was deliberately excluded, where the version bump lives.
Then one or two sentences on the theme of the release in player terms.>
- **Base:** `release` · **Head:** `release-X.Y.Z`
- <N> commits on top of `release`: <one clause each when cherry-picked; omit for clean promotions>
## What's included
**Features**
- <one line, player/creator terms, ending in (#PR)>
**Fixes**
- <symptom that was fixed, with the number when it was measured, ending in (#PR)>
**Technical** — no QA needed
- <CI, tooling, back-merges, logging, dependency bumps (#PR)>
## Test plan
**Build:** `vX.Y.Z.<build>-<short sha>-prod` · one Android + one iPhone.
<When stacked on a previous RC: "Everything QA'd for X.Y.0 (#prev) carries over unchanged —
the list below is only what this RC adds.">
- [ ] **Version** — login screen and **Settings → About** read `vX.Y.Z.<build>-<sha>-prod`
- [ ] **<Feature name>** — <the one action that proves it on a phone → what QA should see> `#PR`
- [ ] …one line per QA-relevant PR, same order as What's included…
- [ ] **Regression** — enter a few scenes, chat, change a wearable, play an emote from the wheel: no crashes
**Known gap:** <anything shipped without device validation, with why>
When one promoted PR is large enough to need its own paragraph (an FTUE change, a Sentry
overhaul), What's included may use a bold **Name — #PR (closes #issue)** heading with a short
paragraph under it instead of a single line — see #2797. Keep that for one or two items, not all.
3.2 Procedure
- Establish the range. From an up-to-date checkout:
git fetch origin main release
git log --oneline origin/release..origin/main # what main has that release doesn't
git log --oneline origin/release..origin/release-X.Y.Z # what the RC branch actually carries
If the two differ, the RC is a cherry-pick: list what is in and, in the lead, what was left
out and why. Commit subjects carry the PR number as (#NNNN); extract them:git log --format=%s origin/release..origin/release-X.Y.Z | grep -o '#[0-9]\+' | sort -u
- Read every promoted PR, not just its title:
gh pr view <N> -R decentraland/godot-explorer --json title,body,closingIssuesReferences
From each take: ## What and ## Why (→ one changelog line), the behaviour changes that ride
along (→ often their own line, or a Known gap), and the one or two test-plan cases that prove
the change on a phone (→ one Test plan line). Do not copy a PR's full test plan into the RC.
- Classify each PR: Feature (new capability a player or creator sees), Fix (a symptom went
away — quote the measurement when the PR has one), Technical (nothing QA can observe). A
chore: that changes runtime behaviour is not Technical.
- Confirm the version.
lib/Cargo.toml and godot/export_presets.cfg carry it; say in the
lead which PR bumped it. The Test plan's Version line is not optional — it is how QA proves
they are on the right build.
- Carry-over. For a patch RC on top of a previous one (1.13.1 after 1.13.0), reference the
previous RC and list only the additions, both in What's included and in the Test plan.
- Write the body to the scratchpad, print it in full, then when asked:
gh pr create -R decentraland/godot-explorer --base release --head release-X.Y.Z \
--title "Release Candidate X.Y.Z" --body-file <scratchpad>/rc-body.md
3.3 Writing the changelog lines
- Player and creator terms first, the mechanism only when it is the news:
"Nearby players rendered as discrete frames, and bounced after landing (#2778)" — not
"Interpolate remote avatar transforms and clamp ground snap (#2778)".
- One line per PR. Two PRs that ship one feature share a line with both numbers.
- Numbers only when they came from the PR and change what the reader thinks
("26.5% completion vs 92%, ~17 logins/day").
- Under Technical the reader should be able to skip the block entirely; nothing there needs a
test case.
- Test plan lines pair a bold name with one concrete action and the visible outcome, and end
with the PR number in backticks so QA can jump to the source when a case fails.
3.4 Self-check
1---2name: pr-description3description: Decentraland Godot Explorer — PR descriptions4---56# Decentraland Godot Explorer — PR descriptions78There are two kinds of PR here and each has its own shape:910| Kind | Base ← head | Title | Shape |11|---|---|---|---|12| **Development PR** | `main` ← feature branch | conventional commit (`feat:` / `fix:` / `chore:` / `refactor:`) | §2 |13| **Release Candidate** | `release` ← `release-X.Y.Z` | `Release Candidate X.Y.Z` | §3 |1415Both follow the same principle (§1). Read it first; it decides what goes in and what stays out.1617## 1. The principle: the author owns the understanding1819The team's guideline for anything with your name on it — messages, PRs, docs, issues — is the20**AG scale** (AG-0 raw AI output … AG-5 you wrote it, AI helped). The target is **AG-3 to AG-5**:21the author has read every sentence, changed what didn't sound right, and can explain any part of22it if asked. *AI can replace knowledge, but it can't replace our understanding.*2324For PRs this means:2526- **What and why come first, in plain language.** Whoever reads `## What` and `## Why` — a reviewer,27 QA, someone on another team, someone reading the changelog in three months — understands what28 changed and why without reading anything else. If they still have to guess, the work was moved29 from the author to them.30- **Short beats complete.** More text is not more clarity. Leave things out rather than pack31 them in. Implementation detail goes in a collapsed block, or nowhere.32- **No raw AI output.** Do not paste a model's summary of the diff. Reviewers read a description33 to *validate the approach and decide what to trust*, not to re-derive the change from a wall of34 bullets. Text that restates the diff file by file, narrates the work ("I've implemented…",35 "This PR introduces a comprehensive…"), or hedges with filler is a rewrite request.36- **Tell the people affected before merging, not after.** If the change touches something37 another team, service, SDK or workflow depends on (shared UI components, the auth flow, the38 comms protocol, the asset pipeline, the mobile-bff contract, the Unity-parity behaviours, CI39 workflows), the PR is not the notification. Post a short message in the relevant shared channel40 — `#ext-foundation` for cross-org — *before* investing in the full change when possible:41 *"I'm planning to merge X, it changes Y and may affect Z. Any concerns?"* Say in the PR that42 you did.43- **QA runs the test plan by hand on a phone.** Every case must be executable cold by someone44 who has never seen the code. The full rules are in `REVIEW.md` §4 → *Writing test steps QA can45 execute*; the short form is in §2.3 below.4647You (Claude) are drafting on the author's behalf. A draft is AG-2 until the author has read it.48**Always print the full body in your reply** so the author reads it before it goes out, and never49describe a change you have not read the diff for.5051## 2. Development PR (base `main`)5253### 2.1 Shape5455```markdown56## What57<What is different after this merges — for the player, the creator, the reviewer or the58 build. 1–3 sentences, plain language. Name at most one or two identifiers.>5960## Why61<The bug, the request, the measurement, the parity gap. 1–3 sentences. Include what the62 change deliberately does NOT do when a reader might assume otherwise.>6364Closes #<issue>6566## Details67<details>68<summary>Expand</summary>6970<Only what a reviewer needs beyond What/Why: root cause, approach and trade-offs,71 screenshots/video, per-file notes when non-obvious. Short paragraphs or a tight bullet72 list. Omit the whole section when What/Why say it all.>7374</details>7576## Test plan7778<See 2.3. Either "No QA needed — no behavior change" or numbered cases.>79```8081Optional extras, in this order, only when they add clarity: a **Heads-up** line right after82`## Why` naming who was told and where (*"Heads-up posted in #ext-foundation — touches the shared83`ModalShell`"*); a **Changes** list (file → what it does) inside Details; a **Future plans** or84**Known gaps** line at the end.8586### 2.2 Writing What and Why8788`## What` and `## Why` are the deliverable. Everything else is optional. Write them so that89reading *only* those two sections answers three questions:90911. **What** is different for the player, the creator, the reviewer or the build after this merges.922. **Why** — the bug, the request, the measurement, the parity gap. One clause is usually enough93 (*"…so screenshots were almost never attached"*, *"…26.5% completion vs 92%"*).943. **What it does not do**, when a reader might reasonably assume otherwise (*"migrating the95 five existing modals is left as a follow-up"*).9697Rules of thumb:9899- 1–3 sentences each. If they need more, the PR probably needs splitting, or the extra belongs100 in Details. Together they must fit on one screen with the Test plan headline visible.101- Name at most one or two identifiers; describe the rest in words. Code goes in Details.102- No narration of the work, no adjectives about the work ("comprehensive", "robust", "clean").103- State behaviour changes that ride along outside the feature's scope — reviewers and QA need104 those most, and they are the easiest thing to lose in a long description.105- If the change is a port from the Unity Foundation Client, say so and name the reference.106107Good What/Why (adapted from #2779):108109> **What** — Replaces the "Report a Bug" Google Form deep link in Settings with a native in-app110> bug report flow that files Intercom tickets through the Decentraland `intercom-proxy`. The111> form pre-fills a screenshot of the game and lands in the same Intercom buckets as the Unity112> Explorer client.113>114> **Why** — The old flow bounced the player out to an external browser and required a Google115> sign-in to attach an image, so screenshots were almost never included.116117Not a What (rewrite it): *"This PR introduces a comprehensive refactor of the bug reporting118system. Key changes include: a new `BugReportService` class…"* — the reader learns the shape of119the diff and nothing about what a player gets or why anyone wanted it.120121### 2.3 Test plan122123Follow `REVIEW.md` §4 exactly. In short:124125- **No behaviour change** (build tooling, CI, metadata, logging level, pure refactor, docs):126 write `No QA needed — no behavior change` and, if useful, one optional verification line127 (*"CI: Static checks + Clippy green"*). Do not invent cases.128- **Behaviour change**: one block per case. **Setup** line only when the required state is129 non-obvious (specific wearables, second account, guest vs signed-in, a deeplink or flag).130 **Steps** numbered, one user action per line, **starting from opening the app**, with concrete131 on-screen names and values. **Expected** result observable enough to mark pass/fail without132 reading code. A **Regression** line whenever shared code was touched. Platform only when the133 case is iOS- or Android-specific. Device, build download and TestFlight/Firebase install are134 assumed — never spend steps on them.135- Each Expected/Regression line is a `- [ ]` checkbox so QA can tick it.136- What the author verified themselves (headless test, fmt/clippy, a device run) can go in137 Details as ticked `- [x]` items. Keep the QA section for what QA still has to do.138139### 2.4 Procedure1401411. Read the actual diff: `git diff origin/main...HEAD --stat` then the files that matter. Do not142 write from the branch name or the commit messages alone.1432. Find the issue it closes (`gh issue view N`) so the *why* is the real one.1443. Decide whether the change can affect another team/service/SDK/workflow. If yes, draft the145 Slack heads-up message for the author alongside the PR body and add the Heads-up line.1464. Write the body to the scratchpad and print it in full in your reply.1475. Open it only when asked, always against `origin` (`decentraland/godot-explorer`), never `fork`:148 ```bash149 git push -u origin <branch>150 gh pr create -R decentraland/godot-explorer --base main \151 --title "<type>: <summary>" --body-file <scratchpad>/pr-body.md152 ```153 To rewrite an existing PR's body: `gh pr edit <N> -R decentraland/godot-explorer --body-file …`.1546. Before handing over, run the checklist below on your own draft.155156### 2.5 Self-check before it goes out157158- [ ] Reading only `## What` and `## Why`, a teammate on another team knows what changed and why.159- [ ] Nothing in the description is a restatement of the diff or of the commit list.160- [ ] Every claim is something you verified in the code or the issue — no guessed behaviour.161- [ ] Ride-along behaviour changes outside the feature are stated, not buried.162- [ ] Test plan cases start from opening the app and end in an observable result.163- [ ] Affected teams are named and were (or will be) told before merge.164- [ ] Visible text outside `<details>` fits on one screen.165166## 3. Release Candidate PR (base `release`)167168An RC promotes `main` (or a cherry-picked subset) into `release`. Its description is the169release's changelog and QA sheet — QA works from it directly, and it is what people read when170asking "what shipped in 1.13.1?". It is built **from the promoted PRs' own descriptions**, which171is why §2 matters: a bad dev PR What/Why makes a bad release line.172173### 3.1 Shape174175```markdown176## Release Candidate X.Y.Z177178<Lead: what is promoted (main sha or the cherry-picked set), whether it is a clean179 promotion or not, what was deliberately excluded, where the version bump lives.180 Then one or two sentences on the theme of the release in player terms.>181182- **Base:** `release` · **Head:** `release-X.Y.Z`183- <N> commits on top of `release`: <one clause each when cherry-picked; omit for clean promotions>184185## What's included186187**Features**188- <one line, player/creator terms, ending in (#PR)>189190**Fixes**191- <symptom that was fixed, with the number when it was measured, ending in (#PR)>192193**Technical** — no QA needed194- <CI, tooling, back-merges, logging, dependency bumps (#PR)>195196## Test plan197198**Build:** `vX.Y.Z.<build>-<short sha>-prod` · one Android + one iPhone.199200<When stacked on a previous RC: "Everything QA'd for X.Y.0 (#prev) carries over unchanged —201 the list below is only what this RC adds.">202203- [ ] **Version** — login screen and **Settings → About** read `vX.Y.Z.<build>-<sha>-prod`204- [ ] **<Feature name>** — <the one action that proves it on a phone → what QA should see> `#PR`205- [ ] …one line per QA-relevant PR, same order as What's included…206- [ ] **Regression** — enter a few scenes, chat, change a wearable, play an emote from the wheel: no crashes207208**Known gap:** <anything shipped without device validation, with why>209```210211When one promoted PR is large enough to need its own paragraph (an FTUE change, a Sentry212overhaul), *What's included* may use a bold `**Name — #PR (closes #issue)**` heading with a short213paragraph under it instead of a single line — see #2797. Keep that for one or two items, not all.214215### 3.2 Procedure2162171. **Establish the range.** From an up-to-date checkout:218 ```bash219 git fetch origin main release220 git log --oneline origin/release..origin/main # what main has that release doesn't221 git log --oneline origin/release..origin/release-X.Y.Z # what the RC branch actually carries222 ```223 If the two differ, the RC is a cherry-pick: list what is in and, in the lead, what was left224 out and why. Commit subjects carry the PR number as `(#NNNN)`; extract them:225 ```bash226 git log --format=%s origin/release..origin/release-X.Y.Z | grep -o '#[0-9]\+' | sort -u227 ```2282. **Read every promoted PR**, not just its title:229 ```bash230 gh pr view <N> -R decentraland/godot-explorer --json title,body,closingIssuesReferences231 ```232 From each take: `## What` and `## Why` (→ one changelog line), the behaviour changes that ride233 along (→ often their own line, or a Known gap), and the one or two test-plan cases that prove234 the change on a phone (→ one Test plan line). Do not copy a PR's full test plan into the RC.2353. **Classify** each PR: Feature (new capability a player or creator sees), Fix (a symptom went236 away — quote the measurement when the PR has one), Technical (nothing QA can observe). A237 `chore:` that changes runtime behaviour is not Technical.2384. **Confirm the version.** `lib/Cargo.toml` and `godot/export_presets.cfg` carry it; say in the239 lead which PR bumped it. The Test plan's **Version** line is not optional — it is how QA proves240 they are on the right build.2415. **Carry-over.** For a patch RC on top of a previous one (1.13.1 after 1.13.0), reference the242 previous RC and list only the additions, both in *What's included* and in the Test plan.2436. Write the body to the scratchpad, print it in full, then when asked:244 ```bash245 gh pr create -R decentraland/godot-explorer --base release --head release-X.Y.Z \246 --title "Release Candidate X.Y.Z" --body-file <scratchpad>/rc-body.md247 ```248249### 3.3 Writing the changelog lines250251- Player and creator terms first, the mechanism only when it is the news:252 *"Nearby players rendered as discrete frames, and bounced after landing (#2778)"* — not253 *"Interpolate remote avatar transforms and clamp ground snap (#2778)"*.254- One line per PR. Two PRs that ship one feature share a line with both numbers.255- Numbers only when they came from the PR and change what the reader thinks256 (*"26.5% completion vs 92%, ~17 logins/day"*).257- Under *Technical* the reader should be able to skip the block entirely; nothing there needs a258 test case.259- Test plan lines pair a **bold name** with one concrete action and the visible outcome, and end260 with the PR number in backticks so QA can jump to the source when a case fails.261262### 3.4 Self-check263264- [ ] Every commit in `origin/release..head` is accounted for by a line or explicitly excluded.265- [ ] Each line was written from the PR's body, not from its title alone.266- [ ] Every Feature and Fix line has a Test plan line; every Technical line has none.267- [ ] Version line present and matching `lib/Cargo.toml`.268- [ ] Anything shipping without a device run is under **Known gap**, not silently omitted.269- [ ] The lead says clean promotion or cherry-pick, and what was left behind.