mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-23 17:33:51 +02:00
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Each task prepares its workspace before it starts the provider. > - Source preparation holds a transaction-scoped advisory lock on a dedicated database connection. > - Preparation can use other connections and the filesystem for longer than PostgreSQL allows that lock connection to stay idle. > - PostgreSQL then closes the connection and releases the lock while preparation still runs. > - This pull request reuses the workspace lock keepalive during source preparation. > - Slow preparation retains exclusion and returns its original result or error. ## Linked Issues or Issue Description **What happened?** Tasks failed during workspace preparation with `CONNECTION_CLOSED` at the configured 60-second idle transaction timeout. No provider had started. A live retry showed the source-lock transaction idle while authorization reads ran on another connection. Its advisory-lock key matched the task's source-preparation key. PostgreSQL closed that transaction at the timeout. The same defect remains on current master. A real PostgreSQL regression also shows that another preparation can enter after the timeout while the original callback still runs. **Expected behavior** Source preparation must keep its advisory lock until the callback and any pending keepalive query finish. The ordinary pool must remain usable. The original preparation result or failure must survive. **Steps to reproduce** 1. Set `idle_in_transaction_session_timeout` to one second in a disposable PostgreSQL database. 2. Call `withIsolatedRepositorySourceLock` and keep its callback active for longer than one second without querying the lock connection. 3. Try to acquire the same source lock from another operation. 4. On the old code, the second operation enters and the first transaction fails with `CONNECTION_CLOSED`. **Paperclip version or commit** Reproduced on source at `cde944ed75648adfb569e86133adc8b118b55dd6`. The repair is rebased onto `d855e550ad`. **Additional context** Refs #16340. That change introduced the keepalive for workspace copyback. This PR applies the existing helper to the source-preparation transaction. ## What Changed - Read the configured idle timeout in the same query that acquires the source lock. - Keep that dedicated connection active during source preparation with the existing helper. - Add two real PostgreSQL regressions for exclusion, company independence, a one-slot ordinary pool, nested acquisition, cleanup, and preservation of callback errors. - Update the execution contract and workspace verification maps. ## Verification - Red before the production edit: both new tests failed. A competing operation entered after the lock expired. A callback failure was replaced by `CONNECTION_CLOSED`. - Green after the repair: 86 tests passed across the source lock, source admission, keepalive, and heartbeat workspace preparation suites. - `pnpm -r typecheck`, `pnpm build`, and `node scripts/check-maps.mjs` passed on final commit `2b5000ce25`. - `git diff --check` passed. The lockfile is unchanged. - After rebase, the same 86 focused tests passed. The two source-lock tests passed again after the review cleanup change. - Live acceptance on `7df94d7a06`: a fresh local instance on macOS arm64, Node v26.4.0, native Codex `gpt-6.1-sol`, and permission mode `never`. PostgreSQL had a one-second idle transaction timeout. A temporary Git wrapper delayed repository reads by four seconds. Workspace preparation after environment acquisition took 8,297 ms. The real task completed, wrote and attached the correct line-count/SHA-256 report, reached `done` with native phase `committed`, and cleared its execution lock. The attachment matched the workspace file and survived browser reload. No dedicated lock connections remained. - The live probe initially used `gpt-5.4`, which the ChatGPT account rejected. Selecting the reported task model, `gpt-6.1-sol`, resolved that readiness failure. The local Claude CLI has no login, so no live Claude run is claimed. - Full CI passed on `2b5000ce25`: all general, chat, workspace, serialized-server, Runner, and E2E test shards, plus typecheck, build, and canary packaging. Both Greptile and Paperclip Review rated this commit 5/5 with no open findings. The Paperclip check concluded neutral; two unrelated visual checks were skipped. - The duplicate local `pnpm test:run` serial invocation was stopped with SIGINT after the complete CI test shards passed. It did not finish locally; no local full-suite pass is claimed. Local proof consists of the focused real PostgreSQL tests, typecheck, build, map checks, and the real task above. - The final commit changes test teardown only; production code matches the live acceptance commit. - Initial sandbox runs could not open test database ports or build IPC sockets. The same commands ran with local process access. The frozen install also found an existing manifest/lockfile mismatch; dependency resolution was local and the lockfile was restored. ## Risks - The lock connection sends one small query per keepalive interval. The helper derives that interval from the configured timeout and caps it at 30 seconds. It does no polling when the timeout is disabled. - The helper joins an in-flight query before the transaction ends. A slow database can therefore delay cleanup. - This change does not disable database deadlines, retry preparation, or recover an unexpected connection loss. It preserves existing admission checks and company/source lock keys. - Deployment and recovery of the reported tasks remain separate from code verification. ## Model Used OpenAI GPT-6 through Codex, with repository tools, code execution, and browser verification. The exact served model variant and context window are not exposed in this session. ## 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>
271 lines
15 KiB
Markdown
271 lines
15 KiB
Markdown
# Execution workspaces, services, and files — agent access
|
|
|
|
Product map: [Execution workspaces, services, and files](../feature-map/workspaces.md).
|
|
|
|
Lost-source reconstruction adds the board-only
|
|
`POST /api/issues/{id}/recovery-actions/reconstruct-workspace` action. It requires
|
|
`runtime:manage` and ordinary task visibility. Agents cannot authorize it. The
|
|
result is a durable fresh-session admission request, subject to current execution
|
|
gates. It grants no additional tool access, budget, work window or permission to
|
|
repeat completed actions. Attachments must belong to the same task and company.
|
|
The original failed run and lost-export evidence remain unchanged.
|
|
Later native run-only cancellations can coexist only with confirmed lease stops,
|
|
board file-only repair and an external-action outcome of `not_performed`.
|
|
Their immutable evidence is checked again at dispatch. Historical cancellation
|
|
retention runs internally and grants no new agent operation or authority.
|
|
|
|
Review date: 2026-10-12. Source commit: `58e16dbddfc9c8d0cf60867f9d9847f0feda055b`.
|
|
Native REST transport reconciled in this change; see the
|
|
[shared transport contract](README.md#native-rest-transport).
|
|
|
|
This is a source-reviewed baseline of representative operations and known gaps.
|
|
Each capability needs operation-level review before its entire scope can be
|
|
qualified. No live agent execution is claimed. Follow the [map contract](../README.md)
|
|
for format, permission checks, evidence and maintenance.
|
|
|
|
## `binding`
|
|
|
|
Review: `baseline`.
|
|
|
|
API: `present` — representative operation IDs:
|
|
|
|
- `GET /api/issues/{id}/heartbeat-context`.
|
|
- `GET /api/execution-workspaces/{id}`.
|
|
- `GET /api/issues/{id}/workspace` inspects the durable task binding.
|
|
- `PUT /api/issues/{id}/workspace` requests the next binding.
|
|
- `GET /api/companies/{companyId}/execution-workspaces?summary=true&selectableForTask=true`
|
|
lists authorized task-selectable workspaces.
|
|
|
|
Agent access: `available` — Native get_task_context / REST / CLI / MCP workspace read.
|
|
|
|
The task-bound native tools `get_workspace`, `list_workspaces`, and
|
|
`select_workspace` use the ordinary authenticated APIs. Legacy agents can call
|
|
those APIs with their admitted run token. These paths do not require a project.
|
|
|
|
Policy: Company/project/workspace visibility and runtime authority; active run, trust
|
|
preset and task ownership where required.
|
|
|
|
Task workspace writes require `issue:mutate`, an active owned task/run for agents,
|
|
and current source visibility. Shared physical roots also require source-project
|
|
workspace write authority. Accepting a new pending choice advances its binding
|
|
revision; superseded retries fail while current-choice retries remain idempotent.
|
|
Binding revisions and request keys fence retries;
|
|
selection applies at the next normal admission without interrupting a live run.
|
|
|
|
Fresh standard-trust tasks with an implicit repository default can use an owned
|
|
remote clone when the shared default source fails the restricted-task workspace
|
|
guard. This does not grant access to the private local checkout or its Git metadata.
|
|
The normal admitted Git identity must already authorize the configured remote.
|
|
Creation of an automatic fallback source is unavailable for explicit, parent/channel, reused
|
|
or low-trust selections, or branch templates without a task-derived placeholder;
|
|
an existing verified owned source remains reusable. Literal branch selections
|
|
retain the original denial without cloning or replacing their unpublished commits.
|
|
New implicit Git defaults record project-source provenance. Untyped explicit,
|
|
inherited and older ambiguous checkout bindings retain the denial; admission reads
|
|
the current company-scoped binding before considering the fallback.
|
|
A retained denial blocks the task as `configuration_incomplete`
|
|
(`workspace_source_restricted`) before dispatch; it grants no access and offers Retry.
|
|
The same blocker applies when shared project workspace repository admission finds
|
|
the restricted source. The existing binding and files stay intact; no provider
|
|
starts and no automatic failure retry is queued.
|
|
An authorized board recovery through `POST /api/issues/{id}/recovery-actions/resolve`
|
|
can schedule a server-owned `recovery` selection for the latest restricted-source
|
|
failure only when its saved evidence proves provider work never started. The
|
|
selection requires task mutation and configured-source access and applies at normal
|
|
admission. It keeps task environment settings and never reads or deletes the denied
|
|
checkout. Pause, budget, approval, interaction, dependency, and ownership gates
|
|
remain effective. Repeated resolution of the same action does not enqueue another
|
|
run. Agents cannot mint recovery provenance through the workspace selection API;
|
|
they can observe the existing recovery action and request board recovery.
|
|
Local paths, unsupported transports and credential-bearing URLs are not fallback
|
|
sources. Company/task/remote bindings, real paths, Git storage and the exact origin
|
|
are revalidated for reuse, cleanup and reopening. A failed save removes only the
|
|
owned task worktree. The [isolated-source service](../../server/src/services/isolated-remote-workspace-source.ts)
|
|
and its local regression tests establish source and lifecycle boundaries; live
|
|
agent/provider qualification remains separate.
|
|
Reconstructable Git expiration adds no agent endpoint or permission. It records
|
|
company/workspace/repository identity and an exact commit before removing owned
|
|
cache files. Existing task/workspace reopening retains current company, source,
|
|
and trust checks. Missing upstream access returns a retryable error; it does not
|
|
choose a newer commit or grant access to another company's source. Shared project
|
|
bases and unresolved recovery remain protected. Local lifecycle evidence is in
|
|
[expiration tests](../../server/src/__tests__/git-workspace-expiration.test.ts).
|
|
|
|
Writable aliases must also be compatible with current and retained physical
|
|
source projects' trust boundaries. Incompatible choices are rejected before
|
|
dispatch; a projectless alias cannot bypass required sandbox containment.
|
|
Source-copy checks apply the same trust boundary before provisioning. The
|
|
authoritative sandbox or SSH selection suppresses configured and built-in
|
|
controller worktree and runtime provisioning regardless of task trust, source
|
|
write grants or board sponsorship. Genuine isolated worktrees still permit
|
|
authorized read-only source copying. Source write permission does not authorize
|
|
controller execution. Explicit, channel, inherited, foreign or nondefault isolated
|
|
source choices remain copy-only for controller provisioning even on local runs.
|
|
Their deny-only workspace marker survives metadata and binding changes; a board
|
|
reopen does not turn it into approval. Implicit trusted local defaults and explicit
|
|
SSH remote provisioning retain their existing placement; no new sandbox provision transport
|
|
is claimed. The implicit default follows task creation: configured policy pointer,
|
|
then primary source, creation time and ID. A missing or foreign configured source
|
|
does not authorize controller hooks. This precedence neither changes existing
|
|
creation validation nor rotates bindings or clears retained copy-only markers.
|
|
Closed-worktree reopening checks current task, agent, organizational and source policies without assuming a future driver. Effective low-trust reopens suppress configured and built-in host scripts. Returned checkpoints must have directory parents for every nested entry before merging, including unchanged entries; invalid output leaves retained host files intact and keeps recovery evidence.
|
|
|
|
Skipping the host scan for a fully unchanged returned snapshot grants no new
|
|
access. The merge still holds its canonical writer lock and runs both authority
|
|
and publication hooks. Denials still propagate, and checkpoint/archive validation
|
|
is unchanged. [Regression coverage](../../packages/adapter-utils/src/workspace-restore-unchanged.test.ts)
|
|
is source-level evidence only.
|
|
Child creation by an admitted agent checks an explicit prospective-task context before the child is saved. The creator run must have an authoritative saved policy and a verified company/agent/parent lineage. Current parent, prospective child and physical source policies must fit that boundary. This creation path does not relax exact task/run checks for existing-task writes. The [child trust regression](../../server/src/__tests__/task-workspace-child-trust.test.ts) is automated source-level evidence, not live agent qualification.
|
|
Source review for these additions: PR #15749 and
|
|
[task workspace routes](../../server/src/routes/task-workspaces.ts),
|
|
[native authority](../../server/src/services/native-runtime/paperclip-runner-tool-authority.ts),
|
|
and [semantic transport](../../server/src/services/task-workspace-tools.ts).
|
|
|
|
Evidence: `source-only` —
|
|
[implementation](../../server/src/routes/workspace-runtime-service-authz.ts#L76) at the
|
|
reviewed commit above.
|
|
|
|
Verification: `not-run` — exercise a listed permitted operation with actual agent
|
|
credentials/run context; inspect its result and applicable persistence/attribution.
|
|
Assert the relevant denied actor, owner, company or mode from Policy. Review the
|
|
remaining operations before qualifying the whole capability.
|
|
|
|
Gaps: Run/task context identifies its actual realized workspace; record existence is not
|
|
a running service.
|
|
|
|
## `provisioning`
|
|
|
|
Source preparation keeps its dedicated lock connection active during work on
|
|
other connections. It preserves company/source exclusion and existing admission
|
|
authority; it adds no agent API or permission. The
|
|
[source-lock regression](../../server/src/services/isolated-remote-workspace-source-lock.test.ts)
|
|
covers PostgreSQL idle timeout, competing preparation, ordinary pool availability,
|
|
and failure cleanup. Deployed agent recovery requires separate live evidence.
|
|
|
|
Review: `baseline`.
|
|
|
|
API: `present` — representative operation IDs:
|
|
|
|
- `GET /api/execution-workspaces/{id}`.
|
|
- `GET /api/execution-workspaces/{id}/workspace-operations`.
|
|
- `POST /api/issues/{id}/workspace/repositories` requests repository preparation.
|
|
|
|
Agent access: `partial` — REST / native call_api reads; task/project assignment triggers
|
|
server provisioning.
|
|
|
|
Native `prepare_repository` and admitted legacy REST callers can request a
|
|
catalog repository or validated repository URL for their current task.
|
|
Preparation occurs at normal admission; the agent cannot supply a host path or
|
|
controller identity. Current source permissions, responsible-user intersection,
|
|
trust restrictions, immutable receipts, and retained physical-root ownership are
|
|
checked before acquisition and publication. Low-trust callers cannot trigger a
|
|
new controller-side Git acquisition. Evidence remains source-only here; focused
|
|
[repository tests](../../server/src/__tests__/task-workspace-repositories-trust.test.ts)
|
|
are separate from live agent qualification.
|
|
|
|
Policy: Company/project/workspace visibility and runtime authority; active run, trust
|
|
preset and task ownership where required.
|
|
|
|
Evidence: `source-only` —
|
|
[implementation](../../server/src/routes/workspace-runtime-service-authz.ts#L76) at the
|
|
reviewed commit above.
|
|
|
|
Verification: `not-run` — exercise a listed permitted operation with actual agent
|
|
credentials/run context; inspect its result and applicable persistence/attribution.
|
|
Assert the relevant denied actor, owner, company or mode from Policy. Review the
|
|
remaining operations before qualifying the whole capability.
|
|
|
|
Gaps: Observing readiness exists. Creating host commands or arbitrary provision targets
|
|
is a separate protected operation.
|
|
|
|
## `services`
|
|
|
|
Review: `baseline`.
|
|
|
|
API: `present` — representative operation IDs:
|
|
|
|
- `POST /api/execution-workspaces/{id}/runtime-services/{action}`.
|
|
|
|
Agent access: `available` — Authorized agent REST / native `call_api` / CLI runtime
|
|
service controls and existing stdio MCP controls.
|
|
|
|
Policy: Company/project/workspace visibility and runtime authority; active run, trust
|
|
preset and task ownership where required.
|
|
|
|
Evidence: `source-only` —
|
|
[implementation](../../server/src/routes/workspace-runtime-service-authz.ts#L76) at the
|
|
reviewed commit above.
|
|
|
|
Verification: `not-run` — exercise a listed permitted operation with actual agent
|
|
credentials/run context; inspect its result and applicable persistence/attribution.
|
|
Assert the relevant denied actor, owner, company or mode from Policy. Review the
|
|
remaining operations before qualifying the whole capability.
|
|
|
|
Gaps: No dedicated production control_workspace_service binding. Generic native
|
|
calls retain runtime trust and workspace/task scope checks. Optional MCP availability
|
|
must be recorded.
|
|
|
|
## `files-git`
|
|
|
|
Review: `baseline`.
|
|
|
|
API: `present` — representative operation IDs:
|
|
|
|
- `GET /api/issues/{issueId}/file-resources/content`.
|
|
- `GET /api/issues/{issueId}/file-resources/list`.
|
|
|
|
Agent access: `partial` — Authorized workspace filesystem tools; Dot workspace bridge
|
|
when enabled; board file-resource HTTP UI.
|
|
|
|
Policy: Company/project/workspace visibility and runtime authority; active run, trust
|
|
preset and task ownership where required.
|
|
|
|
Evidence: `source-only` —
|
|
[implementation](../../server/src/routes/workspace-runtime-service-authz.ts#L76) at the
|
|
reviewed commit above.
|
|
|
|
Verification: `not-run` — exercise a listed permitted operation with actual agent
|
|
credentials/run context; inspect its result and applicable persistence/attribution.
|
|
Assert the relevant denied actor, owner, company or mode from Policy. Review the
|
|
remaining operations before qualifying the whole capability.
|
|
|
|
Gaps: Files panel HTTP requires board. Agents can use their admitted workspace file
|
|
tools; remote/Dot scope and Git command availability differ.
|
|
|
|
File reads also enforce retained source privacy after local copy, configuration
|
|
move/removal, reopen, and project deletion. A task assignment or repository
|
|
receipt does not grant access to private files from another physical owner.
|
|
|
|
Authorized Git-source capture also retains ownership on the actual shared Git
|
|
storage. Source checks use verified checkout identities, including company-known
|
|
owners when separate metadata has no primary-checkout registration. Removing that
|
|
checkout does not release its private committed files in surviving worktrees.
|
|
|
|
## `closure`
|
|
|
|
Review: `baseline`.
|
|
|
|
API: `present` — representative operation IDs:
|
|
|
|
- `PATCH /api/execution-workspaces/{id}`.
|
|
- `GET /api/execution-workspaces/{id}/close-readiness`.
|
|
- `POST /api/execution-workspaces/{id}/reconcile-branch`.
|
|
|
|
Agent access: `partial` — REST / native fallback for permitted update; CLI workspace;
|
|
reconcile-branch requires board.
|
|
|
|
Policy: Company/project/workspace visibility and runtime authority; active run, trust
|
|
preset and task ownership where required.
|
|
|
|
Evidence: `source-only` —
|
|
[implementation](../../server/src/routes/workspace-runtime-service-authz.ts#L76) at the
|
|
reviewed commit above.
|
|
|
|
Verification: `not-run` — exercise a listed permitted operation with actual agent
|
|
credentials/run context; inspect its result and applicable persistence/attribution.
|
|
Assert the relevant denied actor, owner, company or mode from Policy. Review the
|
|
remaining operations before qualifying the whole capability.
|
|
|
|
Gaps: Closure/readiness exists; not every workspace can be reopened, and branch
|
|
reconciliation has a board boundary.
|