# PR Review Process

This document describes how PRs flow through review at LDR. It's the maintainer-facing companion to [CONTRIBUTING.md](../../../CONTRIBUTING.md).

> **Folder convention:** this is the first document under `docs/processes/`. Each folder under `docs/processes/<process-name>/` documents one organizational process, with `README.md` as the entry point so visiting the folder URL on GitHub renders it directly. Future processes (release, security review, etc.) should follow the same pattern.

## Overview

PRs come from a mix of sources: core maintainers, the extended reviewer team, one-off external contributors, AI bots, and Dependabot. Triage labels are auto-applied at PR open and toggled by review events so reviewers can find the right work to focus on without manually scanning every PR.

## Quick reference

| Label | Meaning | Who acts next |
|-------|---------|---------------|
| `external-contributor` | PR author is outside the maintainer team | First codeowner to triage |
| `first-time-contributor` | Author's first PR to this repo | A maintainer should welcome and review |
| `bot` | PR opened by an automated account (`*[bot]`, moltenbot, etc.) | A codeowner — apply higher scrutiny |
| `needs-codeowner-review` | Awaiting first review from a global codeowner | Global codeowners |
| `awaiting-author` | Codeowner requested changes; ball is with author | The PR author |
| `awaiting-codeowner` | Author has responded; needs codeowner re-review | Global codeowners |
| `needs-rework` | PR shows low engagement — broken tests, mechanical churn, ignored feedback, scope violation | The PR author (substantive rework) |

Labels are managed by:
- `.github/labels.yml` (declarative definitions)
- `.github/workflows/labels-sync.yml` (creates/updates labels)
- `.github/workflows/pr-triage.yml` (toggles them per PR)

## Who's a maintainer

**Global codeowners** (review required for any path; merge approval):
- @LearningCircuit
- @hashedviking
- @djpetti

**Extended reviewer team** (codeowners for tests, CI, docs, and templates):
- @scottvr
- @tombii
- @prashant-sharma-cmd
- @elpikola
- @shreydekate

See [`.github/CODEOWNERS`](../../../.github/CODEOWNERS) for the full path-to-owner mapping. The global-owners list is mirrored in `.github/workflows/pr-triage.yml`; both must stay in sync.

## PR triage queue

The canonical search filters maintainers and reviewers should run regularly. Each one returns the PRs that are most useful to act on next.

**External PRs awaiting first codeowner look** — the most important queue:

```
is:open is:pr -author:dependabot[bot] -author:LearningCircuit -author:hashedviking -author:djpetti label:needs-codeowner-review
```

**Likely-stale PRs** (author hasn't responded; candidates to nudge or close — replace the date with ~30 days ago):

```
is:open is:pr label:awaiting-author updated:<2026-04-08
```

**Bot PRs needing higher-scrutiny review** — treat these as proposals, not contributions:

```
is:open is:pr label:bot label:needs-codeowner-review
```

**First-time contributors** — extra welcoming and coaching:

```
is:open is:pr label:first-time-contributor
```

**Author has responded; needs re-review** — triage these regularly to avoid ping-pong delays:

```
is:open is:pr label:awaiting-codeowner
```

## Lifecycle of a PR

```
PR opened (external)  →  needs-codeowner-review
                         + external-contributor
                         + first-time-contributor (if first PR)
                         + bot (if automated account)

needs-codeowner-review  ──[codeowner: changes_requested]──>  awaiting-author
needs-codeowner-review  ──[codeowner: approved]───────────>  (no lifecycle label)

awaiting-author         ──[author pushes commit]──────────>  awaiting-codeowner
awaiting-author         ──[codeowner dismisses review]───>  needs-codeowner-review

awaiting-codeowner      ──[codeowner: approved]───────────>  (no lifecycle label)
awaiting-codeowner      ──[codeowner: changes_requested]─>  awaiting-author
```

`commented` reviews are a no-op — they don't move the lifecycle. Use approve / request-changes / dismiss to move state.

## Reviewer responsibilities

When you pick up a PR with `needs-codeowner-review`:
- Read the PR description for the contributor's verification narrative — what they tested by hand. Missing or generic descriptions are a signal — see "Spotting low-engagement PRs" below.
- Submit one of: approve, request changes, or comment-with-questions. Don't leave the PR in limbo.
- For external/bot PRs, double-check tests actually pass on the branch (CI green != tests pass meaningfully).

When you see `awaiting-codeowner`:
- The author has already responded. Don't make them wait — pick it up promptly or hand off.

When you see `needs-rework`:
- Don't review line-by-line. The PR is in a state that needs the author to take a substantive next step.

## Spotting low-engagement PRs

Reviewers should look for these heuristics — they're stronger signals than "was AI used":

- **Broken tests on the branch.** The author didn't run them locally before submitting.
- **Mechanical churn across many files.** 30+ files of same-shape edits suggests a one-shot generation rather than considered changes.
- **Author doesn't respond to specific review questions**, or responds with another regenerated diff rather than addressing the question directly.
- **Generic description with no concrete verification narrative** — nothing in the PR body suggests the author exercised the change beyond pushing it.
- **Atomic-scope violations.** Multiple unrelated changes bundled. CONTRIBUTING.md is explicit: one logical change per PR.

### Recommended response

Apply `needs-rework`, post a comment listing the concrete issues, ask for either a split or a specific revision. **Do not do the fix work for the contributor unless they go silent for an extended period.** Doing the work for them rewards low-engagement submissions.

## Bot PRs specifically

Bot-author PRs (`moltenbot000`, similar AI agents) have no human in the loop on the PR side. Iterating with the bot via PR comments rarely produces meaningful revisions — the bot typically regenerates rather than addressing specific feedback.

Treat bot PRs as **proposals, not contributions**. If substantive issues exist:

- Take the good parts forward as a maintainer-authored PR (cherry-pick + fix).
- Close the bot PR with a clear rationale comment.

PR #3847 was the working example for this pattern: a moltenbot PR with a sound architectural idea (loguru sink-level redaction) wrapped in mechanical call-site churn and broken tests. The right path was to land the patcher idea cleanly via a maintainer-authored PR and close the bot's submission.

## When to escalate

- **Security findings during review** (exposed secrets, auth bypass, injection): apply `security-review-needed` and ping `@LearningCircuit`. See [`docs/SECURITY_REVIEW_PROCESS.md`](../../SECURITY_REVIEW_PROCESS.md) for the security-specific flow.
- **Legal or licensing concerns**: core team only (`@LearningCircuit @hashedviking @djpetti`). Do not merge.
- **Persistent disagreement on approach**: pause, take it to Discord or an issue for broader input rather than escalating in PR comments.

## Out of scope here

This document is about the **review** process. Adjacent processes (release, security review, deprecation) are documented separately under `docs/processes/<process-name>/` (when migrated) or in their existing locations.
