Add workspace file viewer and artifact links (#7681)

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent work is issue-centered, and reviewers often need to inspect
files, artifacts, and path references produced during that work.
> - Before this branch, workspace-relative paths and artifact file
references were not first-class inspectable objects in the board UI.
> - Safe file viewing needs shared resource contracts, server-side
workspace boundary checks, and UI that opens files without exposing
arbitrary host paths.
> - The workspace file viewer branch needed to stay as one active PR and
be rebased onto current `paperclipai/paperclip:master` for review.
> - This pull request adds the workspace file resource API, issue-page
file viewer and browser, markdown file-reference links, and artifact
file chips.
> - The benefit is that board users can inspect relevant files from
issue context while preserving workspace boundaries and auditability.

## Linked Issues or Issue Description

No public GitHub issue exists for this branch. Internal Paperclip
issues: `PAP-1953`, `PAP-10539`, `PAP-10733`.

Problem / motivation:
- Board users need to open workspace-relative files mentioned by agents
or attached as work-product metadata without switching to a terminal.
- The UI needs to support both direct file-path opening and workspace
browsing/searching from an issue page.
- The server must enforce company access, workspace boundaries, size
limits, rate limits, and safe audit logging.

Related PR:
- Prior closed attempt: #4442
- Single active PR for this branch: #7681

## What Changed

- Added shared workspace file resource types, validators, and
workspace-file `resourceRef` metadata validation for work products.
- Added server routes/services for resolving, listing, and previewing
workspace-relative files with access checks, scan caps, list-specific
limits, and audit logging.
- Added the issue file viewer provider, sheet, workspace browser,
command-palette action, markdown workspace-file autolinks, and artifact
file chips.
- Updated issue workspace UI and stories/tests for file browsing and
workspace file opening.
- Rebased the branch onto current `paperclipai/paperclip:master` and
updated the existing single PR branch.
- Addressed current-head Greptile follow-ups by applying `offset`
consistently across search/recent/changed file listings, restoring
stopped-service port ownership checks before auto-port reuse, and
stabilizing the workspace browser pagination test.

## Verification

Current local verification after rebase to `public/master`:
- `pnpm exec vitest run packages/shared/src/work-product.test.ts
server/src/__tests__/file-resources.test.ts
server/src/__tests__/instance-settings-routes.test.ts
server/src/__tests__/instance-settings-service.test.ts
server/src/__tests__/workspace-runtime.test.ts
ui/src/components/FileViewerSheet.test.tsx
ui/src/components/FileViewerSheet.copy.test.tsx
ui/src/components/WorkspaceFileBrowser.test.tsx
ui/src/components/WorkspaceFileMarkdownBody.test.tsx
ui/src/context/FileViewerContext.test.ts
ui/src/lib/remark-workspace-file-refs.test.ts
ui/src/lib/workspace-file-parser.test.ts
ui/src/components/IssueWorkspaceCard.test.tsx` - 13 files passed, 197
tests passed.
- `pnpm -r --filter @paperclipai/shared --filter @paperclipai/server
--filter @paperclipai/ui typecheck` - passed.
- `pnpm exec vitest run ui/src/components/WorkspaceFileBrowser.test.tsx`
- 1 file passed, 25 tests passed.
- `pnpm exec vitest run server/src/__tests__/file-resources.test.ts
server/src/__tests__/workspace-runtime.test.ts` - 2 files passed, 90
tests passed.
- `pnpm -r --filter @paperclipai/server typecheck` - passed.
- Confirmed branch is `0` behind and `46` ahead of current
`public/master` after rebase and follow-up commits.
- Confirmed the PR diff does not include `pnpm-lock.yaml`.
- Confirmed the PR diff does not include `.github/workflows` changes.
- Searched GitHub for duplicate or related workspace file viewer
PRs/issues; #4442 is the prior closed attempt and this PR is the single
active PR for the branch.
- No screenshots were committed; the task explicitly asked not to add
design screenshots or images unless they were part of the work.

Current remote verification on head
`a698a7bc10137baf7d25bd5722e1d6e0343387c1`:
- Greptile Review - success, 64 files reviewed, 0 comments added, no
unresolved Greptile review threads.
- PR workflow `verify` - success.
- Typecheck + Release Registry, General tests, workspace test shards,
serialized server suites, Build, Canary Dry Run, e2e, Socket, and Snyk -
success.
- `security-review` - neutral, with output saying a draft advisory was
filed for maintainer review and is not a merge block.
- `commitperclip PR Review / review` - cancelled after the security gate
detected flags and timed out while creating/reviewing the advisory. I
reran it once and it cancelled the same way; no actionable code/test
failure was exposed in the job logs.

## Risks

- This is a broad UI/server feature PR, so review needs to pay attention
to route authorization, workspace boundary handling, and markdown
autolink false positives.
- Workspace browsing intentionally caps list results and scan depth;
very large workspaces may require users to refine search terms.
- Remote workspace preview remains unavailable until remote file-access
support is implemented.
- The neutral commitperclip security-review advisory needs maintainer
review, but the check output says it is not a merge block.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected - check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

- OpenAI Codex, GPT-5 coding agent in a Paperclip/Codex local tool-use
environment, medium reasoning, with shell/GitHub CLI tool use for branch
inspection, verification, rebase, PR update, Greptile review, and CI
inspection.

## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Dotta
2026-06-09 17:17:43 -05:00
committed by GitHub
parent bf62e3fbf1
commit 468edd8b22
64 changed files with 10816 additions and 82 deletions
@@ -0,0 +1,377 @@
---
name: paperclip-dev-workspace-run-verify-fix
description: >
Run, verify, reseed, and repair a Paperclip isolated dev workspace service.
Use when asked to start or fix a Paperclip project/worktree service and prove
that it is managed by the Paperclip runtime, has the full bootstrapped cloned
database, is healthy, accepts normal dev credentials, exposes populated app
data, and is visible as running from both the control-plane and the served
workspace app.
---
# Paperclip Dev Workspace Run / Verify / Fix
This skill is for Paperclip-specific development workspaces whose service is
started through project execution workspace runtime services, typically a
worktree service such as `paperclip-dev`.
Success means all of these are true:
- the service was started through the normal managed runtime path, not a
detached workaround
- the worktree database is a full bootstrapped isolated clone of the primary
instance database
- `/api/health` returns `status: ok` and `bootstrapStatus: ready`
- the root page returns `200` and does not show the first-admin setup gate
- the board user can log in with their normal dev credentials
- the app shows populated cloned data, not only a manually copied auth user
- the main control plane shows the service as `running` / `healthy` with the
expected URL
- the served workspace app also knows about that service and shows it as
`running` / `healthy`
If any item fails, keep fixing. Do not mark the issue done because one probe
passed.
## Hard rules
- Read `doc/DEVELOPING.md` before running Paperclip CLI, dev server, worktree,
database, build, or test commands.
- Use the Paperclip CLI and API as the source of truth for worktree and
database operations. Do not use `psql`, raw embedded-Postgres commands, or
ad hoc row copying for the normal fix path.
- Do not manually copy only auth rows as the final fix. That proves login, but
it does not prove the isolated workspace has the full bootstrapped database.
- Prefer managed runtime start/stop routes over `pnpm dev` or detached shell
processes when the task is about a reusable workspace service.
- Avoid destructive git or database actions. Preserve user changes in the
worktree.
- Run the smallest verification that proves the repair. Add focused tests only
when code changed.
- Leave an issue comment with root cause, exact fix, verification, and any
commit link. Set a clear final status.
## Inputs to collect
Use environment variables when available. Do not print API keys or passwords.
- `PAPERCLIP_API_URL`: main control-plane API URL
- `PAPERCLIP_API_KEY`: agent API key
- `PAPERCLIP_RUN_ID`: current run id for runtime-service mutations
- `PAPERCLIP_TASK_ID`: current issue id
- `PAPERCLIP_COMPANY_ID`: company id
- `PAPERCLIP_AGENT_ID`: current agent id
- execution workspace id for the worktree service
- runtime workspace command id, usually `service:paperclip-dev`
- expected service URL, for example `http://paperclip-dev:40631`
- dev credential owner, if the user supplied one; never post the password
If the execution workspace id or service command id is missing, read the issue,
project, or execution-workspace API records first. Do not guess and start an
unmanaged server on a random port.
## Normal run sequence
Use this when the user says to start the workspace, start it again, or fix a
workspace that should be freshly ready.
1. Confirm the latest issue comment and restate the success condition in your
own words.
2. Inspect current runtime state from the main control plane.
3. Stop any managed instance of the target runtime service.
4. Reseed the worktree with a full clone from the primary instance when there
is any doubt about database completeness.
5. Start the runtime service through the managed runtime API.
6. Verify health, bootstrap state, login readiness, populated data, and runtime
visibility from both the main control plane and served workspace app.
7. If a verification item fails, diagnose that exact failure and loop back to
the narrowest repair step.
8. Comment on the issue and set the final disposition.
## Managed start / stop
Use the runtime-service endpoints on the main control plane. Include
`X-Paperclip-Run-Id` so the mutation is associated with the current heartbeat.
```sh
curl -sS -X POST \
"$PAPERCLIP_API_URL/api/execution-workspaces/$EXECUTION_WORKSPACE_ID/runtime-services/stop" \
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
-H "Content-Type: application/json" \
--data-binary '{"workspaceCommandId":"service:paperclip-dev"}'
```
```sh
curl -sS -X POST \
"$PAPERCLIP_API_URL/api/execution-workspaces/$EXECUTION_WORKSPACE_ID/runtime-services/start" \
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
-H "Content-Type: application/json" \
--data-binary '{"workspaceCommandId":"service:paperclip-dev"}'
```
If the API returns an existing service, treat that as a candidate only. Verify
its real `/api/health` before trusting it.
## Full database reseed
Use a full reseed when the app says setup is incomplete, login works but data
is missing, the cloned app does not have the expected companies/issues/agents,
or the user explicitly asks for the normal isolated-workspace database.
```sh
pnpm paperclipai worktree reseed --from-instance default --seed-mode full --yes
```
After reseed, restart through the managed runtime path. A reseed can copy
runtime-service rows whose ids no longer match the local process registry, so
runtime adoption and reconciliation must be verified after the start.
Do not consider the reseed complete until the served app has both auth and
populated product data. A one-off inserted user/account row is a diagnostic
clue, not the final state.
## Core verification probes
Set `SERVICE_URL` to the service URL returned by the runtime API.
```sh
curl -sS "$SERVICE_URL/api/health" | jq
curl -sS -I "$SERVICE_URL/" | head
```
Expected health:
- `status: "ok"`
- `bootstrapStatus: "ready"`
- `bootstrapInviteActive: false`
Failures to reject:
- `bootstrap_pending`: the instance will show the first-admin setup gate
- `database_unreachable`: a web process is listening but its database is dead
- a root `200` with unhealthy `/api/health`: stale process adoption bug or a
dead embedded database behind a live Node process
Check the port owner when a process is already listening:
```sh
lsof -nP -iTCP:"$SERVICE_PORT" -sTCP:LISTEN || true
```
Use this only to identify and remove a stale matching Paperclip dev-runner
process after managed stop fails. Do not kill unrelated processes.
## Verify main control-plane runtime state
Read the execution workspace from the main API and inspect the runtime service
record.
```sh
curl -sS \
"$PAPERCLIP_API_URL/api/execution-workspaces/$EXECUTION_WORKSPACE_ID" \
-H "Authorization: Bearer $PAPERCLIP_API_KEY" | jq
```
The target service should show:
- matching workspace command id, usually `service:paperclip-dev`
- `status: "running"`
- healthy health fields, if present
- the same URL you are probing
- a local provider reference that maps to the running port owner
If the main app says running but `/api/health` is bad, stop and replace the
stale process through the managed runtime.
## Verify served workspace runtime state
The cloned Paperclip app must also know about the service. Query the same
execution workspace through the served app when agent auth is available there:
```sh
curl -sS \
"$SERVICE_URL/api/execution-workspaces/$EXECUTION_WORKSPACE_ID" \
-H "Authorization: Bearer $PAPERCLIP_API_KEY" | jq
```
The served app should agree that the service is `running` / `healthy` at the
same URL. If the main control plane and served app disagree after a reseed,
the cloned database may contain copied runtime-service ids that do not match
the local process registry. Use the normal start/adoption path again and verify
both sides. If code changed in this area, add a focused regression test.
## Verify auth and populated data
Auth and data checks should use product APIs and browser/QA review, not raw DB
queries.
Minimum API checks:
```sh
curl -sS "$SERVICE_URL/api/health" | jq '.status, .bootstrapStatus'
curl -sS "$SERVICE_URL/api/companies" \
-H "Authorization: Bearer $PAPERCLIP_API_KEY" | jq
curl -sS "$SERVICE_URL/api/agents/me" \
-H "Authorization: Bearer $PAPERCLIP_API_KEY" | jq
```
Then verify at least one expected cloned product record through the API, such
as a known project, issue key, company, or execution workspace that should
exist in the primary instance. Pick a record relevant to the current issue
rather than a random table count.
Browser or QA check:
- open the service URL
- confirm the first-admin setup gate is gone
- sign in with normal dev credentials
- confirm the board loads populated companies/projects/issues/runs
- confirm the workspace service appears with the running URL
If this environment cannot launch a browser, ask QA to do the visual/login
check and still complete all API checks you can run. Report that browser
verification was delegated and why.
## Common failures and fixes
### Setup gate appears
Symptom: the page says no admin has claimed the instance.
Fix: run a full worktree reseed from the primary instance, then restart the
managed service. Claiming a first admin can clear the gate, but if the user
asked for the normal isolated workspace database, full reseed is the correct
fix.
Verify: `/api/health` has `bootstrapStatus: ready`, login works, and populated
data exists.
### Login fails
Symptom: bootstrap is ready, but the user's normal dev credentials do not work.
Likely cause: the isolated DB has roles or bootstrap state but lacks the
primary instance auth users/accounts.
Fix: full reseed. Do not manually copy only Better Auth user/account rows as
the final fix.
Verify: user login through browser/QA and `/api/agents/me` with the agent key.
### Login works but data is missing
Symptom: user can sign in, but companies/issues/projects/runs are empty or
clearly incomplete.
Likely cause: a partial auth repair was done instead of a full cloned database.
Fix: full reseed, managed restart, then verify representative cloned records.
### Port listens but health says database unreachable
Symptom: `curl -I /` returns a response, but `/api/health` reports
`database_unreachable`.
Likely cause: stale Node/web process remained alive after embedded Postgres
died.
Fix: managed stop first. If the process survives, identify the matching
Paperclip dev-runner process group for the target port and terminate only that
group. Then managed start.
Verify: `/api/health` is ok after a stability wait and the runtime record is
healthy.
### Main control plane loses track of a running service
Symptom: the service URL works, but the main app says the service was not
created or is stopped.
Likely cause: detached workaround process, stale provider ref, or service
adoption trusted the root URL instead of health.
Fix: shut down the unmanaged process and restart through the managed runtime.
If code repair is needed, ensure adoption checks `/api/health`, replaces
unhealthy adopted processes, and records the current provider ref.
Verify: main runtime row and `/api/health` agree.
### Served app loses track after full reseed
Symptom: the main app sees `paperclip-dev` running, but the cloned app copied a
runtime-service row whose id does not match the local registry.
Likely cause: normal DB clone copied persisted runtime rows from the primary
instance into an isolated environment with different local process metadata.
Fix: use managed start/adoption again. If code repair is needed, adoption
should reconcile by service identity and port, not only by copied row id.
Verify: main app and served app both show the same service as
`running` / `healthy`.
### Runtime start returns permission or run-FK errors in the cloned app
Symptom: starting from the served app returns `403` or a mutation partially
applies before activity logging fails.
Likely causes: the cloned issue/run/agent state does not match the current
heartbeat, or the run id is absent in the cloned database after reseed.
Fix: prefer the main control-plane managed runtime path and full reseed. If
the cloned app state itself must be repaired, use normal Paperclip issue/run
transitions first. Do not hide the condition with raw DB edits; report the
exact guard or missing row if it blocks the normal path.
## When code changes are required
Make a code change only when the normal operational repair exposes a product
bug. Examples from this failure class:
- local port owner detection used the wrong `lsof` arguments
- service adoption trusted root `200` instead of `/api/health`
- unhealthy adopted services were not terminated/replaced
- reseeded runtime-service ids were not reconciled with local process registry
Add focused tests in the affected service test file. For workspace runtime
repairs, the narrow verification is usually:
```sh
pnpm exec vitest run server/src/__tests__/workspace-runtime.test.ts
git diff --check
```
Commit logical code changes and link the commit in the issue comment. If no
tracked code changed, say so explicitly.
## Final issue comment template
Use concrete evidence, not a vague "it works".
```md
Fixed and verified the workspace service.
Root cause:
- <why it broke>
Fix:
- <normal reseed/start/repair steps>
- <code commit if any>
Verified:
- main control plane shows <service> running/healthy at <url>
- served workspace app shows the same service running/healthy
- <url>/api/health is ok with bootstrapStatus ready
- root page returns 200 and no setup gate
- dev login verified by <agent browser / QA / user> without posting credentials
- cloned data verified via <specific API records>
- targeted tests: <commands>
Remaining:
- <none, or named owner/action if blocked>
```
Mark the issue `done` only when every success-condition item is satisfied. If
not, mark `blocked` with a named unblock owner and the exact action needed.