24 KiB
Workspace Technical Implementation Spec
Role of This Document
This document translates workspace-product-model-and-work-product.md into an implementation-ready engineering plan.
It is intentionally concrete:
- schema and migration shape
- shared contract updates
- route and service changes
- UI changes
- rollout and compatibility rules
This is the implementation target for the first workspace-aware delivery slice.
Locked Decisions
These decisions are treated as settled for this implementation:
- Add a new durable
execution_workspacestable now. - Each issue has at most one current execution workspace at a time.
issuesget explicitproject_workspace_idandexecution_workspace_id.- Workspace reuse is in scope for V1.
- The feature is gated in the UI by
/instance/settings > Experimental > Workspaces. - The gate is UI-only. Backend model changes and migrations always ship.
- Existing users upgrade into compatibility-preserving defaults.
project_workspacesevolves in place rather than being replaced.- Work product is issue-first, with optional links to execution workspaces and runtime services.
- GitHub is the only PR provider in the first slice.
- Both
adapter_managedandcloud_sandboxexecution modes are in scope. - Workspace controls ship first inside existing project properties, not in a new global navigation area.
- Subissues are out of scope for this implementation slice.
Non-Goals
- Building a full code review system
- Solving subissue UX in this slice
- Implementing reusable shared workspace definitions across projects in this slice
- Reworking all current runtime service behavior before introducing execution workspaces
Existing Baseline
The repo already has:
project_workspacesprojects.execution_workspace_policyissues.execution_workspace_settings- runtime service persistence in
workspace_runtime_services - local git-worktree realization in
workspace-runtime.ts
This implementation should build on that baseline rather than fork it.
Terminology
Project workspace: durable configured codebase/root for a projectExecution workspace: actual runtime workspace used for one or more issuesWork product: user-facing output such as PR, preview, branch, commit, artifact, documentRuntime service: process or service owned or tracked for a workspaceCompatibility mode: existing behavior preserved for upgraded installs with no explicit workspace opt-in
Architecture Summary
The first slice should introduce three explicit layers:
-
Project workspace- existing durable project-scoped codebase record
- extended to support local, git, non-git, and remote-managed shapes
-
Execution workspace- new durable runtime record
- represents shared, isolated, operator-branch, or remote-managed execution context
-
Issue work product- new durable output record
- stores PRs, previews, branches, commits, artifacts, and documents
The issue remains the planning and ownership unit. The execution workspace remains the runtime unit. The work product remains the deliverable/output unit.
Configuration and Deployment Topology
Important correction
This repo already uses PAPERCLIP_DEPLOYMENT_MODE for auth/deployment behavior (local_trusted | authenticated).
Do not overload that variable for workspace execution topology.
New env var
Add a separate execution-host hint:
PAPERCLIP_EXECUTION_TOPOLOGY=local|cloud|hybrid
Default:
- if unset, treat as
local
Purpose:
- influences defaults and validation for workspace configuration
- does not change current auth/deployment semantics
- does not break existing installs
Semantics
local- Paperclip may create host-local worktrees, processes, and paths
cloud- Paperclip should assume no durable host-local execution workspace management
- adapter-managed and cloud-sandbox flows should be treated as first-class
hybrid- both local and remote execution strategies may exist
This is a guardrail and defaulting aid, not a hard policy engine in the first slice.
Instance Settings
Add a new Experimental section under /instance/settings.
New setting
experimental.workspaces: boolean
Rules:
- default
false - UI-only gate
- stored in instance config or instance settings API response
- backend routes and migrations remain available even when false
UI behavior when off
- hide workspace-specific issue controls
- hide workspace-specific project configuration
- hide issue
Work Producttab if it would otherwise be empty - do not remove or invalidate any stored workspace data
Data Model
1. Extend project_workspaces
Current table exists and should evolve in place.
New columns
source_type text not null default 'local_path'local_path | git_repo | non_git_path | remote_managed
default_ref text nullvisibility text not null default 'default'default | advanced
setup_command text nullcleanup_command text nullremote_provider text null- examples:
github,openai,anthropic,custom
- examples:
remote_workspace_ref text nullshared_workspace_key text null- reserved for future cross-project shared workspace definitions
Backfill rules
- if existing row has
repo_url, backfillsource_type='git_repo' - else if existing row has
cwd, backfillsource_type='local_path' - else backfill
source_type='remote_managed' - copy existing
repo_refintodefault_ref
Indexes
- retain current indexes
- add
(project_id, source_type) - add
(company_id, shared_workspace_key)non-unique for future support
2. Add execution_workspaces
Create a new durable table.
Columns
id uuid pkcompany_id uuid not nullproject_id uuid not nullproject_workspace_id uuid nullsource_issue_id uuid nullmode text not nullshared_workspace | isolated_workspace | operator_branch | adapter_managed | cloud_sandbox
strategy_type text not nullproject_primary | git_worktree | adapter_managed | cloud_sandbox
name text not nullstatus text not null default 'active'active | idle | in_review | archived | cleanup_failed
cwd text nullrepo_url text nullbase_ref text nullbranch_name text nullprovider_type text not null default 'local_fs'local_fs | git_worktree | adapter_managed | cloud_sandbox
provider_ref text nullderived_from_execution_workspace_id uuid nulllast_used_at timestamptz not null default now()opened_at timestamptz not null default now()closed_at timestamptz nullcleanup_eligible_at timestamptz nullcleanup_reason text nullmetadata jsonb nullcreated_at timestamptz not null default now()updated_at timestamptz not null default now()
Foreign keys
company_id -> companies.idproject_id -> projects.idproject_workspace_id -> project_workspaces.id on delete set nullsource_issue_id -> issues.id on delete set nullderived_from_execution_workspace_id -> execution_workspaces.id on delete set null
Indexes
(company_id, project_id, status)(company_id, project_workspace_id, status)(company_id, source_issue_id)(company_id, last_used_at desc)(company_id, branch_name)non-unique
3. Extend issues
Add explicit workspace linkage.
New columns
project_workspace_id uuid nullexecution_workspace_id uuid nullexecution_workspace_preference text nullinherit | shared_workspace | isolated_workspace | operator_branch | reuse_existing
Foreign keys
project_workspace_id -> project_workspaces.id on delete set nullexecution_workspace_id -> execution_workspaces.id on delete set null
Backfill rules
- all existing issues get null values
- null should be interpreted as compatibility/inherit behavior
Invariants
- if
project_workspace_idis set, it must belong to the issue's project and company - if
execution_workspace_idis set, it must belong to the issue's company - if
execution_workspace_idis set, the referenced workspace'sproject_idmust match the issue'sproject_id
4. Add issue_work_products
Create a new durable table for outputs.
Columns
id uuid pkcompany_id uuid not nullproject_id uuid nullissue_id uuid not nullexecution_workspace_id uuid nullruntime_service_id uuid nulltype text not nullpreview_url | runtime_service | pull_request | branch | commit | artifact | document
provider text not nullpaperclip | github | vercel | s3 | custom
external_id text nulltitle text not nullurl text nullstatus text not nullactive | ready_for_review | approved | changes_requested | merged | closed | failed | archived
review_state text not null default 'none'none | needs_board_review | approved | changes_requested
is_primary boolean not null default falsehealth_status text not null default 'unknown'unknown | healthy | unhealthy
summary text nullmetadata jsonb nullcreated_by_run_id uuid nullcreated_at timestamptz not null default now()updated_at timestamptz not null default now()
Foreign keys
company_id -> companies.idproject_id -> projects.id on delete set nullissue_id -> issues.id on delete cascadeexecution_workspace_id -> execution_workspaces.id on delete set nullruntime_service_id -> workspace_runtime_services.id on delete set nullcreated_by_run_id -> heartbeat_runs.id on delete set null
Indexes
(company_id, issue_id, type)(company_id, execution_workspace_id, type)(company_id, provider, external_id)(company_id, updated_at desc)
5. Extend workspace_runtime_services
This table already exists and should remain the system of record for owned/tracked services.
New column
execution_workspace_id uuid null
Foreign key
execution_workspace_id -> execution_workspaces.id on delete set null
Behavior
- runtime services remain workspace-first
- issue UIs should surface them through linked execution workspaces and work products
Shared Contracts
1. packages/shared
Update project workspace types and validators
Add fields:
sourceTypedefaultRefvisibilitysetupCommandcleanupCommandremoteProviderremoteWorkspaceRefsharedWorkspaceKey
Add execution workspace types and validators
New shared types:
ExecutionWorkspaceExecutionWorkspaceModeExecutionWorkspaceStatusExecutionWorkspaceProviderType
Add work product types and validators
New shared types:
IssueWorkProductIssueWorkProductTypeIssueWorkProductStatusIssueWorkProductReviewState
Update issue types and validators
Add:
projectWorkspaceIdexecutionWorkspaceIdexecutionWorkspacePreferenceworkProducts?: IssueWorkProduct[]
Extend project execution policy contract
Replace the current narrow policy with a more explicit shape:
enableddefaultModeshared_workspace | isolated_workspace | operator_branch | adapter_default
allowIssueOverridedefaultProjectWorkspaceIdworkspaceStrategybranchPolicypullRequestPolicyruntimePolicycleanupPolicy
Do not try to encode every possible provider-specific field in V1. Keep provider-specific extensibility in nested JSON where needed.
Service Layer Changes
1. Project service
Update project workspace CRUD to handle the extended schema.
Required rules
- when setting a primary workspace, clear
is_primaryon siblings source_type=remote_managedmay have nullcwd- local/git-backed workspaces should still require one of
cwdorrepo_url - preserve current behavior for existing callers that only send
cwd/repoUrl/repoRef
2. Issue service
Update create/update flows to handle explicit workspace binding.
Create behavior
Resolve defaults in this order:
- explicit
projectWorkspaceIdfrom request project.executionWorkspacePolicy.defaultProjectWorkspaceId- project's primary workspace
- null
Resolve executionWorkspacePreference:
- explicit request field
- project policy default
- compatibility fallback to
inherit
Do not create an execution workspace at issue creation time unless:
reuse_existingis explicitly chosen andexecutionWorkspaceIdis provided
Otherwise, workspace realization happens when execution starts.
Update behavior
- allow changing
projectWorkspaceIdonly if the workspace belongs to the same project - allow setting
executionWorkspaceIdonly if it belongs to the same company and project - do not automatically destroy or relink historical work products when workspace linkage changes
3. Workspace realization service
Refactor workspace-runtime.ts so realization produces or reuses an execution_workspaces row.
New flow
Input:
- issue
- project workspace
- project execution policy
- execution topology hint
- adapter/runtime configuration
Output:
- realized execution workspace record
- runtime cwd/provider metadata
Required modes
shared_workspace- reuse a stable execution workspace representing the project primary/shared workspace
isolated_workspace- create or reuse a derived isolated execution workspace
operator_branch- create or reuse a long-lived branch workspace
adapter_managed- create an execution workspace with provider references and optional null
cwd
- create an execution workspace with provider references and optional null
cloud_sandbox- same as adapter-managed, but explicit remote sandbox semantics
Reuse rules
When reuse_existing is requested:
- only list active or recently used execution workspaces
- only for the same project
- only for the same project workspace if one is specified
- exclude archived and cleanup-failed workspaces
Shared workspace realization
For compatibility mode and shared-workspace projects:
- create a stable execution workspace per project workspace when first needed
- reuse it for subsequent runs
This avoids a special-case branch in later work product linkage.
4. Runtime service integration
When runtime services are started or reused:
- populate
execution_workspace_id - continue populating
project_workspace_id,project_id, andissue_id
When a runtime service yields a URL:
- optionally create or update a linked
issue_work_productsrow of typeruntime_serviceorpreview_url
5. PR and preview reporting
Add a service for creating/updating issue_work_products.
Supported V1 product types
pull_requestpreview_urlruntime_servicebranchcommitartifactdocument
GitHub PR reporting
For V1, GitHub is the only provider with richer semantics.
Supported statuses:
draftready_for_reviewapprovedchanges_requestedmergedclosed
Represent these in status and review_state rather than inventing a separate PR table in V1.
Routes and API
1. Project workspace routes
Extend existing routes:
GET /projects/:id/workspacesPOST /projects/:id/workspacesPATCH /projects/:id/workspaces/:workspaceIdDELETE /projects/:id/workspaces/:workspaceId
New accepted/returned fields
sourceTypedefaultRefvisibilitysetupCommandcleanupCommandremoteProviderremoteWorkspaceRef
2. Execution workspace routes
Add:
GET /companies/:companyId/execution-workspaces- filters:
projectIdprojectWorkspaceIdstatusissueIdreuseEligible=true
- filters:
GET /execution-workspaces/:idPATCH /execution-workspaces/:id- update status/metadata/cleanup fields only in V1
Do not add top-level navigation for these routes yet.
3. Work product routes
Add:
GET /issues/:id/work-productsPOST /issues/:id/work-productsPATCH /work-products/:idDELETE /work-products/:id
V1 mutation permissions
- board can create/update/delete all
- agents can create/update for issues they are assigned or currently executing
- deletion should generally archive rather than hard-delete once linked to historical output
4. Issue routes
Extend existing create/update payloads to accept:
projectWorkspaceIdexecutionWorkspacePreferenceexecutionWorkspaceId
Extend GET /issues/:id to return:
projectWorkspaceIdexecutionWorkspaceIdexecutionWorkspacePreferencecurrentExecutionWorkspaceworkProducts[]
5. Instance settings routes
Add support for:
- reading/writing
experimental.workspaces
This is a UI gate only.
If there is no generic instance settings storage yet, the first slice can store this in the existing config/instance settings mechanism used by /instance/settings.
UI Changes
1. /instance/settings
Add section:
ExperimentalEnable Workspaces
When off:
- hide new workspace-specific affordances
- do not alter existing project or issue behavior
2. Project properties
Do not create a separate Code tab yet.
Ship inside existing project properties first.
Add or re-enable sections
Project WorkspacesExecution DefaultsProvisioningPull RequestsPreviews and RuntimeCleanup
Display rules
- only show when
experimental.workspaces=true - keep wording generic enough for local and remote setups
- only show git-specific fields when
sourceType=git_repo - only show local-path-specific fields when not
remote_managed
3. Issue create dialog
When the workspace experimental flag is on and the selected project has workspace automation or workspaces:
Basic fields
Codebase- select from project workspaces
- default to policy default or primary workspace
Execution modeProject defaultShared workspaceIsolated workspaceOperator branch
Advanced section
Reuse existing execution workspace
This control should query only:
- same project
- same codebase if selected
- active/recent workspaces
- compact labels with branch or workspace name
Do not expose all execution workspaces in a noisy unfiltered list.
4. Issue detail
Add a Work Product tab when:
- the experimental flag is on, or
- the issue already has work products
Show
- current execution workspace summary
- PR cards
- preview cards
- branch/commit rows
- artifacts/documents
Add compact header chips:
- codebase
- workspace
- PR count/status
- preview status
5. Execution workspace detail page
Add a detail route but no nav item.
Linked from:
- issue work product tab
- project workspace/execution panels
Show
- identity and status
- project workspace origin
- source issue
- linked issues
- branch/ref/provider info
- runtime services
- work products
- cleanup state
Runtime and Adapter Behavior
1. Local adapters
For local adapters:
- continue to use existing cwd/worktree realization paths
- persist the result as execution workspaces
- attach runtime services and work product to the execution workspace and issue
2. Remote or cloud adapters
For remote adapters:
- allow execution workspaces with null
cwd - require provider metadata sufficient to identify the remote workspace/session
- allow work product creation without any host-local process ownership
Examples:
- cloud coding agent opens a branch and PR on GitHub
- Vercel preview URL is reported back as a preview work product
- remote sandbox emits artifact URLs
3. Approval-aware PR workflow
V1 should support richer PR state tracking, but not a full review engine.
Required actions
open_prmark_ready
Required review states
draftready_for_reviewapprovedchanges_requestedmergedclosed
Storage approach
- represent these as
issue_work_productswithtype='pull_request' - use
statusandreview_state - store provider-specific details in
metadata
Migration Plan
1. Existing installs
The migration posture is backward-compatible by default.
Guarantees
- no existing project must be edited before it keeps working
- no existing issue flow should start requiring workspace input
- all new nullable columns must preserve current behavior when absent
2. Project workspace migration
Migrate project_workspaces in place.
Backfill
- derive
source_type - copy
repo_reftodefault_ref - leave new optional fields null
3. Issue migration
Do not backfill project_workspace_id or execution_workspace_id on all existing issues.
Reason:
- the safest migration is to preserve current runtime behavior and bind explicitly only when new workspace-aware flows are used
Interpret old issues as:
executionWorkspacePreference = inherit- compatibility/shared behavior
4. Runtime history migration
Do not attempt a perfect historical reconstruction of execution workspaces in the migration itself.
Instead:
- create execution workspace records forward from first new run
- optionally add a later backfill tool for recent runtime services if it proves valuable
Rollout Order
Phase 1: Schema and shared contracts
- extend
project_workspaces - add
execution_workspaces - add
issue_work_products - extend
issues - extend
workspace_runtime_services - update shared types and validators
Phase 2: Service wiring
- update project workspace CRUD
- update issue create/update resolution
- refactor workspace realization to persist execution workspaces
- attach runtime services to execution workspaces
- add work product service and persistence
Phase 3: API and UI
- add execution workspace routes
- add work product routes
- add instance experimental settings toggle
- re-enable and revise project workspace UI behind the flag
- add issue create/update controls behind the flag
- add issue work product tab
- add execution workspace detail page
Phase 4: Provider integrations
- GitHub PR reporting
- preview URL reporting
- runtime-service-to-work-product linking
- remote/cloud provider references
Acceptance Criteria
- Existing installs continue to behave predictably with no required reconfiguration.
- Projects can define local, git, non-git, and remote-managed project workspaces.
- Issues can explicitly select a project workspace and execution preference.
- Each issue can point to one current execution workspace.
- Multiple issues can intentionally reuse the same execution workspace.
- Execution workspaces are persisted for both local and remote execution flows.
- Work products can be attached to issues with optional execution workspace linkage.
- GitHub PRs can be represented with richer lifecycle states.
- The main UI remains simple when the experimental flag is off.
- No top-level workspace navigation is required for this first slice.
Risks and Mitigations
Risk: too many overlapping workspace concepts
Mitigation:
- keep issue UI to
CodebaseandExecution mode - reserve execution workspace details for advanced pages
Risk: breaking current projects on upgrade
Mitigation:
- nullable schema additions
- in-place
project_workspacesmigration - compatibility defaults
Risk: local-only assumptions leaking into cloud mode
Mitigation:
- make
cwdoptional for execution workspaces - use
provider_typeandprovider_ref - use
PAPERCLIP_EXECUTION_TOPOLOGYas a defaulting guardrail
Risk: turning PRs into a bespoke subsystem too early
Mitigation:
- represent PRs as work products in V1
- keep provider-specific details in metadata
- defer a dedicated PR table unless usage proves it necessary
Recommended First Engineering Slice
If we want the narrowest useful implementation:
- extend
project_workspaces - add
execution_workspaces - extend
issueswith explicit workspace fields - persist execution workspaces from existing local workspace realization
- add
issue_work_products - show project workspace controls and issue workspace controls behind the experimental flag
- add issue
Work Producttab with PR/preview/runtime service display
This slice is enough to validate the model without yet building every provider integration or cleanup workflow.