关于 paperclip-dev-workspace-run-verify-fix
面向 Paperclip 开发工作区的技能,用于启动、修复和验证托管 worktree 服务。它强调完整数据库重播、运行时归属检查和端到端就绪验证,而非依赖单项健康探测。
主要功能
- 01托管运行时的启动与停止
- 02完整重播 worktree 数据库
- 03检查端口进程所属工作区
- 04验证健康状态、登录与数据
- 05核对控制平面及应用状态
适用场景
- 01启动 Paperclip worktree 服务
- 02修复未完成初始化或不健康的工作区
- 03诊断占用固定端口的同级工作区
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ paperclipai/ paperclip --skill paperclip-dev-workspace-run-verify-fix安装后在 Claude Code 中输入/paperclip-dev-workspace-run-verify-fix确认已加载。
详细介绍
Paperclip 开发工作区的运行、验证与修复
此技能适用于 Paperclip 专属开发工作区,其服务通过项目执行工作区运行时服务启动,通常是名为 paperclip-dev 的 worktree 服务。
满足以下所有条件即表示成功:
- 服务通过常规托管运行时路径启动,而非使用脱离管理的变通方案
- worktree 数据库是从主实例数据库完整引导的隔离克隆
/api/health返回status: ok和bootstrapStatus: ready- 根页面返回
200,且不显示首次管理员设置页面 - 看板用户可以使用其常规开发凭据登录
- 应用显示已填充的克隆数据,而不只是手动复制的身份验证用户
- 主控制平面将该服务显示为
running/healthy,并显示预期 URL - 提供服务的工作区应用也识别该服务,并将其显示为
running/healthy - 占用服务端口的进程确实属于目标工作区:
readlink /proc/<pid>/cwd解析到目标 worktree 内,而非相邻工作区
如果任何一项未通过,请继续修复。不能因为某一项检查通过就标记问题已完成。
硬性规则
- 主检出目录
/srv/paperclip/home/paperclipai/paperclip(包含真实实例.env的主仓库)绝不能作为工作区修复目标。切勿将 worktree 运行时服务指向该目录,切勿在该目录或<master>/.paperclip/worktrees/下执行git worktree add,修复工作区时也切勿编辑其.env。托管工作区在各自的目录中运行,例如~/.paperclip-worktrees/instances/<slug>/…。 - 切勿仅凭
running/healthy运行时记录或通过的/api/health检查就认定一切正常。service:paperclip-dev端口在服务配置中固定,因此相邻工作区的进程可能占用该端口,并仍能通过健康检查。每次修复前后都要确认端口占用进程的实际 cwd(readlink /proc/<pid>/cwd);运行时记录中记载的cwd和pid可能由端口接管伪造。 - 运行 Paperclip CLI、开发服务器、worktree、数据库、构建或测试命令前,请先阅读
doc/DEVELOPING.md。 - 将 Paperclip CLI 和 API 作为 worktree 与数据库操作的事实依据。常规修复流程中,不要使用
psql、原始内嵌 Postgres 命令或临时复制行的方式。 - 不要将仅手动复制身份验证行作为最终修复。那只能证明可以登录,不能证明隔离工作区拥有完整引导的数据库。
- 如果任务涉及可复用的工作区服务,应优先使用托管运行时启动/停止路由,而不是
pnpm dev或脱离管理的 shell 进程。 - 避免破坏性的 git 或数据库操作。保留 worktree 中的用户更改。
- 运行足以证明修复有效的最小验证。仅在更改了代码时添加针对性测试。
- 留下问题评论,说明根本原因、确切修复方式、验证结果以及任何提交链接。设置明确的最终状态。
需要收集的信息
如有可用环境变量,请使用。不要打印 API 密钥或密码。
PAPERCLIP_API_URL:主控制平面 API URLPAPERCLIP_API_KEY:Agent API 密钥PAPERCLIP_RUN_ID:当前运行 ID,用于运行时服务变更PAPERCLIP_TASK_ID:当前问题 IDPAPERCLIP_COMPANY_ID:公司 IDPAPERCLIP_AGENT_ID:当前 Agent ID- worktree 服务对应的执行工作区 ID
- 运行时工作区命令 ID,通常是
service:paperclip-dev - 预期服务 URL,例如
http://paperclip-dev:40631 - 开发凭据所属用户(如果用户提供);切勿发布密码
如果缺少执行工作区 ID 或服务命令 ID,请先读取问题、项目或执行工作区的 API 记录。不要猜测并在随机端口上启动未托管的服务器。
常规运行流程
当用户要求启动工作区、重新启动工作区,或修复应已准备就绪的工作区时,请使用此流程。
- 确认最新的问题评论,并用自己的话复述成功条件。
- 从主控制平面检查当前运行时状态。
- 检查目标端口是否冲突:确认当前端口的占用者及其实际 cwd,并检查是否有其他工作区的运行时记录也声明使用同一端口(参见“端口冲突与工作区身份”)。
- 如果另一个正在运行的工作区或 Agent 运行占用了端口,请通过其受管控制路径停止该占用者,并确认端口保持空闲。请在重启目标工作区之前执行此操作;否则,占用者可能会重新启动并再次占用该端口。
- 停止目标运行时服务的任何受管实例。
- 如果无法确定数据库是否完整,请从主实例完整克隆并重新填充工作树。
- 通过受管运行时 API 启动运行时服务。
- 从主控制平面和所服务的工作区应用中,验证运行状况、引导状态、登录就绪状态、数据填充情况、运行时可见性以及端口占用者身份。
- 如果某项验证失败,请诊断该具体故障,并从最小范围的修复步骤开始重新处理。
- 在问题中发表评论并设置最终处理结果。
受管启动 / 停止
在主控制平面上使用运行时服务端点。请包含 X-Paperclip-Run-Id,以便将变更与当前心跳关联起来。
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"}'
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"}'
如果 API 返回了现有服务,只能将其视为候选服务。请先验证其实际 /api/health 和端口占用者身份,再决定是否信任它。
端口冲突与工作区身份
service:paperclip-dev 在服务配置中固定了显式端口,因此每个运行该服务的工作区都会占用同一端口。启动时,如果现有进程的命令行看起来相似,服务接管机制就会接管当前占用该端口的进程——并记录请求的 cwd,而不是进程所有者的真实 cwd。由此会产生两个后果:
- 多个工作区可能针对同一端口都显示为
running,但实际上只有一个进程;无论你打开哪个工作区 URL,访问到的都是这个进程 - 运行时记录中的
cwd、pid和健康状态看起来都正确,但该 URL 实际提供的可能是同级工作树中的应用
身份检查——在信任任何现有服务之前,以及每次启动或重启之后,都要运行此检查:
pid=$(lsof -nP -iTCP:"$SERVICE_PORT" -sTCP:LISTEN -t | head -1)
readlink "/proc/$pid/cwd"
解析出的 cwd 必须位于目标工作树内(开发服务器通常运行于 <worktree>/server)。只有 /proc/<pid>/cwd 才是权威信息。不要把运行时记录中的 cwd 字段、根路径返回 200 或健康的 /api/health 当作身份正确的证明——占用端口的同级工作区进程这三项都会通过。
如果端口由同级工作区的进程占用(端口抢占):
- 确定当前应该由哪个工作区占用固定端口——通常是问题中提到的工作区。
- 确认当前所有者的工作区,以及是否有正在运行的 Agent 任务或托管运行时服务负责保持其运行。使用真实的
/proc/<pid>/cwd、同级执行工作区记录以及所属问题/任务的状态;不要根据目标运行时记录推断所有权。 - 首先通过所有者自身的托管控制路径停止进程:停止同级工作区的运行时服务,或停止/取消负责监督并重新启动该进程的运行中任务。不要在监督进程仍处于活动状态时直接杀掉同级进程,否则它会立即重启并重新占用端口。
- 等待一段稳定时间,确认端口持续未被占用。如果端口立即再次被占用,应找到并停止仍在运行的监督进程,而不是循环重启目标服务。
- 只有在冲突所有者已停止后,才启动或重启目标服务,并重新运行身份检查:
curl -sS -X POST \
"$PAPERCLIP_API_URL/api/execution-workspaces/$EXECUTION_WORKSPACE_ID/runtime-services/restart" \
-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"}'
- 只有在冲突所有者保持停止时,重启才算有效。如果端口再次被占用,应再次停止并排查所有者/监督进程;不要反复重启目标服务来对抗它。
- 如果两个工作区确实需要同时运行,它们就不能共用固定端口。停止不需要运行的工作区(通过其自身工作区的托管停止操作,或结束不断重新启动它的任务),在问题评论中说明当前由哪个工作区占用端口,并将固定端口冲突升级为产品问题,而不是反复重启。
在宣布成功之前,还要获取你怀疑有冲突的同级工作区,并比较其 runtimeServices[].port 值:跨工作区只要有多条 running 记录占用目标端口,就始终是必须解决的冲突,而不是可以绕过的问题。
完整数据库重建
如果应用提示设置尚未完成、登录成功但数据缺失、克隆的应用中没有预期的公司/问题/Agent,或用户明确要求使用常规的隔离工作区数据库,请执行完整重建。
npx paperclipai worktree reseed --from-instance default --seed-mode full --yes
重建后,通过托管运行时路径重启。重建可能会复制运行时服务记录,而其 ID 与本地进程注册表不再匹配,因此必须在启动后验证运行时接管和协调状态。
在服务中的应用同时具备身份验证和已填充的产品数据之前,不要认为重建已完成。临时插入的用户/账户记录只是诊断线索,并非最终状态。
核心验证探针
将 SERVICE_URL 设置为运行时 API 返回的服务 URL。
curl -sS "$SERVICE_URL/api/health" | jq
curl -sS -I "$SERVICE_URL/" | head
预期健康状态:
status: "ok"bootstrapStatus: "ready"bootstrapInviteActive: false
需要拒绝的故障:
bootstrap_pending:实例会显示首次管理员设置门禁database_unreachable:Web 进程正在监听,但其数据库已失效- 根路径返回
200,但/api/health不健康:陈旧进程接管错误,或运行中的 Node 进程背后嵌入式数据库已失效
当进程已在监听时,检查端口占用者:
lsof -nP -iTCP:"$SERVICE_PORT" -sTCP:LISTEN || true
仅在托管停止失败后,使用此方法识别并移除陈旧的、匹配的 Paperclip 开发运行器进程。不要终止无关进程。
验证主控制平面运行时状态
从主 API 读取执行工作区,并检查运行时服务记录。
curl -sS \
"$PAPERCLIP_API_URL/api/execution-workspaces/$EXECUTION_WORKSPACE_ID" \
-H "Authorization: Bearer $PAPERCLIP_API_KEY" | jq
目标服务应显示:
- 匹配的工作区命令 ID,通常为
service:paperclip-dev status: "running"- 健康的健康状态字段(如果存在)
- 与正在探测的 URL 相同的 URL
- 一个映射到端口占用进程的本地提供方引用
如果主应用显示服务正在运行,但 /api/health 状态异常,请停止服务,并通过托管运行时替换陈旧进程。
验证已提供服务的工作区运行时状态
克隆的 Paperclip 应用也必须知道该服务。若该应用可使用 Agent 身份验证,请通过已提供服务的应用查询同一执行工作区:
curl -sS \
"$SERVICE_URL/api/execution-workspaces/$EXECUTION_WORKSPACE_ID" \
-H "Authorization: Bearer $PAPERCLIP_API_KEY" | jq
已提供服务的应用应确认该服务在同一 URL 上处于 running / healthy 状态。如果重新播种后主控制平面与已提供服务的应用状态不一致,克隆的数据库可能包含与本地进程注册表不匹配的运行时服务 ID。请再次使用常规启动/接管流程,并验证两端。如果此处代码有变更,请添加有针对性的回归测试。
验证身份验证和已填充的数据
身份验证和数据检查应使用产品 API 和浏览器/QA 审查,而不是直接查询数据库。
最低限度的 API 检查:
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
然后通过 API 验证至少一条预期存在的克隆产品记录,例如主实例中应该存在的已知项目、问题键、公司或执行工作区。选择与当前问题相关的记录,而不是随机统计某个表的数量。
浏览器或 QA 检查:
- 打开服务 URL
- 确认首次管理员设置门禁已消失
- 使用常规开发凭据登录
- 确认看板加载了已填充的公司/项目/问题/运行记录
- 确认工作区服务显示了正在运行的 URL
如果此环境无法启动浏览器,请让 QA 执行视觉/登录检查,并完成所有可以运行的 API 检查。报告浏览器验证已委派,并说明原因。
常见故障和修复方法
服务 URL 提供的是另一个工作区(端口被占用)
症状:工作区 URL 能加载正常运行的 Paperclip 应用,但该应用属于另一个同级工作树——分支或数据错误,或者 UI 不断将你带到另一个工作区的页面。
可能原因:固定服务端口由另一个工作区的进程占用,启动时接管流程将该进程关联到了此工作区的运行时记录。健康检查会通过,因为同级应用实际上运行正常。
修复:按照“端口冲突和工作区身份”中的先检查占用者流程,识别并停止同级服务或监管运行任务,确认端口保持空闲,然后启动或重启目标服务。重新验证新端口占用进程的 /proc/<pid>/cwd 指向目标工作树内的路径。
验证:身份检查通过,并且通过该 URL 提供的一条工作区特定记录(分支、问题键或工作区 ID)与目标工作区相符。
随手启动后记录的端口发生漂移
现象:运行时记录和实际监听的端口不一致,或者有人手动启动应用后,之前固定的端口发生了变化。
可能原因:未托管的 pnpm dev(例如使用 --bind lan)会自行选择端口,而不是使用托管的固定端口。
修复:停止未托管进程,然后通过托管方式启动。验证记录的 URL 端口与绑定端口一致,并且身份检查通过。
出现设置门槛
现象:页面提示尚未有管理员认领此实例。
修复:从主实例完整地重新播种工作树,然后重启托管服务。认领首位管理员可以解除门槛,但如果用户要求使用常规的隔离工作区数据库,正确的修复方式是完整重新播种。
验证:/api/health 中的 bootstrapStatus: ready,登录正常,并且存在已填充的数据。
登录失败
现象:引导状态已就绪,但用户的常规开发凭据无法使用。
可能原因:隔离数据库中有角色或引导状态,但缺少主实例的身份验证用户/账户。
修复:完整重新播种。不要仅手动复制 Better Auth 用户/账户记录作为最终修复方案。
验证:通过浏览器/QA 登录用户,并使用 Agent 密钥访问 /api/agents/me。
登录正常,但缺少数据
现象:用户可以登录,但公司/问题/项目/运行记录为空或明显不完整。
可能原因:执行了部分身份验证修复,而不是完整克隆数据库。
修复:完整重新播种,通过托管方式重启,然后验证有代表性的克隆记录。
端口正在监听,但健康检查提示数据库不可达
现象:curl -I / 返回响应,但 /api/health 报告 database_unreachable。
可能原因:嵌入式 Postgres 退出后,过期的 Node/Web 进程仍在运行。
修复:先通过托管方式停止。如果进程仍在运行,找出与目标端口匹配的 Paperclip dev-runner 进程组,并且仅终止该进程组。然后通过托管方式启动。
验证:等待服务稳定后,/api/health 显示正常,且运行时记录状态健康。
主控制平面无法跟踪运行中的服务
现象:服务 URL 可以正常访问,但主应用提示该服务尚未创建或已停止。
可能原因:使用了脱离托管的变通进程、provider ref 已过期,或者服务接管时依据的是根 URL,而不是健康检查结果。
修复:关闭未托管进程,并通过托管运行时重启。如果需要修复代码,请确保接管会检查 /api/health、替换不健康的已接管进程,并记录当前 provider ref。
验证:主运行时记录与 /api/health 一致。
完整重新播种后,服务应用无法跟踪服务
现象:主应用看到 paperclip-dev 正在运行,但克隆的应用复制了一个 runtime-service 记录,其 id 与本地注册表不匹配。
可能原因:常规数据库克隆将主实例中持久化的运行时记录复制到了本地进程元数据不同的隔离环境中。
修复:再次通过托管方式启动/接管。如果需要修复代码,接管应通过服务身份和端口进行协调,而不能只依赖复制过来的记录 id。
验证:主应用和服务应用都将同一服务显示为 running / healthy。
克隆的应用中,运行时启动返回权限或运行外键错误
现象:从服务应用启动时返回 403,或变更已部分应用,但随后活动日志记录失败。
可能原因:克隆的问题/运行/Agent 状态与当前心跳不匹配,或者重新播种后克隆数据库中不存在运行 id。
修复:优先使用主控制平面的托管运行时路径,并执行完整重新播种。如果必须修复克隆应用自身的状态,请先使用常规 Paperclip 问题/运行状态转换。不要通过直接修改数据库来掩盖问题;如果常规路径被阻止,请报告具体的保护条件或缺失记录。
以往修复中反复出现的问题
以下问题曾导致之前的修复失败;深入调试前请先检查。
- 工作区实例日志位于
~/.paperclip-worktrees/instances/<slug>/logs。在猜测登录或启动失败的原因之前,请先查看这些日志。 - 通过局域网或 tailscale IP 探测服务可能会返回
403,而127.0.0.1(或paperclip-dev主机名)可以正常工作。在断定身份验证出错之前,请先探测环回地址。 - 浏览器 QA 如果打开的是空白公司路径(例如
/FOR/...),应先切换到克隆数据中存在的公司键(例如/PAP/...),再判断重新播种是否失败。 - 对运行时或问题进行变更时遇到
409,通常表示另一个正在运行的任务持有所有权锁。不要强制操作;请等待,或通过问题进行协调。 - 完整重新播种后,克隆的 DB 可能包含与本地进程不匹配的复制运行时服务行。请重新运行受管理的启动流程并检查身份,不要轻信复制过来的行。
何时需要更改代码
只有当常规运维修复暴露出产品缺陷时,才进行代码更改。此类故障的示例:
- 本地端口占用进程检测使用了错误的
lsof参数 - 服务接管只信任根路径的
200,而不是/api/health - 端口占用进程接管时记录了请求的 cwd,而未验证占用进程的实际
/proc/<pid>/cwd,因此接管了同级工作区的进程 - 固定的服务端口在同级工作区之间发生冲突
- 未终止或替换不健康的被接管服务
- 重新播种后的运行时服务 id 未与本地进程注册表协调
在受影响服务的测试文件中添加有针对性的测试。对于工作区运行时修复,通常只需进行以下范围的验证:
pnpm exec vitest run server/src/__tests__/workspace-runtime.test.ts
git diff --check
提交逻辑上的代码更改,并在问题评论中附上提交链接。如果没有更改受版本控制的代码,请明确说明。
最终问题评论模板
请提供具体证据,不要只说“可以运行”。
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
- port owner identity: /proc/<pid>/cwd resolves inside the target worktree
- <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>
只有在满足所有成功条件后,才将问题标记为 done。否则,请将其标记为 blocked,并注明负责解除阻塞的人员及所需的具体操作。