# PR body, commit message, and branch naming

Templates used by Phase E when opening migration PRs against consumer repos.

---

## PR body template

````markdown
## Summary

Auto-migrates `<repo>` to be compatible with an upcoming `application_sdk` change.

**Target SDK release:** `<spec.target_release>` (e.g. `v4.0.0`, or `application_sdk#1234`)

**SDK change context:** <spec.Context verbatim>

---

## Fixes applied

| Check | File:Line | Patched | Mode |
|---|---|---|---|
| R1 — <check name> | `clients/foo.py:42` | yes | deterministic (`fix:` block) |
| R2 — <check name> | `handlers/bar.py:17` | yes | LLM-rewrite (no `fix:` block) |
| R3 — <check name> | `clients/foo.py:108` | **no — TODO** | LLM abstained |

**Imports added:**
- `clients/foo.py`: `from application_sdk.errors import AppError`

---

## Verification

- `python -m py_compile` on changed files: **PASS** (or **FAIL** — see note)
- `ruff check` on changed files: **PASS** (or **WARN** with verbatim output below, or **not installed**)

```
<ruff output verbatim if warnings were produced>
```

> Consumer test suite was **not** run by the auto-migration. Leaving it to CI checks in
> this app's repo to do appropriate testing of the changes.

---

## TODOs for the human reviewer

These hits were detected by the audit but could not be auto-patched. Please address them
before merging:

- [ ] `clients/foo.py:108` — R3 — Broad `except Exception` near SDK call.
  LLM abstained because the try-body context was ambiguous.
  **Recommended action (from spec):** <recommendation verbatim>

- [ ] `app/init.py:55` — R2 — Message-string inspection.
  LLM diff rejected (scope creep: 11 lines).
  **Recommended action (from spec):** switch to isinstance check against the new typed error class

---

## `needs_review` findings (not auto-patched — require human judgment)

These hits were classified `needs_review` during the audit. They are not guaranteed to break,
but warrant a look before this PR merges:

- `tests/test_conn.py:24` — R3 — `except Exception` [needs_review — try body context ambiguous]

---

## Original audit findings for this repo

<verbatim copy of the per-repo section from Phase C of the audit report — includes the SDK
pin, anchor files, and every confirmed/needs_review/false_positive hit with triage reasons
and per-hit recommendations>

---

## How this PR was generated

Generated by `/audit-consumers --raise-prs` from the `application-sdk` repository.
Re-run the same skill against the latest spec to regenerate (close this PR first).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
````

**Notes on the template:**
- The `🤖 Generated with [Claude Code]` footer is **PR body only** — mirrors the `upgrade-v3`
  precedent. It MUST NOT appear in the commit message.
- Omit the "TODOs for the human reviewer" section if all hits were patched successfully.
- Omit the "`needs_review` findings" section if there were none.
- The `## Verification` `py_compile FAIL` note should not be reachable in practice (Phase E5
  rolls back and does not open a PR if `py_compile` fails) — include this note as a guard
  for the reviewer in case of a race condition.
- Verbatim ruff output goes between the ``` fences only when non-empty.

---

## Commit message template

Format per `docs/standards/commits.md` (Conventional Commits):

```
<type>(<scope>): <imperative description, lowercase, ≤50 chars, no trailing period>

- R1: <one-line description of the change> (N sites)
- R2: <one-line description> (N sites)
- R3: N sites flagged as TODOs — see PR body
```

**Field rules:**

| Field | Rule |
|---|---|
| `type` | Default `refactor` (type/import changes). Use `fix` only for genuine bug-level breakage (impact `definite-break`). Phase E never uses `feat`. |
| `scope` | The spec slug, trimmed to ≤20 chars. Example: `sql-error-types`. |
| Description | Imperative mood, lowercase, ≤50 chars, no trailing period. |
| Body | One bullet per check that produced at least one applied fix or unresolved hit. ≤72-char wrap. |
| Co-Authored-By | **MUST NOT be included.** Hard rule from `CLAUDE.md`. |
| Customer/tenant/run IDs | **MUST NOT appear anywhere.** Hard rule from `CLAUDE.md`. |

**Do not let the LLM compose the commit message.** Too much variance; easy to accidentally
include forbidden tokens. Use this template mechanically, substituting values from the Phase E
run state.

**Single-check example:**
```
refactor(sql-error-types): catch AppError in SQL client paths

- R1: except ClientError/ValueError → except AppError (3 sites)
  Adds: from application_sdk.errors import AppError
```

**Multi-check example:**
```
refactor(sql-error-types): migrate exception handling for new SDK errors

- R1: except ClientError/ValueError → except AppError (3 sites; 1 TODO — LLM abstained)
- R2: SQL_CLIENT_AUTH_ERROR string match replaced with isinstance (1 site)
- R3: 2 broad-except sites flagged as needs_review — see PR body
```

---

## Branch naming

```
sdk-audit/<spec-slug>
```

`<spec-slug>` is the same slug used for the report filename (spec title lowercased,
spaces → hyphens, trimmed to ≤30 chars). It is chosen uniquely per audit by the engineer
authoring the spec.

**Collision handling:**
- If a remote branch `sdk-audit/<slug>` exists with an **open PR** → Phase E2 (idempotency
  check) skips this repo and notes the PR URL. Do not force-push.
- If a remote branch exists with **no open PR** → `git push -u` will fail with a
  "non-fast-forward" error if the remote has commits not in the local clone. This is correct
  behaviour — the engineer should investigate before clobbering.
- Optional date suffix (`sdk-audit/<slug>-YYYYMMDD`) is deferred to stage 3 — not needed
  for v1.

**`--update-prs` flag** (stage 3, not v1): will push to an existing PR's branch when fixes
differ, instead of skipping.
