← Back to Research

How we deal with slop at Corridor: PR Descriptions

Researchers from BetterUp Stanford Social Media Lab coined the term Workslop: “AI-generated content that looks good, but lacks substance.” In a survey of 1,150 US desk workers, they found that recipients of low-quality AI workslop reported spending about two additional hours dealing with each incident.

Corridor is a small, fast-moving, AI-native team, and fixing workslop can quickly consume the time AI saves us. Sloppy code takes time to fix, sloppy internal documents create confusion, and sloppy PR descriptions make changes harder to review. AI-native development requires intentionality: we need to spend some of the time AI saves us reducing that burden on our coworkers, future selves, and customers.

One recurring source of slop was pull request (PR) descriptions. An agent could explain a one-line fix under five different headings. To address this, we created a writing skill, a linter, and a PR description quality checker.

1. The problem with PR-description slop

Here's a one-line PR diff and the PR description that Claude Code wrote for it:

- FILES=$(git diff --name-only "$OLD_REV" "$NEW_REV" -- web/public/tools tool/VERSION)
+ FILES=$(git diff --name-only "$OLD_REV" "$NEW_REV" -- web/public/tools tool/VERSION ':(exclude)web/public/tools/*-init.sh')

Original PR description

Changes

Before. The check_install deployment gate listed every changed file under web/public/tools. Any match forced the PR to bump tool/VERSION and to publish a signed tool release first.

web/public/tools/ holds more than installer assets. It also holds the four environment setup scripts: alpha-init.sh, beta-init.sh, gamma-init.sh, and delta-init.sh. So a setup-script-only PR had to cut a tool release that shipped none of its changes.

Did a reviewer really need the same one-line change explained under “Before,” “After,” “What,” “How,” and “Motivation”? Probably not. We wanted our coding agents to write concise descriptions that any engineer could understand without having to wade through excessive repetition or confusing prose.

PR descriptions grew faster than code changes

We examined merged PRs from August 2025 through August 2026 to see how widespread this was. We measured description length in prose words: the explanatory text in paragraphs and bullet points, excluding headings, code blocks, tables, and block quotations.

We found that median PR description length increased by 983%, while median diff size increased by 55%.

Monthly description length and diff size indexed to August 2025; description length rises much faster.
Figure 1. Monthly medians relative to August 2025, when each series is set to 100. Shading shows the middle half of descriptions and diffs.

Because larger changes could reasonably need longer explanations, we also compared descriptions within five diff-size groups. We again found that description length increased in every group. For 11–50-line diffs, for example, the median rose from 11 to 189 words, an increase of 1,618%.

Median prose words increased within all five groups of changed lines.
Figure 2. Description length increased in every diff-size group. Lines show monthly median prose words from August 2025 through August 2026.

The shift toward larger changes could not explain the growth on its own -- agents were writing more even for changes of a similar size.

Description length also differed by model

We also grouped PRs by the Claude model named in their merge commits. Opus 5 had the longest median description in this subset, about 7.6 times the median attributed to Opus 4.6.

Median prose words by attributed model: Opus 4.6, 83.5; Opus 4.7, 184; Opus 4.8, 236.5; Fable 5, 310; Opus 5, 633.
Figure 3. Description length by the model named in each PR’s merge commit. Dots show medians; lines span the middle half of descriptions.

The difference remained across PR diff size groups. Among PRs with 51–200-line changes, median description length ranged from 77 words for Opus 4.6 to 660.5 for Opus 5.

We realized that a writing standard needs to work across models, including ones that produce much longer explanations.

More words did not necessarily give reviewers more information

One description contained 1,302 prose words. It answered 13 review concerns, then repeated all 13 answers at the bottom. The agent even introduced the second set with: “Same issue-by-issue answers as above, repeated here at the end for reviewers scanning from the bottom.” This was one of many examples of unnecessary repetition.

Measuring word counts helped us find repetition, but couldn’t tell us whether a description explained the change. To assess that, we built a PR description quality checker using GPT-6 Sol and ran it on descriptions from the five weeks before we introduced the skill. We gave it each PR’s title, description, diff, and recorded results of automated GitHub checks, then asked it to evaluate four criteria:

CriterionWhat we askedRating
ExplanationDoes the description explain the change and give the reviewer the context needed to assess it?Good, Mixed, Poor
ReadabilityDo the ideas connect clearly, without confusing ordering or wording?Good, Mixed, Poor
ConcisionDoes the detail contribute useful information, without unnecessary repetition or tangents?Good, Mixed, Poor
AccuracyDo factual statements about behavior, scope, and testing match the supplied evidence?Supported, Contradicted, Unverifiable

Most descriptions were easy to follow: 97.6% received Good for Readability. But only 60.5% received Good for Explanation, and 11.7% contained unnecessary repetition or tangents. For Accuracy, the PR description quality checker flagged at least one contradicted claim in 34.5% of descriptions with valid results.

We needed our writing standard to address all three problems: repetition, missing context, and claims that contradicted the supplied evidence.

The full rating definitions are in Appendix C.

2. What makes a helpful PR description?

  • The problem. What needs to change, and why? Who is affected by the bug, what is too slow, or what should a new feature let someone do?
  • The solution. How does the PR address the problem? Explain the resulting behavior and decisions or trade-offs a reviewer needs to understand.
  • Testing. How was the change checked? State what the tests or manual checks establish and what still needs verification.

An unhelpful, sloppy PR description:

  • Uses more words than needed to explain the change.
  • Gives a simple fix the same lengthy treatment as a complex change.
  • Repeats the same information under different headings.
  • Buries the problem, solution, and testing details in commentary that adds little to a reviewer’s understanding.
  • Includes contradictory or incorrect statements about the PR.

3. Enforcing helpful PR descriptions

We found a useful starting point in ASD-STE100, or Simplified Technical English, a writing standard developed for aerospace documentation. Its rules help readers follow an instruction and understand when it applies. That includes keeping sentences short, using consistent terms, and stating a condition before the instruction it qualifies. The open-source SimpleEnglish project turns these principles into a skill: instructions an agent loads for a writing task.

We built three things to adapt it to our codebase and test how well it worked:

  1. A version of the SimpleEnglish skill for PR descriptions, asking agents to clearly explain the impact of the PR, describe how it works, and include testing steps.
  2. A prose linter: automated checks that give agents specific feedback on their drafts. It flags sentences over 30 words, filler expressions such as “robust,” and Latin abbreviations such as “e.g.” It also warns when a description is long overall or relative to the diff.
  3. A PR description quality checker: We used GPT-6 Sol with the four criteria above to check whether descriptions explained the change and matched the code.

The PR description from the first section passed the linter’s sentence checks, but the PR description quality checker flagged its repeated explanations. The following rewrite passed all sentence checks:

Rewritten PR description

Changes to standalone setup scripts no longer require a tool release. The version gate excludes web/public/tools/*-init.sh, including matching scripts in nested directories. Other installer assets remain gated, and the workflow still runs for setup-script edits.

Testing

Setup-script-only changes bypassed the gate. Installer-only, version-only, mixed, and new non-setup-file changes remained gated. The workflow also parsed successfully.

4. What improved and what still needed work

We compared descriptions written before the skill was available with descriptions written after rollout, using the same linter and PR description quality checker for both groups.

Descriptions became shorter and less repetitive

Median prose words fell from 226 to 163, while the sentence-check pass rate rose from 38.6% to 98.3%.
Figure 4. Descriptions before the skill (June 25–July 29) and after it became available (October 1–12, 2026). Passing means no errors for long sentences, filler expressions, or Latin abbreviations.

Median length fell from 226 to 163 prose words, a 28% reduction, even as the median diff grew. The sentence-check pass rate rose from 38.6% to 98.3%.

The share rated Mixed or Poor for Concision fell from 11.7% to 4.6%, a 61% reduction. Descriptions were shorter, and the PR description quality checker was less likely to find repetition or tangents. Both improvements remained when we compared periods with the same mix of diff sizes.

However, clearer prose still left gaps

After rollout, 62.0% of descriptions explained the change and its context well enough to receive Good for Explanation, compared with 60.5% before.

However, even among descriptions that passed the linter, 37.8% received Mixed for Explanation: the PR description quality checker could follow the main change, but an important detail was missing or unclear.

The PR description quality checker also checked whether claims about behavior, scope, and testing matched the supplied evidence. It flagged at least one contradiction in 34.5% of descriptions before rollout and 32.4% afterward, among those with valid Accuracy results.

One example PR changes how long background scans waited to send diagnostic data before exiting:

Original description, shortened

Background scans keep the longer telemetry timeout because they run unattended.

In the PR, if a relaunch failed, the code used the shorter timeout. The description left out that exception, making it sound as though every background scan kept the longer wait. Our writing rules told agents how to write concisely, but didn’t ask them to check whether their explanation matched the code.

So.... we fixed it

We added this rule to the skill:

Before finalizing the description, compare it with the diff. Check whether it explains the changed behavior, including conditions or exceptions that affect how a reviewer should evaluate it. Add missing details that matter, remove unsupported claims, and distinguish tests added from tests actually run. Keep the explanation proportionate to the change.

We then used Claude Opus 5.5 to rewrite the descriptions from the same group of PRs. Each rewrite used the updated skill, the original description, the diff, and available check results. The PR description quality checker evaluated the rewrites using the same rubric as before.

Baseline, skill, and regenerated description ratings: Good Explanation 60.5%, 62.0%, 89.3%; Good Readability 97.6%, 98.6%, 99.7%; Good Concision 87.8%, 95.4%, 99.1%; contradiction flags 34.5%, 32.4%, 16.0%.
Figure 5. Baseline shows descriptions before rollout; Skill shows descriptions after rollout. Regenerated shows those same later descriptions rewritten with the updated skill. The first three panels show the share rated Good. Accuracy shows the share with a contradiction flag, using valid ratings and matching pairs for Skill and Regenerated.

Good Explanation rose from 62.0% to 89.3%. Among PRs with valid Accuracy ratings for both versions, contradiction flags fell from 32.4% to 16.0%.

Here’s the rewritten version of the background-scan description above:

Rewritten description, shortened

Background scans keep the longer telemetry timeout. If a relaunch fails, they use the shorter default.

The rewrite includes the failed-relaunch exception, so a reviewer can understand when the shorter timeout applies.

The rewrites were 52% longer: median length rose from 163 to 248 prose words. They also received better ratings for Concision, with Good rising from 95.4% to 99.1%, and Readability, with Good rising from 98.6% to 99.7%.

This is what we wanted the skill to do: remove repetition while keeping the details someone needs to understand the change.

5. Applying the skill to open-source PRs

We wanted to test our skill on PRs that weren't authored by our own developers to see how it would work in the wild. Below we examine two open-source PRs with agent-generated descriptions: PowerToys #41454, authored by Copilot, and protomolecule #263, which credits Claude Code in its description and implementation commit. Both explained a narrow change across several overlapping sections.

Using the skill on a PowerToys grammar fix

The PowerToys PR changed “1 hours” to “1 hour” in the Awake tray menu. Its 30-line diff mostly added resource definitions and generated accessors. The description explained the grammatical error in its opening, Problem, Root Cause, and Impact sections.

Original PR description

This PR fixes a grammatical mistake in the PowerToys Awake taskbar context menu where "1 hours" was displayed instead of the correct "1 hour".

Rewritten with the skill

Fixed “1 hours” to “1 hour” in Awake’s “Keep awake on interval” tray menu. GetDefaultTrayOptions() now uses the new AWAKE_HOUR resource ("{0} hour") for that entry. It previously used AWAKE_HOURS ("{0} hours").

Added AWAKE_MINUTE ("{0} minute") for future one-minute intervals and generated both resource properties in Resources.Designer.cs. The existing 30-minute and two-hour entries still use their plural resources. This follows the singular/plural pattern in ImageResizer’s TimeRemainingConverter.cs.

Fixes #41220.

Testing

  • Build: dotnet build src/modules/awake/Awake/Awake.csproj was blocked by firewall DNS restrictions on i1qvsblobprodcus353.vsblob.vsassets.io.

The rewrite explains the resource change once, keeps the details about the other menu options, and says that the build was blocked. It went from 216 to 75 prose words, a 65% reduction. We removed repeated explanations of the grammar mistake while keeping the implementation and testing details.

Using the skill on a protomolecule dependency fix

The protomolecule PR fixed an Astro parser that ESLint, a code linter, couldn’t find when run from a project subdirectory. The fix made the parser an optional peer dependency: projects using Astro would install it at their workspace root. Its 60 changed lines included a 46-line release note. The description repeated the dependency change in its Summary, Solution, Changes, and Technical Details.

Original PR description

Summary

Fixes Astro parser resolution issues by moving astro-eslint-parser from dependencies to peerDependencies, ensuring the parser is accessible when ESLint runs from consumer workspace subdirectories.

Closes #261

Rewritten with the skill

Fixed Astro parser resolution when ESLint runs from a consumer workspace subdirectory. In v4.0.1, .astro files still used the JavaScript parser and failed with The keyword 'interface' is reserved.

Although --print-config showed astro-eslint-parser@1.2.2, ESLint did not resolve the parser inside the config package’s nested node_modules. Moved astro-eslint-parser (^1.0.0) from dependencies to optional peerDependencies so Astro consumers install it at the workspace root. This follows the plugin peer-dependency pattern introduced in v4.0.0.

Added the parser to devDependencies for this package’s linting and exported it as astroParser for explicit imports. The config uses the consumer’s parser installation.

Closes #261. Related: #259, #257.

Testing

The original description gives these steps after upgrading to v4.0.2; it reports no completed test results.

  • Run pnpm add -D -w astro-eslint-parser to install the parser at the workspace root alongside the other ESLint plugins.
  • Remove ignoreAstroFiles, which excludes **/*.astro, from the ESLint config.
  • Run pnpm --filter=@your-app/name lint and check that Astro files parse and lint without errors.

Generated with Claude Code.

The rewrite explains the dependency change once and keeps the installation and testing steps together. It also states that the original reported no completed test results, so the next engineer knows those checks still need to be run. It went from 283 to 148 prose words, a 48% reduction.

6. What we learned

The writing rules helped our agents produce shorter, less repetitive descriptions. Checking those descriptions against the code helped them include conditions they’d missed and correct claims that didn’t match the evidence. The resulting descriptions were sometimes longer, because they had more to explain.

This is one way we’re spending the time AI saves us: asking agents to do the extra work of checking their explanations before passing that work on to someone else.

You can try the skill in Appendix A and adapt it to your own workflow.

Appendix

A. The skill

---
name: corridor-deslop-pr-descriptions
description: >-
  Rules for writing a PR description in plain English.
  Read this BEFORE you write or edit one. Also use it when someone says a
  PR body is too long, hard to read, or sounds like AI: "the PR body is a wall
  of text", "write this in plain English". It covers short sentences, no hedges
  or filler, and the issue link and testing checklist every PR body needs.
  Check a draft with `pnpm run lint:prose`.
argument-hint: '[file or PR number to check; omit to apply while drafting]'
---

# Simple English for PR descriptions

These rules apply to PR descriptions.

## The rules

| Rule                          | Do this                                                                                                          |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| **Max 25 words per sentence** | Split longer sentences. Do not drop "the" or "that" to fit. The linter errors past 30.                           |
| **One idea per sentence**     | Two facts joined by an em dash or ", and" are two sentences.                                                     |
| **Condition first**           | "If the flag is off, the export is uncapped." Not "The export is uncapped if the flag is off."                   |
| **No hedges**                 | Use `must` for a requirement and `can` for a possibility. Delete `should`, `may`, `might`, `could`, and `would`. |
| **Simple past, active voice** | "We removed the column." Not "The column has been removed."                                                      |
| **One word per idea**         | Pick one of check, verify, confirm, validate, or ensure, and keep it. Do the same for config and settings.       |
| **No filler**                 | Delete `simply`, `robust`, `comprehensive`, `crucial`, `leverage`, `utilize`, `in order to`, and `prior to`.     |
| **No show-off words**         | Delete `load-bearing`, `plainly`, `quietly`, `byte-identical`, `carve-out`, and `provably`. Give the proof.      |
| **No Latin**                  | Write "for example" and "that is", not `e.g.` and `i.e.` List the items instead of `etc.`                        |
| **Code stays exact**          | Never change a name, path, flag, or quoted error. The linter counts each as one word.                            |

Keep every fact and number. Cut words, not facts.

## Lead with the result

Say what changed for the reader before you give a hash, a path, or a stack trace.

> **Before:** Query signature `b7872` runs a DISTINCT pre-query with a three-way join.
>
> **After:** Analytics for three teams have not refreshed in two days. One long rollup
> key stops the whole rollup. Query signature `b7872` is the cause.

## What a PR body needs

A reviewer reads the body to decide whether the change is safe to ship. Put the details
that decision needs up front.

1. What changed, in one or two sentences.
2. A link to the related issue.
3. A testing checklist.

Most PRs need about 100 words.
Leave out notes about how you got there. That means old attempts, pasted test output,
failures that were already on `main`, and a "what this PR does not do" section. Do not bold
the start of every bullet. End with one plain link to the agent session, if there is one.

## Check the explanation against the diff

Before finalizing the description, compare it with the diff. Check whether it
explains the changed behavior, including conditions or exceptions that affect how a
reviewer should evaluate it. Add missing details that matter, remove unsupported
claims, and distinguish tests added from tests actually run. Keep the explanation
proportionate to the change.

## Check the draft

```bash
pnpm run lint:prose --pr <PR_NUMBER>                                # a PR on GitHub
pnpm run lint:prose body.md --pr-body --changed-lines 135    # a PR body before the PR exists
```

Run it before you mark the PR ready. If the body changes later, run it again before
merge. Fix every error. Warnings are your call. CI does not run it. Its tests are
`pnpm run lint:prose:test`.

## Attribution

Based on ASD-STE100 Issue 9, by way of
[AminBlg/SimpleEnglish](https://github.com/AminBlg/SimpleEnglish) (MIT). ASD-STE100 is a
registered trademark of ASD. This skill is not endorsed by ASD.

B. The linter

The linter checks both the total word count and the amount of explanation relative to the code change. It supplies two types of warnings:

  • Length warning: more than 400 prose words.
  • Density warning: at least 250 prose words and more than three prose words per changed line.

These warnings prompt an editing pass. A small security fix may need more explanation than a large, repetitive refactor, so the agent will still need to judge which details matter.

The linter also warns about repeated bold labels at the start of bullets, descriptions of abandoned attempts, long agent footers, and missing testing steps. It flags a fixed list of words, including “simply,” “robust,” and “provably.”

For example, suppose a change makes the client retry a timed-out upload up to three times:

Before

Added robust error handling.

After

The client retries timed-out uploads up to three times.

Simply deleting “robust” would leave the behavior just as unclear. Our rewrite, on the other hand, clearly states what triggers a retry and how many retries the client allows.

C. The PR description quality checker

CriterionGoodMixedPoor
ExplanationThe change, purpose, and context needed to assess it are understandable.The main change is understandable, but an important detail is missing or unclear.The reviewer must reconstruct what changes or what it accomplishes from other sources.
ReadabilityIdeas connect clearly and the main point is easy to find.Some passages need rereading or untangling.Confusing wording or organization obscures the relationships between ideas.
ConcisionDetails contribute useful context, reasoning, or evidence.Some repetition or tangents could be removed without losing useful information.Repetition or irrelevant detail substantially obscures the explanation.
Accuracy ratingMeaning
SupportedThe supplied evidence backs up the claim.
ContradictedThe supplied evidence conflicts with the claim.
UnverifiableThe supplied evidence cannot establish whether the claim is true.

Get Started Today

Security should move at the same pace as innovation. Start building securely with Corridor.