Files
PaperClipAI/server
DottaandClaude Opus 5.5 53645a0104 perf(workspaces): record per-phase timings for workspace saves (#19472)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native runs in a sandbox save the workspace to object storage. A run
saves before it starts work and again at the end.
> - A save of a large repository takes 150 to 250 seconds when it runs
alone. When several large saves overlap, each save takes 510 to 900
seconds.
> - A slow save before the run starts delays the agent. It also gives a
deploy a wider window to interrupt the run.
> - The save operation records only a start time and an end time. It
does not show which phase uses the time: in-sandbox capture, provider
download, re-hash, encrypted upload, or publication.
> - Without that data, a speed fix is a guess. Each possible bottleneck
needs a different fix.
> - This pull request records a bounded duration for each phase in the
save operation's local metadata.
> - The benefit is that one busy window on a deployment identifies the
bottleneck. Then a targeted fix can be measured.

## Linked Issues or Issue Description

**Is your feature request related to a problem?**
Saves of large repository workspaces are slow, and a save does not
record where the time goes. On one deployment, saves of a 460MB
workspace archive (a full repository with history) took 150 to 250
seconds when they ran alone. During a window where eight large saves
overlapped, each save took 510 to 895 seconds. Saves of small
repositories in the same window stayed at 2 to 6 seconds. The operation
row has only `startedAt` and `finishedAt`, so the contended resource is
unknown. It can be the sandbox CPU, the provider download, or server
disk and network.

**Describe the solution you'd like**
Each save invocation records per-phase durations and the archive size in
the operation's metadata, for both outcomes.

**Describe alternatives you've considered**
Changes to the speed of a single phase without phase data. A local
profile of one capture showed that `git fsck --full` on the archived
history uses about half of the capture time. But full object integrity
is a tested requirement, and the cheaper `--connectivity-only` check
does not meet it. A larger delta-base cache gave no gain. A threaded
check helps only on sandboxes with more than one core. Also, the overlap
slowdown can come from the server, not the sandbox. A fix chosen without
phase data could miss the real bottleneck.

Related: #19444 (skip redundant saves on follow-up runs), #19458
(overlap file hashing in capture), #19462 (bounded bootstrap retry after
a closed runtime listener).

## What Changed

- `captureRemoteWorkspaceArchive` accepts an optional `timing` record.
It writes `captureMs` (helper install and in-sandbox capture),
`transferMs` (provider download), `verifyMs` (server re-hash),
`byteSize`, and `entries` as each phase ends.
- `saveRemoteWorkspace` adds `uploadMs` (encrypt, hash, and store) and
`publishMs` (manifest read and authority commit). It also sets
`reusedUpload` when it reuses an earlier upload.
- `settleSaveInvocation` writes a `saveTiming` record
(`paperclip.remote-workspace-save-timing.v1`) with `outcome`, `totalMs`,
and the finished phases. It writes the record on success and on failure.
A failed save keeps the phases that it finished.
- The record holds only numbers, a fixed outcome, and a timestamp. It
holds no paths or provider text. It stays in the instance database
(`workspace_operations.metadata`), so it is not Telemetry.

## Verification

- Extended `retains the writer and previous snapshot when object storage
fails` in `server/src/services/remote-workspaces.integration.test.ts`.
After the storage failure, the `final` operation has a `failed` timing
with capture, transfer, and verify phases and no upload phase. After the
retry, it has a `succeeded` timing with every phase.
- Red/green: with the source change reverted, the test fails (`expected
[ undefined ] to deeply equal [ { …(9) } ]`). With the change, it
passes.
- `npx vitest run src/services/remote-workspaces.integration.test.ts -t
"object storage fails"` passes.
- `npx tsc --noEmit -p server` passes.
- A reviewer can read the record through `GET
/api/heartbeat-runs/:runId/workspace-operations`
(`metadata.saveTiming`).

## Risks

- Low risk. The change adds metadata only. It does not change save
order, fencing, retries, or archive bytes.
- The latest settled invocation replaces `saveTiming`. Concurrent
invocations of the same boundary keep only the last record. That is
acceptable for diagnosis.
- `performance.now()` calls add negligible cost.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, through Claude Code
with tool use (shell, browser for read-only staging GETs, code
execution).

## 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 15:54:13 +00:00
..