# PR description audit + screenshot refresh (shared fragment)

> **Naming convention.** Files named `_*.md` under `playbooks/` are **shared
> fragments**. The engine renderer (`engine/playbook.js`) does not (yet)
> implement an `<include>` directive, so each consuming playbook inlines this
> fragment verbatim and the two copies are kept in sync by hand. If you change
> this file, also update the inlined copies in `playbooks/fix.md` and
> `playbooks/implement.md` (and any other playbook that pushes commits to a
> tracked PR). The matching test in `test/unit/config-and-playbooks.test.js`
> ("PR description audit fragment is inlined consistently") fails fast when the
> copies drift.

## When to run

Run this audit **after** you push new commits to the PR's source branch and
**before** you mark the work item done.

Skip the audit entirely when:

- This dispatch was invoked with `meta.skipDescriptionAudit: true` (the
  engine renders an explicit "audit suppressed" notice when the flag is set).
- You did not push any new commits this dispatch (no behavior change → no
  description drift).
- The dispatch produced a no-op completion (`noop: true`).

When you skip, still report it: set `meta.descriptionAudit.ran = false` and
`meta.descriptionAudit.result = "skipped:<reason>"` in the completion JSON.

## Audit steps

1. **Fetch the current PR description.**
   - GitHub: `gh pr view <num> --repo <owner>/<repo> --json body --jq .body`
   - Azure DevOps: `az repos pr show --id <num> --query description -o tsv`
2. **Compute the diff for the commits you just pushed.**
   - `git log --stat <baseline>..HEAD` where `<baseline>` is `origin/main` for
     the first push or `origin/<branch>@{1}` (pre-push tip) for follow-up
     pushes. `git log --oneline <baseline>..HEAD` if you only need the SHA list.
3. **Compare description sections against the diff.** For each section,
   evaluate whether the new commits change anything it claims. Focus
   specifically on:
   - Feature lists / "what changed" bullet points
   - Config / option / matrix tables
   - File or path lists
   - Line counts, before/after metrics, perf numbers, table cell values
   - ASCII diagrams that depict layout the diff touched
   - Embedded markdown image refs (`![alt](url)`) — see the screenshot
     subsection below
4. **Decide the result.**
   - Description is consistent with the diff → no-op. Record
     `meta.descriptionAudit = { ran: true, result: "no-changes-needed" }`
     in the completion report and stop here.
   - Description needs targeted edits → build the patched body. **Preserve prose voice**. Edit only the stale parts. Do **not** rewrite. Do **not**
     add new sections that weren't there before. Do **not** change the PR
     title. Do **not** flip draft state, close/reopen, or post a separate PR
     comment narrating the description edit.
5. **Push the patched description.**
   - GitHub: write the new body to a temp file, then
     `gh pr edit <num> --repo <owner>/<repo> --body-file <file>`
   - Azure DevOps:
     `az repos pr update --id <num> --description "$(Get-Content -Raw <file>)"`
     (PowerShell) or the POSIX equivalent. ADO's description field also
     truncates around ~4 KB on the `--description` flag — if you hit that,
     fall back to `az rest --method patch` against
     `…/pullRequests/<id>?api-version=7.1` with `{ "description": "<body>" }`.
6. **GET-verify the description post-patch** by re-running the fetch from
   step 1 and confirming the body matches what you sent.

## Screenshot handling

This applies only when the **existing** description embeds markdown image
refs. **Never introduce screenshots to a PR that didn't already have any** —
this audit is a refresh, not an introduction.

1. Extract every `![alt](url)` from the current description.
2. For each image, decide whether it depicts a view this dispatch's diff
   touched. Heuristics (any one is enough):
   - `alt` text or filename mentions a component / route / view name that
     appears in the changed files.
   - The image URL path contains an issue/PR id matching this PR.
   - The description text immediately around the image references files
     present in the diff.
3. If the view was touched **and** the project has a runnable dev server
   (detect via `package.json` scripts named `dev`, `start`, or `serve`)
   **and** Playwright MCP is available, recapture:
   1. Spin up the dev server with a detached handoff (see
      `shared-rules.md` → "Long-Running Commands"). Capture PID + log path +
      URL + stop command.
   2. Drive Playwright MCP to the relevant route and screenshot the same
      view. Save raw PNGs to `agents/<id>/screenshots/` only. **NEVER `git add` a PNG** — adding any code path that commits a screenshot is a
      release-blocker bug.
   3. Upload as a PR attachment:
      - **ADO**: `PUT /pullRequests/<id>/attachments/<filename>` with the
        PNG bytes, then reference the returned URL in the description.
      - **GitHub**: there is no documented REST endpoint for PR-comment
        image uploads. The browser drag-drop path uses
        `github.com/upload/asset`, which `gh` does not wrap. If you cannot
        find a reliable scripted upload path, **skip** this screenshot,
        leave the old image URL in place, and record
        `meta.descriptionAudit.result = "screenshots-refreshed-skipped (gh-attachment-unavailable)"`.
   4. Patch the description to swap old image URLs for new.
   5. Stop the dev server using the recorded stop command.
4. If the dev server fails to start, the route 404s, Playwright fails, or
   the host's attachment API is unreachable → **skip that screenshot**
   without failing the work item. Record the skip and the reason in
   `meta.descriptionAudit`.

## Out-of-scope guardrails

- Don't add screenshots that weren't already there.
- Don't rewrite description prose beyond the targeted stale edits.
- Don't modify the PR title.
- Don't toggle draft state, close/reopen the PR, or post a separate PR
  comment narrating the description change.
- Don't fail the work item on a screenshot or attachment failure — degrade
  gracefully and record the skip.
- Screenshots **never** land in the repo. Save raw PNGs only under
  `agents/<id>/screenshots/`. No `git add` of any PNG, ever.

## Completion-report shape

When the audit ran (whether it edited the description or no-op'd), include
this block in the JSON completion report (see
`docs/completion-reports.md` → "PR description audit"):

```json
{
  "meta": {
    "descriptionAudit": {
      "ran": true,
      "result": "description-patched",
      "oldScreenshots": [],
      "newScreenshots": [],
      "patchedSections": ["feature-bullets", "config-table"]
    }
  }
}
```

Omit `meta.descriptionAudit` entirely when the audit did not run (no
commits pushed, no-op completion, etc.). When `meta.skipDescriptionAudit`
suppressed the audit, set `ran: false` and
`result: "skipped:meta-flag"` so the audit history stays auditable.
