DiffCI
← All case studies

Case study · red-build study

Which of Paperclip's red builds an agent could have caught before the push

paperclipai/paperclip is a public open-source repository. It is not a DiffCI customer, has not installed DiffCI and has not endorsed anything here. This study only read Paperclip's public CI history through the GitHub API, about 200 read-only calls made one at a time. Nothing ran in Paperclip's CI and nothing was replayed. The results were shared with the maintainers in a GitHub Discussion before this page was published.
74 of 300PR runs red in 14 hours
12red only on the PR's own pre-push failures
11 of 12of those were agent-written
~3,700runner-minutes in those 11 runs

Why Paperclip

Paperclip is a very busy repository with pull requests from coding agents. Its last 300 runs of the PR workflow cover only about 14 hours, from 20:37 UTC on November 10 to 10:27 UTC on November 11, 2026. It already selects its own tests: a "Select Vitest files" job and an incremental CI plan. So DiffCI wouldn't save much by selecting fewer tests in Paperclip's CI. The question here is a different one: how many red builds would never have been pushed if the agent had run the right checks in its own loop first?

Method

For each red run, every failing job was sorted into one of two groups. Pre-push jobs are checks an agent can run locally: repository-rule checks, Typecheck + Release Registry, General tests, Sandbox provider tests, Runner support suite, Verify Paperclip Runner, Build, and Verify serialized server suites. CI-only jobs are the E2E shards, Canary Dry Run, Docker context integrity, and the lockfile and migration-ordering steps of the policy job.

Paperclip's master has no test workflow on push and there is no merge queue, so there is no base run to compare against. Instead, each failing test was identified from its check-run annotation (file, test name and first message line). A failure counts as the PR's own when no other PR in the window failed the same way. Failures shared across PRs, such as one report-assets test on 10 PRs and one heartbeat-plugin test on 9, are a broken base or an always-failing test the author couldn't fix. Failures that are only timeouts count as load or flakiness. A run is agent-written when its head commit has an agent co-author line or its branch has an agent prefix such as codex/.

Results: 74 red runs

Red runsRunner-minutes
All red PR runs (60 PRs)7425,563
Preventable: every failure was the PR's own, in a pre-push job124,048
— of which agent-written113,702
Partly: an own failure, plus E2E, shared or unknown ones144,848
— of which agent-written62,082
Not the PR's own: shared, timeout or no usable annotation3612,504
CI-only: E2E shards only113,832
Infrastructure1331

Inside the agent's loop, 11 of the 74 red runs (15%) could have been stopped before the push. All 11 were agent-written. Together they cost 3,702 runner-minutes, and each was a wait of about half an hour: a red run took a median of 32 minutes from start to finish. In 6 more agent-written runs the agent would have fixed its own failure, but the run would still have gone red from an E2E or shared failure.

Most red runs were not the author's fault. 36 failed on shared, timed-out or unannotated tests and 11 only in E2E shards. A pre-push check can't help with those, and this page doesn't count them.

The 12 preventable runs

The failures are the kind DiffCI's selection and checks are built for: Vitest unit and integration tests, plus tsc. Each run links to its public GitHub Actions page.

RunAgent-writtenFailing jobFirst failure
42922381980YesVerify serialized server suitesA vi.mock of ../services/task-retry.js without the DECLINED_TASK_RETRY_REASONS export
42940191943YesGeneral testsAssertion failure in chat-channels.integration.test.ts
42951846997YesGeneral testsTypeError: timer.unref is not a function in event-loop-stall-monitor.ts
42962300744YesGeneral tests (3 jobs)Failures in chat-channels and voice-session-store integration tests
42963271556YesTypecheck + Release Registry'"codex_app_server"' is not assignable to '"mock" | "remote" | "runner"' in packages/paperclip-runner
42964700326YesGeneral testsAssertion failure in native-failed-workspace-recovery.test.ts
42968455542YesGeneral testspaperclip_runner_binary_missing in native-codex-runner.ts
42973958854No markerVerify Paperclip RunnerA failed insert in the native-restart test fixture
42976147787YesGeneral testsSandbox creation has already transferred ownership
42976154239YesGeneral testsAssertion failure in plugin-fresh-workspaces.integration.test.ts
42989667294YesGeneral testsAssertion failure in attention-retention-work.test.ts
42991185935YesVerify Paperclip RunnerA failed insert in the native-restart test fixture, then Maximum call stack size exceeded

The vi.mock case is worth noting: a mock that doesn't return an export the code now needs is a mock-edge case, which DiffCI's graph tracks. The type error is the kind of failure a test-selection tool can't see at all. It is caught by the typecheck, as in the own-repository catch replay and the LobeHub replay.

What this shows and what it doesn't

It shows that in a busy, agent-heavy repository, almost every red build that a pre-push check could have stopped was agent-written: 11 of 12. The failures were ordinary Vitest and tsc failures, not E2E or infrastructure.

It doesn't show that DiffCI would have caught each of them. DiffCI's actual plan wasn't computed for these commits. It would miss one only if its plan left the failing file out, and checking that needs a replay of these heads, which Paperclip hasn't been asked for. Other limits:

Method, scripts and the per-run data (analysis.json, own-failures.json, agent-authorship.json and the check-run annotations) are in DiffCI's research repository (research/ci-replay/paperclip/red-builds/).