mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-21 08:22:20 +02:00
fix(server): release idle tracking after aborted static UI files (#16685)
## Thinking Path > - Paperclip runs work for AI agents. > - Hosts must prove that accepted work has finished before idle sleep. > - HTTP request tracking keeps unfinished writes active after a client leaves. > - Static file middleware has no returned completion promise. > - An aborted UI download can leave its request active forever. > - This PR completes only a matched file response and keeps other work tracked. ## Linked Issues or Issue Description Refs #15522. Found while qualifying #16679. A search found no duplicate static-download idle fix. **What happened?** Closing a client during a static UI download leaves one tracked request active. The response has closed, but the file middleware never calls the wrapped response end method. The instance cannot report idle after that request. **Expected behavior** A closed, matched static file response must not leave a permanent work hold. Unfinished application writes must remain tracked. A missing file must keep the normal fallback and error paths. **Steps to reproduce** 1. Serve a large local file through the static UI middleware with idle admission enabled. 2. Start the download over HTTP. 3. Disconnect the client after its first data chunk. 4. Observe that the tracked request remains active with the original middleware. **Paperclip version or commit** Reproduced on the behavior in `d30648dee89720358271c276b9fc28cf83f18be7`. This branch starts at `cfa9dce295970a85b3d8ee14cab0eccf50bc72e2`. ## What Changed - Mark the completion of a known read-only response without dropping pending middleware promises. - Wrap static UI file serving. A successful file match is required before a closed response can complete its tracking. A late match rechecks an already closed response. Missing files keep the normal fallback. - Use the wrapper for hashed assets, other static UI files, and public development files. Preserve caller headers and cache options. - Test both HTTP abort timings, nested routers, conservative mounted-application holds, parent writes, mutating GET fallbacks, and callback-only work. - Update the idle contract and its verification map. Correct the paused-message recipe from hosted qualification: an explicit Pause cancels older queued work, so test new accepted input after the pause and await the normal resume lifecycle. ## Verification - The original two real stream-abort regressions fail with the original Express static behavior. - Three review regressions fail on `e260f4694afab8447b0cb203f439cb3ccad254e8`: a client closing before file lookup, an unfinished parent in a mounted application, and an unfinished parent in a root without installed handler tracking. Fixed sources were restored byte for byte after the comparison. - Two focused suites: 16 passed. They cover nested routers, both abort timings, hidden parent writes, late missing-file fallbacks, contents, HEAD, conditional responses, and caller headers. - Module and map checks pass. Map checks are structural only. - `pnpm -r typecheck` and `pnpm build`: passed on `f242ee739344484995d3addf81635512370778c8`. - Full CI matrix: 54 successful checks and 2 intentional Storybook skips. - Apex: 5/5 on the exact current head, with zero unresolved threads. - Full local maintained test groups are still running before final handoff. - The initial local broad run was stopped before changing source for the P1 review findings. Two file-mode fixture failures in that run came from the scratch runner's inherited `0077` mask; the same suite passes with the normal child test mask `0022`, without source or host configuration changes. The replacement run uses `0022` for test children and command-scoped `init.defaultBranch=master` for existing Git fixture assumptions. - This PR has not been deployed to a hosted environment. ## Risks Only a matched file served by the admitted application with installed handler tracking can complete this way. Routers share that scope. Mounted Express applications and roots without handler tracking keep the existing conservative response hold; this change does not claim their hidden writes are tracked. An unfinished tracked parent promise still blocks sleep after a file client disconnects. Redirects, missing files, application callbacks, and other GET requests retain their guards. No authentication, company scope, provider cleanup, or scheduling gate is removed. No schema or API response changes. ## Model Used OpenAI GPT-6 through Codex, with reasoning, source editing, and shell tools. The session does not expose a more specific serving model ID or its context window size. ## 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 - [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>
This commit is contained in:
1 parent
1f27427ffd
commit
63fbb714e6
7 files changed
+386
-9
No files matched your search
@@ -44,6 +44,16 @@ It remains reachable during an idle drain. Use `/api/health` for explicit databa
|
||||
readiness, signed bootstrap, and authorized diagnostics; liveness does not
|
||||
establish database health or claim a workspace.
|
||||
|
||||
Automated: [static UI idle completion](../../server/src/__tests__/static-ui-idle.test.ts)
|
||||
covers real aborted file streams, disconnects before file lookup, nested routers,
|
||||
and unfinished parent/fallback writes. Mounted Express applications retain their
|
||||
conservative hold. It does not qualify provider cleanup or a hosted sleep/wake cycle.
|
||||
|
||||
Manual: On an otherwise idle disposable instance, close a static UI download
|
||||
served by the admitted application before it finishes. Repeat with a disconnect
|
||||
before headers arrive. Confirm that neither leaves a permanent request hold. An
|
||||
unfinished application write must still prevent sleep after its client leaves.
|
||||
|
||||
### `settings-backup`
|
||||
|
||||
Automated: [experimental settings](../../ui/src/pages/InstanceExperimentalSettings.test.tsx) and [database backup routes](../../server/src/__tests__/instance-database-backups-routes.test.ts) cover their UI/API contracts.
|
||||
|
||||
@@ -100,10 +100,16 @@ the task (and separately its ancestor), preserve a draft, then resume the actual
|
||||
hold; no hidden send should occur while paused. Check both task presentations
|
||||
when available; the named component tests do not prove every host integration.
|
||||
|
||||
Pause an agent after a follow-up is accepted but before the prior turn finishes.
|
||||
Let release and periodic recovery run. Confirm the saved request remains pending,
|
||||
then resume the agent and verify one delivery. A paused request must not prevent
|
||||
another eligible recipient's queued work from proceeding.
|
||||
Fixture-backed recovery check: start with an agent in its completed paused
|
||||
lifecycle state, then insert a new accepted `deferred_issue_execution` request
|
||||
through the [PostgreSQL recovery fixture](../../server/src/modules/wake-queue/adapters/postgres.test.ts).
|
||||
Normal wake admission
|
||||
rejects paused agents, so this pending state cannot be created by sending a new
|
||||
message through the ordinary UI or API. Let release and periodic recovery run.
|
||||
Confirm that the saved request remains pending. Resume the agent, wait for its
|
||||
lifecycle to become ready, and verify one delivery. An explicit Pause cancels
|
||||
older queued work; those cancelled receipts must not be replayed. A paused
|
||||
request must not prevent another eligible recipient's queued work from proceeding.
|
||||
|
||||
### `agent-chat`
|
||||
|
||||
|
||||
Reference in new issue
Block a user