## Thinking Path > - Paperclip manages AI agents and their task workspaces. > - Isolated Git worktrees and private repository copies remain on the host after tasks finish. > - These files can fill the disk even when every commit is available upstream. > - Cleanup must retain unique files, local Git state, live work, and recovery data. > - This pull request records and verifies an exact-commit restoration recipe before expiration. > - Normal reopening restores that commit even after upstream advances. > - The benefit is lower host disk use while finished tasks remain available for later work. ## Linked Issues or Issue Description Refs #10285 and #2022. Follows #16433 for skill-cache expiration. Related #11936 provides separate host-local cleanup tooling; this change extends the existing company-scoped lifecycle worker and normal workspace reopening. ROADMAP.md does not specify a competing implementation. ## What Changed - Add opt-in expiration for verified Paperclip-owned Git worktrees after their whole task tree has been done or cancelled for 24 hours. - Preserve active and scheduled runs, previews, pending reopening, unresolved recovery, dirty files, unknown ignored files, and unsupported or unique Git state. - Verify an authenticated fetch in a fresh object store. Save repository identity, exact commit, branch, and ownership before any deletion. - Rename eligible files into recorded managed trash. Resume interrupted cleanup under source and lifecycle locks. - Expire private base clones only after consumer, file, reference, reflog, and object checks pass. Preserve shared project base clones. - Rebuild missing bases and worktrees through normal reopening at the recorded commit. Return an explicit retryable error when repository access fails. - Add independent workspace/base switches, dry-run reports, reclaimed-byte counts, and operational documentation. No table, public API, UI, or recovery-retention change. ## Verification - Real local bare remote plus Postgres: create at A, advance upstream to B, expire the worktree and private base, reopen at A, then make and retain a subsequent commit. - Final lifecycle suite: all 20 tests passed in 46.41 seconds at `d72947a2e45be5d0c912debb03c46b15863b4798`. Review fixes cover second-cycle expiration, newer-branch recovery, normal preparation after restoration, branch adoption, and Git administration path controls. Eleven existing branch reconciliation controls also passed. Covers unique files and Git state, live descendants, scheduled runs, recovery, previews, unauthorized access, upstream failure, concurrent reopening, interrupted deletion, shared bases, and nested private worktrees. - Final guard regressions: 3 affected tests passed. Existing workspace/runtime/preparation/source controls: 328 tests passed in 238.69 seconds. - Full repository typecheck and build passed locally. The full local test attempt did not pass: initial native-session cases hit macOS Postgres shared-memory exhaustion, source changes invalidated two discovery cases, and the checkout disappeared near the end. The final-code retry of the remaining skill and unrelated controls passed all 103 tests. All checks passed on this exact head, including typecheck, general and serialized tests, Runner checks, build, canary dry-run, browser E2E, and security scans. Greptile reviewed the head at 5/5 with all findings resolved. - Fixed a blocking CI policy false positive for `docker-compose-secrets.test.mjs`. The test still rejects GitHub secret expressions, including dot, bracket, and multiline forms. Its full 15-test suite passes. - Map validation and `git diff --check` passed. - Staging dry-run inspection, measured reclamation, and ordinary product acceptance remain pending. No live acceptance is claimed. ## Risks - Git restoration depends on continued upstream availability and credentials. Cleanup skips unverified commits; restoration never substitutes the default branch. - File deletion requires multiple conservative proofs. Unknown or unsupported state is retained and can limit reclamation. - Source and lifecycle locks serialize preparation, reopening, and expiration. Remote verification has a deadline but can delay work on the same source. - Start with both expiration switches disabled. When enabled, dry-run remains the default. Rollback disables further deletion while retaining restoration support. ## Model Used OpenAI GPT-6 through Codex, with reasoning, repository inspection, tool use, and code execution. The exact backend model ID and context-window size 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 (focused tests; full local limitation documented above) - [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>
13 KiB
Execution workspaces, services, and files — agent access
Product map: Execution workspaces, services, and files.
Review date: 2026-10-12. Source commit: 58e16dbddfc9c8d0cf60867f9d9847f0feda055b.
Native REST transport reconciled in this change; see the
shared transport contract.
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 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}/workspaceinspects the durable task binding.PUT /api/issues/{id}/workspacerequests the next binding.GET /api/companies/{companyId}/execution-workspaces?summary=true&selectableForTask=truelists 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 a 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. 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 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.
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 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 is automated source-level evidence, not live agent qualification. Source review for these additions: PR #15749 and task workspace routes, native authority, and semantic transport.
Evidence: source-only —
implementation 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
Review: baseline.
API: present — representative operation IDs:
GET /api/execution-workspaces/{id}.GET /api/execution-workspaces/{id}/workspace-operations.POST /api/issues/{id}/workspace/repositoriesrequests 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
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 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 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 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 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.