chore: enforce PR→issue linking discipline (#1263)

PRs that omit Fixes #N break the release-time reverse lookup
(issue → closing PR → merge sha → first containing tag), since the
auto-link only fires on the explicit closing keywords. We've been doing
this by hand on recent fixes; codify it so future PRs don't drift.

- Add .github/pull_request_template.md with a Fixes # placeholder so
  the link surface is in front of the author by default.
- Add a corresponding bullet to the Bug follow-up workflow in the root
  AGENTS.md so the discipline lives next to the methodology that
  produces issue-linked work.
This commit is contained in:
PerishFire 2026-05-11 17:24:24 +08:00 committed by GitHub
parent a797e079b1
commit f2db5a749c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 11 additions and 0 deletions

10
.github/pull_request_template.md vendored Normal file
View file

@ -0,0 +1,10 @@
<!-- Required for issue/PR auto-link. Use Fixes / Closes / Resolves so the
linked issue closes on merge and release-time reverse lookup works.
Delete this whole block if the PR genuinely doesn't close any issue. -->
Fixes #
## Summary
-
## Validation
-

View file

@ -97,6 +97,7 @@ The following is a working playbook for routine bug follow-ups, distilled from r
- **Hold the spec's scope.** Defects discovered outside the bug's described boundary belong in a follow-up — their own red spec, their own PR — not in this fix. List them in the PR body's "Adjacent issues" section with the rationale and move on. - **Hold the spec's scope.** Defects discovered outside the bug's described boundary belong in a follow-up — their own red spec, their own PR — not in this fix. List them in the PR body's "Adjacent issues" section with the rationale and move on.
- **Let the fix read as an invariant.** Prefer a named helper whose docblock describes what must hold over a bolt-on `if` guard with apologetic history-comments. The call site should read as intent. - **Let the fix read as an invariant.** Prefer a named helper whose docblock describes what must hold over a bolt-on `if` guard with apologetic history-comments. The call site should read as intent.
- **Diff against the baseline.** When neighboring suites have pre-existing failures, stash or check out upstream before claiming "no new failures." - **Diff against the baseline.** When neighboring suites have pre-existing failures, stash or check out upstream before claiming "no new failures."
- **Link the issue from the PR body.** Use `Fixes #N` / `Closes #N` / `Resolves #N` so the issue auto-closes on merge and the release-time reverse lookup (`gh issue view N --json closedByPullRequestsReferences` → `git tag --contains <merge sha>`) actually has a chain to follow. The repo's PR template prompts for this; deleting the prompt is fine when the PR genuinely closes nothing.
- **Stage human verification for visible bugs.** When the symptom needs an eye to confirm — UI, platform-native behavior, animations, race conditions a unit test can't see — green specs alone aren't acceptance. Stand up a buggy-vs-fix comparison the reviewer can drive themselves (typical shape: two namespaced runtimes, one on `main`, one on the fix branch), and seed any required data only through production HTTP APIs; source-level test backdoors invalidate the verification because they prove a fake flow rather than the real one. - **Stage human verification for visible bugs.** When the symptom needs an eye to confirm — UI, platform-native behavior, animations, race conditions a unit test can't see — green specs alone aren't acceptance. Stand up a buggy-vs-fix comparison the reviewer can drive themselves (typical shape: two namespaced runtimes, one on `main`, one on the fix branch), and seed any required data only through production HTTP APIs; source-level test backdoors invalidate the verification because they prove a fake flow rather than the real one.
For a worked example of one full loop (red e2e spec → fix → green), see `e2e/tests/dialog/stop-reconciles-message.test.ts` (issue #135). For a worked example of one full loop (red e2e spec → fix → green), see `e2e/tests/dialog/stop-reconciles-message.test.ts` (issue #135).