Files
PaperClipAI/doc
Dotta 5dee4993f1 fix(server): distinguish aborted HTTP responses in access logs (#18781)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Operators use HTTP logs to investigate task updates and database
contention.
> - Node starts each response with status 200 before it sends any
headers.
> - The custom HTTP formatter printed that status when the client
disconnected before a response.
> - This made an aborted request look like a successful task update.
> - This pull request labels incomplete responses as aborted and
preserves error severity.
> - Operators can distinguish HTTP delivery from database commit
evidence.

## Linked Issues or Issue Description

- Refs: #18753.
- Related: #14369 removes writes from legacy comment attribution reads.
This change addresses a separate logging defect.

The missing-write incident in #18753 remains unconfirmed. We reproduced
a misleading `PATCH /issues/:id 200` log when the client received no
HTTP response. We did not reproduce an HTTP 200 delivered before the
mutation committed.

## What Changed

- Label premature connection closure as `request aborted before
response` or `request aborted during response` in both success and error
log paths.
- Log aborts as warnings, including on routes whose successful polling
logs are normally silent. Keep explicit errors and 5xx responses at
error severity.
- Keep private webhook paths and query values out of aborted-request
logs.
- Add real HTTP disconnect coverage and real PostgreSQL task-update
tests with synthetic data.
- Document that HTTP completion logs do not establish database commit or
rollback.

## Verification

- Baseline: the new HTTP disconnect test failed because the log said
`PATCH /api/issues/synthetic-task 200` at info level, while the client
received no response.
- `node node_modules/vitest/vitest.mjs run
server/src/__tests__/http-log-aborted-response.test.ts
server/src/__tests__/http-log-redaction.test.ts
server/src/__tests__/issue-update-lock-contention-postgres.test.ts`
passed: 81 tests in 3 files.
- `pnpm -r typecheck` and `pnpm build` passed. The build used a second
clean clone at the exact PR commit.
- GitHub CI completed at `e3e96d836119df5fa1c7f22615484265de5aa700`: 52
checks passed and two Storybook checks were skipped. Greptile returned
5/5 with no findings.
- Local `pnpm test:run` passed all six chat shards (1,834 tests). Its
general server stage then reported four failures. We stopped the run
after preserving these results and comparing the failing cases. The
later workspace and serialized stages were not completed locally. The
full local suite is not green.
- The four observed failures were the two OAuth concurrency cases in
`tool-access-service.test.ts`, the bounded workspace conflict case in
`workspace-runtime.test.ts`, and the `enableNativeRunner`-disabled
stop-control case in `muse-control.test.ts`.
- Comparisons used the same sanitized environment on unchanged master
`2b14ec03a48f17606db53fd7fc0edc53bc930409` and this PR. Both OAuth cases
pass in isolation on both revisions. The full tool-access file passes
all 498 tests on the PR; master has a different 15-second timeout, with
497 tests passing. The workspace case passes on the PR but fails
port-reservation setup on master. The Muse case passes on both. These
results do not explain or clear the original broad-suite OAuth and Muse
failures.
- Tested on macOS arm64, Node.js 26.4.0, pinned pnpm 9.15.4, and
disposable embedded PostgreSQL. The API fixture uses a synthetic
process-adapter agent. No model provider runs and no production database
or real credentials participate.
- The PostgreSQL fixture verifies an actual lock waiter with
`pg_blocking_pids`. A blocked issue update does not return 200 before
release. After release, status, monitor policy, inline comment, and
audit receipt are persisted before the completed response.
- Holding the company row with `FOR NO KEY UPDATE` does not block these
fixture writes. They return 200 with persisted data while the compatible
parent-row lock remains held.
- An injected database trigger failure returns 500 and leaves the issue,
comment, and mutation audit unchanged. Two influence-attempt receipts
remain under the existing documented cap policy.
- The combined lock/disconnect/failure test verifies no delivered
response, an aborted log, and rollback after an injected database
failure. The injected failure is not evidence of the reporter's
production root cause.
- Source inspection at release `v2026.1023.0`
(`1b4c803816e82b0265c8422e1a804327a7ed8c54`) finds the same
unconditional success formatter and awaited issue-update transaction.
That release was not runtime-tested.

## Risks

- Log consumers that parse the literal method/path/status message must
handle the aborted-request message.
- Client disconnects during streaming or SSE responses also use the
aborted warning. Expected disconnects may therefore produce more
warnings.
- A disconnected request may still commit after the client leaves. This
change reports transport completion and does not cancel or retry
mutations.
- This PR does not close #18753. The original missing-write cause and a
received-200-without-commit scenario remain unconfirmed.
- Local full-suite failures remain unresolved despite green CI. Keep
this PR in draft until that verification gap is accepted or resolved.

> This is a targeted bug fix. ROADMAP.md does not list overlapping
feature work.

## Model Used

- OpenAI GPT-6 through Codex, with reasoning, tool use, code execution,
and parallel subagent investigation.
- The exact runtime model ID and context window size were not exposed to
the agent.

## 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
- [ ] 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
- [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
2026-11-03 09:26:03 -06:00
..
…