Commit Graph
1 Commits
Author SHA1 Message Date
DottaandPaperclip 6e5b9794d6 perf(github): avoid duplicate webhook body reads (#17894)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - GitHub webhooks enter a durable action queue before the worker
processes them.
> - A worker must check retry eligibility and win an atomic claim before
it replays a delivery.
> - The current eligibility read returns the complete webhook body, and
the successful claim returns it again.
> - This pull request reads small eligibility fields first and returns
the body only to the successful claimant.
> - The worker keeps its retry, ownership, runtime, credential, and
recovery checks.
> - The benefit is less database transfer without removing durable
replay data.

## Linked Issues or Issue Description

Refs: #17666, #15981. Those changes own the existing recovery scheduler
and ingress module. This PR changes the result projections and
validation order within that module. It has no dependency on another
open PR. A public issue and PR search found no duplicate change.

**What existing behavior does this improve?**

The durable GitHub webhook worker reads action eligibility and replays
claimed deliveries.

**Current behavior**

The first read returns all action columns and the complete webhook body.
A successful claim returns them again. An active claim or future retry
also downloads a body that this worker cannot process.

**Proposed behavior**

Read only ID, status, result, and update time for eligibility. Return
ID, endpoint ID, and payload from the successful atomic claim. Validate
that claimed payload before replay.

**Reason and benefit**

A synthetic 131,105-byte webhook body produces about 263 KB of response
values before this change. The changed service returns about 132 KB for
successful processing and fewer than 150 bytes for skipped work. This
removes duplicate body transfer.

**Breaking changes**

There is no public API or schema change. Malformed active or delayed
rows now remain under their current claim or retry deadline. The
eligible claimant validates and fails them later. A worker that does not
own the claim can no longer clear their payload.

## What Changed

- Use a small projection for initial action eligibility.
- Return the replay body only from a successful claim. Keep the existing
status, stale-time, and previous-attempt guards.
- Validate the row returned by the claim. Fence invalid-payload
settlement by kind, processing status, and the claimed attempt.
- Add 19 real PostgreSQL regressions for complete replay bodies, skipped
work, retries, stale-claim recovery, contention, corrupt bodies, claim
loss, replacement bodies, and payload retention.
- Wait for the saved chat authentication link in one existing async UI
test. Keep its exact destination and all other assertions. This repairs
a second independent CI test failure without changing product UI.
- Accept the two precise invalid-repository diagnostics from Git 2.55
and 2.56 in one existing test. This repairs an independent CI
compatibility failure. It changes no Git expiration service behavior.
- Add a reproducible benchmark that loads the real baseline service from
an explicit commit. It captures generated SQL and parameters, query
plans, response-value estimates, timings, and complete valid-work
behavior comparisons.

## Verification

- Baseline: `663fe667d66d3863d0919a8724f1d99500feef0b`.
- PostgreSQL 17.11, Node 24.22.1. The fixture uses synthetic random body
content. No hosted database or provider was changed.
- `pnpm exec vitest run
server/src/services/chat-channels/github/webhook-ingress.test.ts`: 19
passed with a dedicated disposable PostgreSQL database.
- Existing assembled GitHub tests: recovery from pre-ingress failure,
scheduler retry and restart, exact terminal redelivery, runtime
pause/resume, recovery tombstones, and credential rotation passed.
- `node scripts/check-module-boundaries.mjs` and `git diff --check`:
passed.
- Benchmark: `node --import ./server/node_modules/tsx/dist/loader.mjs
scripts/benchmark-github-webhook-ingress.ts
--baseline-ref=663fe667d66d3863d0919a8724f1d99500feef0b`. The default
uses disposable embedded PostgreSQL. `PAPERCLIP_TEST_DATABASE_URL` can
select a dedicated migrated test database with a name that ends in
`_test`.
- Ten timed samples after two warmups, with alternating
baseline/treatment order. The main acceptance criterion is response
size, rather than a timing threshold. The 0.51 ratio bound is
deterministic for this fixed synthetic fixture; it is not a promise for
arbitrary webhook bodies or result metadata sizes. Focused tests measure
the body transfer reduction directly. Actual wire savings require
post-rollout measurement.

| Scenario | Baseline response-value bytes | Changed response-value
bytes |
| --- | ---: | ---: |
| Received | 263,276 | 131,547 |
| Due retry | 263,342 | 131,613 |
| Stale processing | 263,291 | 131,563 |
| Future retry | 131,692 | 136 |
| Active processing | 131,642 | 86 |
| Processed | 131,659 | 103 |

The benchmark compares response status/body, all stable durable action
fields, and the replay body, signature, delivery ID, and provider
routing. All six valid scenarios match. It excludes generated row IDs
and timestamps. The final measurement records treatment commit
`f1ad8ce838700237b56f895cc61a9c6ed0850cd6`, an empty initial worktree
status, and the SHA-256 of the actual source. It ran from the server
directory to verify that Git paths resolve from the repository root.
Local timing ranges overlap and vary with host load. An earlier loaded
repeat had a due-retry median of 19.61 ms before and 22.58 ms after, so
no consistent latency gain is claimed. The byte reduction is the
verified result. Response-value estimates exclude PostgreSQL
protocol/TLS overhead and JSONB whitespace. These are local
measurements, not a claim about deployed latency or a monthly bill.

`pnpm -r typecheck` and `pnpm build` passed on production-change commit
`6f4f36bff525494dadda30b49ea6a9a33976e1ab`. Greptile completed with 5/5
and no review threads on that commit. Commit
`2abf9b1e50c568b632e7568398a5826a6122e456` changes only the Git test
matcher. Commit `f1ad8ce838700237b56f895cc61a9c6ed0850cd6` fixes
benchmark provenance after automated review. Commit
`ee2a96dad549c72da3132d2b93fd11eb1c45bc31` repairs an existing async UI
test. Final commit `994d398d12be65f595b305d1c2f8a61b9d587ff9` extends
real PostgreSQL contention coverage after review. Full CI completed
successfully on that final head: [workflow run
40537698349](https://github.com/paperclipai/paperclip/actions/runs/40537698349).
All 55 successful checks, one neutral review, and two optional skipped
checks are terminal; none are pending or failing. Fresh Greptile and
Paperclip reviews are both 5/5, with zero unresolved review threads. The
production source SHA-256 remains
`402f9c66cd030c9ded9f39a3bc72d26c56627752c0950b102adcab15c4512df3`, so
the production proof is unchanged. The full local `pnpm test:run` was
not completed because this host exhausted embedded PostgreSQL semaphore
resources; the focused real PostgreSQL tests used a private disposable
container. Current-head remote CI passed the full suite. A direct server
typecheck before runner preparation reported missing runner build
outputs; supported preparation and the full typecheck then passed. The
first complete build found a partial local Copilot closure from the
dependency cache. The complete pinned closure was restored and verified,
then the complete build passed. The first local test run also found a
fixture teardown order error; the teardown was corrected, then all 17
tests passed. Frozen dependency installation failed on the existing
master patch metadata. Independent cached dependency directories were
cloned, their workspace links were checked, and no dependency manifest
or lockfile was changed.

The first CI server4 shard failed one existing Git expiration test: Git
2.56 reported `gitfile does not point to a valid repository`, while the
test expected the Git 2.55 phrase `not a git repository`. The service
returned the correct `inspection_failed` result and preserved the
workspace. Separate test-only commit
`2abf9b1e50c568b632e7568398a5826a9c6ed0850cd6` accepts either precise
diagnostic. The affected assembled test passed locally with real
PostgreSQL 17.11 and Git 2.55; the captured CI diagnostic also matches,
and unrelated permission errors do not. This was a compatibility
failure, not a transient failure or an unchanged rerun.

Paperclip Review found that the benchmark recorded its own scratch
directory as an uncommitted change. The final benchmark-only repair
captures repository status before scratch creation, runs Git commands
from the repository root, records the source hash before measurement,
and rejects source edits during measurement. A full benchmark run from
the server directory then reported clean provenance and passed all six
byte/behavior comparisons. The review thread was marked addressed after
this proof.

The next full CI run found a separate unchanged UI test failure in
`ConnectionIntentInteractionBody.test.tsx`: the saved authentication
link was still absent after one fixed flush. The same failure reproduced
locally: one failed and 50 passed. Separate test-only commit
`ee2a96dad549c72da3132d2b93fd11eb1c45bc31` uses the existing bounded
`waitForAssertion` helper around the identical destination assertion.
The whole file then passed all 51 tests, and UI token gates passed. The
no-extra-setup and no-automatic-completion assertions remain. No UI
runtime or timeout changed.

Paperclip Review also asked for parallel claims from stale-processing
and due-failed rows. The contention test now covers those states and
received rows. Each case holds the claims until all 12 workers have read
the same eligible attempt, then uses real PostgreSQL to assert one
replay and exactly one attempt increment. The whole focused suite passed
all 19 tests. This coverage change does not modify production code or
the measured queries. The review thread was marked addressed after this
proof.

## Risks

- Payload validation now follows claim commit. If the process stops
there, the existing stale-claim recovery path retains the body and
retries it. Tests cover fresh and stale processing rows.
- A malformed future retry now fails when due instead of being cleared
by an unrelated early worker. The claimed-attempt guard prevents invalid
settlement from overwriting a later claim. Terminal invalid-payload
settlement intentionally retains the baseline result shape (`code` and
`retryable: false`, without `attempts`). Changing that result contract
is outside this byte reduction; the non-retryable row cannot become due
again.
- Existing runtime-generation, credential, recovery-source,
replay-barrier, redaction, and retry checks remain in place. Existing
assembled tests cover these boundaries.
- There is no migration, startup job, dependency change, or deployment
in this PR.

## Model Used

- OpenAI GPT-6 through Codex. The exact runtime model ID and context
window are not exposed by this environment. Capabilities used: source
analysis, code editing, shell tools, PostgreSQL experiments, and
automated tests.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes (The
public contract is unchanged; benchmark usage and evidence are
documented above.)
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-27 12:05:30 -05:00