Files
PaperClipAI/maps/agent-access-map/workspaces.md
T
DottaandPaperclip 24067a16b5 fix(workspaces): classify restricted shared repository admission (#16895)
## Thinking Path

> - Paperclip manages agents, their tasks, and their saved work.
> - Workspace preparation must check access before an agent starts.
> - A shared project checkout can contain another task's private
worktree.
> - The access guard correctly rejects a run that cannot read that work.
> - The isolated-source path reports this as a configuration blocker
after #16545.
> - Shared project repository admission missed that classification and
returned a generic setup failure.
> - This change uses the same blocker at that boundary so recovery names
the required workspace repair.

## Linked Issues or Issue Description

Refs #16545 and #16095. The open retained-reader repair in #16832
addresses deleted identities, a separate condition.

**What happened?**

A task reused a shared project workspace. Another worktree in that
checkout retained a private task that the run could not read. Repository
admission rejected access before provider startup. The run reported
`setup_failed`, and recovery showed a generic no-live-execution notice.

**Expected behavior**

Keep the access denial. Report `configuration_incomplete` with reason
`workspace_source_restricted`. Preserve the workspace and files. Give
the owner the existing workspace repair action. Do not queue an
automatic failure retry or report this expected blocker as an unexpected
Sentry failure.

**Steps to reproduce**

1. Bind a task to a shared project checkout.
2. Add a private task worktree beneath that checkout.
3. Continue the shared task as an agent that cannot read the private
task.
4. Inspect the run result and recovery action.

**Version and deployment**

Reproduced in the full heartbeat service from source revision
`06d4fe3121`, with PostgreSQL and real Git worktrees on macOS. The
provider never starts. The regression also covers the existing
Git-worktree path.

## What Changed

- Apply the existing restricted-source classifier to task repository
admission.
- Extend the heartbeat regression to shared project workspaces.
- Assert one run and one recovery action, no provider start, no
unexpected Sentry report, and preserved binding and files.
- Update the workspace documentation and both capability maps.

## Verification

- Red: the shared project regression returned `setup_failed`; the
Git-worktree control passed.
- Green: the same two cases pass after the change.
- `node scripts/check-maps.mjs` and `git diff --check` passed.
- Current head `e26cdd45da76c6edaa1622856f9c69e9f48b81fb`: 55 successful
checks, one neutral check, two skipped Storybook checks. Greptile 5/5,
no review threads, mergeable.
- All 337 tests passed across `heartbeat-workspace-branch-containment`,
`heartbeat/workspace-preparation`, `run-failure-report`, and
`recovery/stranded-notice`.
- `pnpm -r typecheck` and `pnpm build` passed. All full current-head CI
suites passed in [run
39315969013](https://github.com/paperclipai/paperclip/actions/runs/39315969013).
The duplicate local full Vitest run was stopped after 11 minutes when CI
completed; it has no local full-suite completion claim. The 337 affected
local tests completed successfully.
- Running-app acceptance at `e26cdd45da76c6edaa1622856f9c69e9f48b81fb`:
a synthetic private Git worktree caused the shared task to stop with the
visible Workspace source restricted notice and no process start. The
normal workspace API selected a separate task directory. Resolving the
recovery action resumed the same assignee. A real process fixture
completed, the output persisted after reload, and both source files
remained intact. Checkout and execution locks cleared. There were no
automatic failure retries.
- The first recovered process wrote its report to the command cwd. This
was a fixture output-path mistake. The corrected fixture reads the
selected workspace from the authenticated API before writing. One
explicit additional admission verified that output. The record retains
both successful process runs and the earlier configuration-blocked run.
This is local process-adapter acceptance, not live Codex/provider
qualification.
- Staging diagnosis confirmed a retained private source and failure
before provider startup. The original task has not been recovered. The
deployed UI hides its workspace selector, and no authenticated
task-write API is available in this session. No live Codex recovery is
claimed.

## Risks

The same exact 403 denial is now a configuration blocker during shared
repository admission. Other failures retain their original type. Access
policy, cloning, credentials, provider execution, and workspace contents
do not change. An existing task still requires a permitted workspace
repair before it can run.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, code execution, and browser
tools. The exact deployment identifier 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
- [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>
2026-10-19 16:03:16 -05:00

14 KiB

Execution workspaces, services, and files — agent access

Product map: Execution workspaces, services, and files.

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.

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}/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 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. A retained denial blocks the task as configuration_incomplete (workspace_source_restricted) before dispatch; it grants no access and names the owner action on the task. 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. 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/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 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.