mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-03 01:27:09 +01:00
## 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>
102 lines
6.4 KiB
Markdown
102 lines
6.4 KiB
Markdown
# Project source repositories
|
|
|
|
The Create project dialog accepts a name and optional GitHub repository selections.
|
|
It uses the same `RepositoryEditor` as project Configuration. Description remains
|
|
editable in Configuration. Status, goal links, and target dates remain supported by
|
|
the API but are omitted from creation; Status and Goals are omitted from Configuration.
|
|
Old Overview URLs and saved Overview preferences redirect to Configuration.
|
|
|
|
## API and persistence
|
|
|
|
- `GET /api/companies/:companyId/project-repositories` returns `repositories`,
|
|
`connectionCount`, and `failedConnectionCount`. Repository IDs are GitHub's stable
|
|
numeric IDs represented as strings. Results are deduplicated across accessible
|
|
grants and sorted by full name. Connection labels are display provenance only.
|
|
- `POST /api/companies/:companyId/projects` accepts optional `repositoryIds`.
|
|
The server resolves new selections through the caller's authorized GitHub grants
|
|
before creating the project and all repository workspaces in a transaction.
|
|
The existing `workspace` input remains supported; it cannot be combined with
|
|
`repositoryIds`.
|
|
- `PUT /api/projects/:id/repositories` accepts the selected `repositoryIds` array.
|
|
Accessible retained IDs refresh their canonical name and URL after renames or
|
|
transfers; unavailable retained IDs keep their saved metadata.
|
|
Replacement is transactional. Existing selections may be retained or removed even
|
|
if their GitHub connection becomes unavailable. New identities require current
|
|
access. Legacy URL workspaces are preserved, and matching legacy URLs are adopted
|
|
without creating a duplicate workspace. Local/remote workspace locations survive
|
|
detaching their repository.
|
|
|
|
No schema migration is required. Selected repositories are normal project workspaces
|
|
with `metadata.githubRepositoryId`. Existing manual `repoUrl` workspaces remain
|
|
editable through Configuration and the workspace API. One workspace remains primary.
|
|
Tasks materialize the other distinct repositories as editable checkouts inside their
|
|
workspace, including when no local folders are configured. Local execution and sandbox
|
|
staging use the same layout; sandbox restore preserves each repository's Git history.
|
|
See [Project Repository Checkouts](DEVELOPING.md#project-repository-checkouts) for paths,
|
|
ignore rules, and reuse behavior. Responsible-user credential rules still apply.
|
|
A repository selection never delegates credentials.
|
|
|
|
Fresh tasks using the default repository workspace can clone the same authorized
|
|
remote into a company- and task-scoped source directory when an existing project
|
|
checkout contains another task's restricted workspace. The clone uses the run's
|
|
Git connection and keeps separate Git metadata. Source access checks still apply
|
|
before the task workspace is published. Low-trust review runs cannot use this
|
|
persistent clone fallback. This fallback does not copy unpublished
|
|
local changes or replace reused workspaces, explicit workspace selections, pinned
|
|
branches, or repositories that only exist on the local filesystem.
|
|
Configured branch templates must derive the branch from the task identifier, ID,
|
|
title, or slug. Literal or task-independent templates retain the source denial,
|
|
so a fresh clone cannot replace a selected local branch with a new empty branch.
|
|
New implicit Git workspace defaults record their project-source provenance.
|
|
An untyped checkout binding from an older task is ambiguous and keeps the denial,
|
|
including explicit checkout IDs and inherited parent sources. Admission rechecks
|
|
the current binding rather than inferring a default from the selected checkout ID.
|
|
When the denial is retained, the run stops before the agent starts with a
|
|
`configuration_incomplete` failure (reason `workspace_source_restricted`). The task
|
|
is blocked with a notice that tells the owner to push unpublished work and select a
|
|
new workspace, or to grant access to the restricted task, then retry. Paperclip
|
|
does not report this owner action to Sentry as an unexpected run failure.
|
|
This applies to both isolated-source checks and repository admission for a shared
|
|
project workspace. Neither path replaces the existing workspace or retries the
|
|
same access denial automatically.
|
|
Startup validation of these recorded defaults keeps the task's effective workspace
|
|
strategy. It does not substitute the agent's shared strategy or grant project writes.
|
|
Workspace close and reopen use the recorded isolated source. Both operations
|
|
verify its company, task, repository URL, and separate Git storage before use.
|
|
|
|
## Discovery and setup
|
|
|
|
The server checks company membership, grant ownership/status, and organization-grant
|
|
audiences before loading provider metadata. Connection managers receive no bypass to
|
|
another person's personal repositories. Managed GitHub grants refresh installation
|
|
access; PAT connections use paginated `/user/repos`. Provider failures are reported
|
|
without exposing provider error bodies or credential material. Successful connections
|
|
remain selectable when another connection fails.
|
|
|
|
`ConnectionSetupFlow` owns provider setup in both Apps and project dialogs. Task
|
|
intents retain their existing callback protocol. Standalone dialogs verify the saved
|
|
connection through the API after the sign-in popup returns to the instance. Project
|
|
name and repository drafts stay mounted across setup and cancellation.
|
|
|
|
## UI review and verification
|
|
|
|
`Proposals/Project repos` contains the reviewed states, including loading, failure,
|
|
empty search, disconnected GitHub, multiple repos, legacy URLs, forty selections,
|
|
mobile, and short viewports. The configuration story composes the production page
|
|
properties through an explicit repositories slot. Story setup and saves use fixtures.
|
|
|
|
- Shared visual control: `ui/src/components/RepositoryEditor.tsx`.
|
|
- Data and error handling: `ProjectRepositoryInput.tsx`.
|
|
- Configuration persistence and legacy editing: `ProjectRepositories.tsx` and
|
|
`LegacyProjectRepository.tsx`.
|
|
- Production dialog: `NewProjectDialog.tsx`.
|
|
- Server tests: `project-repositories.test.ts` and
|
|
`project-repositories-persistence.test.ts`.
|
|
- Browser acceptance: `tests/e2e/project-repositories.spec.ts`.
|
|
|
|
The browser suite uses a real temporary server and database. It verifies creation,
|
|
forty persisted repos, mobile scrolling, removal/save/reload, legacy URL editing,
|
|
and rejection without a partial project. Provider discovery is simulated in the
|
|
picker rejection test. GitHub network and popup behavior use deterministic fixtures
|
|
in integration/component tests; the suite does not authorize a real GitHub account.
|