mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-22 00:30:19 +02:00
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Native runs must preserve files when Stop ends a turn before its result is saved. > - The cancellation path could skip file retention and leave a prepared remote workspace active. > - An older controller could also admit an extra run while an earlier lost-source hold was unresolved. > - Rejecting every later run then prevented recovery even after that extra run was stopped and its files were reconciled. > - This change retains cancelled workspaces and records the exact proof for a safely reconciled intervening cancellation. > - Recovery can proceed without changing cancelled history, replaying actions, or weakening execution gates. ## Linked Issues or Issue Description Refs: #16583, #16690, #16696. **What happened?** Stop could acknowledge a native turn without recording its unsaved file obligation. Reconstruction also rejected a later cancelled run after its exact environment and files had been reconciled. The task could remain inactive with an `in_progress` status, which the reconstruction endpoint rejected. **Expected behavior** Retain unsaved files after Stop. Permit a board operator to reconstruct from saved sources only after each intervening cancellation has a confirmed stop and a file-only repair record. Preserve the original failed export and the cancelled run. **Steps to reproduce** 1. Prepare a native workspace and cancel the turn before a structured result exists. 2. Inspect its file retention and remote lease state. 3. For a task with an older destroyed export, reconcile the later cancelled run's exact files and unperformed actions. 4. Request reconstruction from verified same-task attachments. ## What Changed - Pin prepared native files before cancellation settlement. Defer teardown if pinning fails. Use the existing stop-only cleanup worker. - Repair active warm and ephemeral sources, plus stopping and confirmed-stopped historical cancellations in bounded startup and periodic batches. Preserve the existing exact-source checks. Bound row-lock waits and advance past a busy source. - Accept only same-owner native run-only cancellations with no result or current coordinator, confirmed stopped leases, and board file-only repair with `not_performed` action outcomes. - Save hashes of each intervening repair and recheck them at queue claim and final dispatch. Reject changed evidence or another later run. - Expose saved-source reconstruction in the issue API for inactive `blocked` and `in_progress` tasks, and hide it when an execution or checkout owner exists. Permit the same inactive source at submission. Keep the source, owner, version, attachment, budget, pause and retry checks. - Update the shared evidence type, recovery runbook and capability maps. Add an optional cancelled-native no-action confirmation to file recovery, resetting evidence when its source changes. No migration, new endpoint, permission or dependency. ## Verification - The new fixture fails on the original validator with `workspace_reconstruction_stale` after valid cancelled-run file repair. - Focused source and Stop retention tests: 94 passed before the final contention case. The subsequent three-suite run passed 123 tests, including real PostgreSQL lock contention and public startup/API/final-dispatch paths. The final narrow-query rerun passed all 123 tests. Review correction: all 143 tests passed across four suites, including real lock contention through the teardown boundary and stopping/stopped allocations. - Six broader cancellation, executor, heartbeat and restart suites: 831 passed, 9 existing skips. - Final correction: all 145 backend tests and all 36 recovery UI tests passed. Real contention covers both warm reusable and ephemeral sources. Optional no-action confirmation defaults to uncertified, and changing source clears evidence. Workspace `pnpm -r typecheck` and token gates passed on these sources. - Maps, module boundaries and Git whitespace checks: passed. Full `pnpm build` passed on the final `4e8c0e73ac` revision as well as the prior reviewed heads. A full local run of the preceding fix completed with 21,724 passes, 109 skips and three failures in webhook body limits, a status-card timeout, and a port-allocation fixture. All three suites then passed unchanged in an isolated rerun (209 tests). The full local run remains recorded as failed; the final head's full CI passed (55 completed checks, no failures). A fresh full local run is also in progress on the frozen reviewed head. - Preserved failures: the first attempt lacked a built plugin SDK; after building prerequisites, the regression reached the expected validator rejection. Treatment tests caught the file-only action's deliberately null continuation owner. A real lock test then caught the database driver's wrapped timeout error. Both defects were corrected and retested. Independent review then found a teardown race after a failed retention write. A second review found active warm sources excluded by the historical policy filter; the regression reproduced both warm failures before correcting the query. Its real-row-lock fixture reproduced the failure; the fixed release guard and historical stopping/stopped cases pass. No checks were weakened. - A pre-deployment walkthrough caught the issue API still hiding reconstruction for an inactive `in_progress` task. The status/capability baseline reproduced three failures, including inappropriate capability flags for execution/checkout owners. The fix uses the same inactive-state requirements as submission; its positive fixture reads the assembled issue API before reconstructing. All 117 tests in those three recovery suites pass, and all 28 heartbeat-recovery tests pass separately. Full workspace typecheck passed again. Full build passed with the exact `4e8c0e73ac` stamp; latest-head independent review is 5/5 with no open findings. All 55 latest-head checks completed with no failures. - Live tenant acceptance remains pending. No successful reconstruction, export or warm-process proof is claimed by these local tests. ## Risks - Stop retains unfinished work, so storage remains until file recovery and explicit cleanup complete. - Reconstruction remains held for unknown or performed external actions, foreign owners, live or rebound environments, accepted results, changed evidence, and unrelated later runs. - The sweep examines at most five candidates per pass. It uses a fair cursor and bounded transaction waits. It performs no provider call and starts no new turn. - Intervening verification reads only the needed JSON fields. The source transaction still serializes recovery for the same task. ## Model Used OpenAI Codex, GPT-6 family, with reasoning, tool use and code execution. The session does not expose an exact backend model ID or context-window size. ## 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>
253 lines
14 KiB
Markdown
253 lines
14 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 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.
|
|
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`
|
|
|
|
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.
|