-
Notifications
You must be signed in to change notification settings - Fork 2
plat 344
| Coordination | Value |
|---|---|
| Assigned agent | Codex |
| Ticket state |
implemented locally on remove-workflow-files-api (dc649a5ab); not merged or deployed |
| Last synchronized | 2026-09-21 |
| Priority | P1 performance / cross-user outage vector |
Selected workspace document routes (uploads, document writes, folder operations,
restores, imports) took one process-wide workflowFilesMu mutex
(WorkflowDocumentLock). The lock was acquired before the upload body was
read. A single slow POST /api/upload therefore blocked those document writes
from every user — including the agent's chat submission journal PUTs — for as
long as the upload streamed. This did not lock unrelated workspace routes.
On confida (2026-09-21 10:12–10:15 CEST) one upload from an external client
held the lock for 3m10s (ending 400). When it released, the queued writes
completed in a burst: conversation PUTs at 3m10s/2m40s and two chat journal
saves at 40.9s/21.2s. The agent's 15s journal-save context had long fired, so
both saves returned 503 "Cannot durably accept submission; nothing was sent"
— and the writes landed late anyway, orphaning a delivery_uncertain journal
record that replays 409 while its delivery cannot be reconciled (a live tmux
blocks the existing native-transcript absence proof). The reported user's
message remained wedged when the incident was recorded; this implementation
does not clear that record.
Ordinary document GETs bypassed the mutex. The internal /api/workflow-files
endpoint used POST even for reads and acquired it, so those reads could also
queue behind the upload. The incident presented mainly as scattered send
failures rather than a full outage.
The relevant design choices in workspace/ were:
-
One lock for otherwise independent document routes.
WorkflowDocumentLock(workflow_files.go) was a singlesync.Mutexshared by uploads, document PUTs, folder ops, restores, and imports (server.go). The workspace service principally proxies real filesystem operations; it does not need one global transaction over those requests. -
Lock held across network I/O. Middleware acquires the mutex, then the
handler reads the body:
UploadFilecallsFormFileunder lock, with its 10 MB file-size check occurring only after multipart parsing. Network slowness became lock-hold time. Multipart parsing may spool to disk; it should not be described as necessarily holding the whole body in memory. -
A legacy revision endpoint remained after its write surface was retired.
/api/workflow-filesstill implemented checked writes and a managed multi-file commit, but the current external-tool catalog is read/run-only:write_file,patch_file, and plan mutations are not exposed. Read-only external tools still called this endpoint, needlessly sharing the mutex.
Commit dc649a5ab on remove-workflow-files-api:
- Removed
WorkflowDocumentLockfrom upload, document, folder, restore, and import routes inworkspace/server.go, and deleted its process-wide mutex. A slow upload can no longer hold that mutex ahead of unrelated journal PUTs. - Removed
/api/workflow-files, its handler, revision-checked write/commit branches, and obsolete tests. Kept the generic authenticated document API. - Preserved external
read_file,list_files,search_files, run-file views, knowledge reads, andget_plan: the agent server reads its shared workspace filesystem withos.Rootconfinement and private-path/symlink checks. Where the agent has no shared workspace mount, it uses the existing service-token-only read-only/api/shared-assetsendpoint. That endpoint remains blocked from the generic public workspace proxy. The existingget_planresponse retains its read-only content fingerprint for client compatibility; there is no checked-write consumer. - Removed the now-pointless external per-workflow read mutex and updated the CLI/MCP setup documentation. No per-path locks, JSONL migration, journal rewrite, or upload-stream size/time limit was introduced by this change.
The ordinary document API now has filesystem-style concurrency: independent
paths proceed independently, while overlapping same-file writes are not
transactionally merged. Existing os.WriteFile paths may still expose a
partial file to a concurrent reader. Atomic replacement and targeted
read-modify-write coordination, if required, are separate scoped work—not a
reason to restore a process-wide lock.
The stranded delivery_uncertain receipt is not fixed here. An outcome-less
receipt is not proof that dispatch never ran: it can also remain when the
post-dispatch outcome save fails. A blind TTL or automatic resend could duplicate
a user's message. Recovery needs explicit durable phase/acknowledgement evidence
or a provider/transcript reconciliation path, with a targeted regression test.
ListUnresolvedChatSubmissions currently has no callers. Do not clear or
redispatch the live receipt solely because the workspace lock is removed.
-
go test ./...inworkspace/passes after removing the endpoint and lock. - Focused
TestExternal*agent-server tests passed with temporary local compile shims for unrelated, in-flight Gmail changes; the shims were removed and are not in the commit. Normal agent-server compilation remains blocked by those unrelated Gmail symbol/signature errors until that work is completed. - Still needed before claiming production resolution: slow-upload/concurrent-PUT
regression, sustained concurrent-write latency check, merge to
main, deployment to Confida, and live verification. None has been claimed here. - Separate live chat-reliability case to reconcile: Shubham's orphaned submission
39b34b8f-c6b5-4d26-9ab7-c3bda2cef129(sessionbe5087d3-b2c4-4fe1-a114-007a3fe12dcd). Its current state has not been rechecked after this local-only implementation.
Auto-synced from docs/ on main. Edit there, not here.