关于 create-issue-interaction-ui
面向 Paperclip 维护者的指南,介绍如何新增议题线程交互类型,涵盖共享契约、服务端行为、UI、对话渠道、外部调用工具和文档。
主要功能
- 01定义共享类型与验证
- 02实现服务端生命周期和路由
- 03添加 UI 卡片与线程接线
- 04扩展对话渠道处理
- 05更新 CLI、MCP、插件 SDK 和文档
适用场景
- 01新增议题线程交互类型
- 02创建不同的负载与解决结果变体
- 03规划跨层实现与测试
MCP 相关
指南要求在 Paperclip 的 MCP 服务中添加 MCP 工具,复用现有的 createIssueThreadInteraction 调用路径,并测试其负载结构。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ paperclipai/ paperclip --skill create-issue-interaction-ui安装后在 Claude Code 中输入/create-issue-interaction-ui确认已加载。
详细介绍
原文在翻译后有更新,译文可能不是最新。
创建新的 Issue 线程交互 UI(开发者/维护者技能)
开发者/维护者技能。请勿将其安装到生产环境中的 Paperclip Agent 上。
此技能将引导 Paperclip 贡献者从共享契约开始,逐步为新的 Issue 线程交互类型添加 Issue 详情页接线、辅助工具和文档。这是一项面向开发者/维护者的技能:受众是在 paperclipai/paperclip 中修改代码的人类或编码 Agent,而不是在已部署的 Paperclip 公司中运行的运维 Agent。
请勿将其安装到生产环境中的 Paperclip Agent 上。本指南面向修改 Paperclip 项目本身的仓库贡献者。
适用场景
- 正在引入一种新的交互类型(紧凑型选择器、结构化评分、线程内审批卡片等)。
- 现有交互需要一个具有不同负载结构、验证方式或解决结果的平行变体(此时
ask_user_questions并不合适,因为选项数量、目标绑定或结果结构有所不同)。 - 评审者要求新卡片具备“与其他交互相同的审计、过期、替代和后续处理语义”。
不适用场景
- 为现有交互类型添加字段,且不需要新的负载架构。请直接修改现有验证器和 UI。
- 更改 Paperclip Agent 调用交互的方式。请更新
skills/paperclip或references/api-reference.md;这属于 Agent 指南,而不是卡片开发。 - 构建非线程 UI(Issue 详情侧边栏、项目看板小组件等)。这些 UI 有各自的组件约定。
心智模型
每种 Issue 线程交互都有四个组成部分:
| 层级 | 职责 |
|---|---|
| 共享 | 类型常量、负载/结果接口、Zod 验证器、导出的类型、共享测试覆盖。 |
| 服务端 | 服务的创建/接受/拒绝/响应、过期、替代、幂等性、活动日志、唤醒发送。 |
| UI | 卡片的待处理/已解决/已过期状态、测试数据、Storybook、Issue 线程/IssueDetail 接线。 |
| 辅助工具/文档 | CLI 命令、MCP 工具、插件 SDK 类型+宿主+测试路径、skills/paperclip 指南。 |
现有的四种类型是权威参考。选择最接近的一种,并复用其接线方式,而不要另行发明一套机制:
request_confirmation— 绑定到target的单项是/否确认,支持过期/替代。request_checkbox_confirmation— 针对不可变选项集的有限多选。ask_user_questions— 小型类型化表单,不绑定目标。suggest_tasks— 提议可由看板逐项接受的任务。
如果新卡片需要绑定目标并采用是/否式解决方式,请参照这两种 request_* 类型。如果它是结构化表单,请参照 ask_user_questions。如果它会生成可创建的子实体,请参照 suggest_tasks。
权威示例
目前最完整的端到端参考是复选框确认功能的推出过程(合并于 4d5322c82,GitHub PR #7649)。开始前请先阅读该差异:
git show --stat 4d5322c82
该实现所依据的计划保存在 Issue 文档 PAP-10415 中。如果你通过 Paperclip 本身开展这项工作,可将其用作自己计划文档的模板。
操作顺序
首先完成共享契约。这是最小且正确的改动,即使 UI 尚未定稿也可以提交;后续每一层都会读取其中的类型和验证器。
1. 共享契约(最小范围,优先落地)
涉及:
packages/shared/src/constants.ts— 将 kind 字符串添加到ISSUE_THREAD_INTERACTION_KINDS,并添加任意一个大小常量(参照REQUEST_CHECKBOX_CONFIRMATION_OPTION_LIMIT = 200)。packages/shared/src/types/issue.ts— 添加Option、Payload、Result和Interaction接口。扩展文件末尾的IssueThreadInteraction以及 payload/result 联合类型。packages/shared/src/types/index.ts— 重新导出新类型。packages/shared/src/validators/issue.ts— 为 payload、result 和创建输入变体添加 Zod 架构。适用目标绑定时,复用现有的requestConfirmationTargetSchema。packages/shared/src/validators/index.ts— 重新导出新架构。packages/shared/src/index.ts— 在包根目录重新导出。packages/shared/src/issue-thread-interactions.test.ts— 扩展表格测试,覆盖新的 payload 变体。
已经经过讨论并必须满足的验证不变量:
- 选项列表必须有数量上限(checkbox kind 使用 200;请选一个 UX 能紧凑呈现的数字)。
- 一个 payload 中的选项 ID 必须唯一,任何默认选择都必须引用已知 ID。
- 标签和描述的长度上限须与现有问题选项一致。不要擅自放宽上限。
- 目标绑定使用共享的
RequestConfirmationTarget架构,让过期处理沿用同一条代码路径。
2. 服务端服务和路由
涉及:
server/src/services/issue-thread-interactions.ts— 在以下位置添加该 kind:- 支持的 kind 列表(顶部附近的
SUPPORTED_KINDS); mapInteractionRow(解析 payload/result 的switch (row.kind));- 创建输入验证(
switch (data.kind)); - 接受、拒绝、响应和过期处理分支;
- 活动日志 payload 和后续唤醒 payload。
- 支持的 kind 列表(顶部附近的
server/src/routes/issues.ts— 扩展所有按 kind 分支处理的逻辑(尤其是 checkbox PR 中第 6096 行附近的响应形状分支)。server/src/__tests__/issue-thread-interactions-service.test.ts— 覆盖创建、携带结果接受、附带原因拒绝、目标过期、用户评论时取代、幂等冲突以及唤醒 payload 形状。server/src/__tests__/issue-thread-interaction-routes.test.ts— 覆盖创建以及 HTTP 响应/接受/拒绝行为、公司范围隔离和授权。
服务端不变量:
- 仅允许 Board 解决。由 Agent 发起的接受/拒绝必须走现有的 403 路径;不要添加按 kind 绕过限制的逻辑。
- 公司范围隔离。读取、写入、过期处理和取代操作都必须按
companyId过滤。绝不能只信任issueId。 - 目标过期。如果创建时指定了
target,且之后出现更新的 revision,则该交互必须以outcome: "stale_target"过期。不要自行实现过期逻辑——调用其他request_*kind 使用的同一辅助函数。 - 用户评论时取代。默认设置
supersedeOnUserComment: true,除非 payload 架构对此另有说明。 - 幂等性。必须遵循现有 kind 所用的确定性
idempotencyKey格式(<kind>:<issueId>:<decisionKey>:<revisionId>);重复的 POST 必须返回已有卡片,不得重复创建。 - 后续策略。支持
none、wake_assignee和wake_assignee_on_accept。根据提问者是阻塞等待答案(wake_assignee),还是只关心是否接受(wake_assignee_on_accept),选择相应的默认值。
3. UI 卡片与问题线程接线
修改:
ui/src/components/IssueThreadInteractionCard.tsx— 添加卡片组件 (例如RequestCheckboxConfirmationCard)和结果组件 (例如RequestCheckboxConfirmationResolution)。根据interaction.kind扩展现有的 switch。复用卡片外壳,不要引入 并行的卡片框架。ui/src/lib/issue-thread-interactions.ts— 添加类型化辅助函数,例如getCheckboxConfirmationSelectedLabels,让卡片保持声明式。ui/src/lib/issue-thread-interactions.test.ts— 测试辅助函数。ui/src/components/IssueThreadInteractionCard.test.tsx— 覆盖待处理、 已解决、过期、禁用/提交中和验证错误状态。ui/src/fixtures/issueThreadInteractionFixtures.ts— 为新类型至少添加 一个待处理和一个已解决的测试数据。ui/storybook/stories/questions-and-approvals/issue-thread-interactions.stories.tsx— 为 关键状态添加 Storybook 条目。ui/src/pages/IssueDetail.tsx— 扩展卡片渲染所依据的按类型分支 (回调接线、响应提交)。ui/src/components/IssueChatThread.tsx— 如果该类型会影响线程级 渲染(徽标、摘要、计数),则更新这里的按类型 switch。ui/src/api/issues.ts— 扩展类型化的 accept/reject/respond 请求体。
UI 不变量:
- 紧凑渲染。卡片必须能舒适地展示约 100 个选项 (限定滚动区域;已解决状态的摘要优先显示数量——不要将每个选中项都显示为行内标签)。
- 全选和清除选项必须位于卡片内,而不是全局菜单中。
- accept 请求体使用类型专属的字段名(例如
selectedOptionIds, 而不是 suggest-tasks 的selectedClientKeys)。不要复用其他类型的字段名。 - 过期、已取代和已接受状态必须使用不同的文案;复用现有的结果组件外壳。
4. CLI、MCP、插件 SDK 辅助工具
外部调用方必须能创建此新交互,而无需手动编写 JSON。修改:
cli/src/commands/client/issue.ts— 添加 CLI 子命令,或扩展通用交互创建路径。cli/src/__tests__/issue-subresources.test.ts— 覆盖新的标志组合。packages/mcp-server/src/tools.ts— 添加一个接受新请求体结构的 MCP 工具;复用现有的createIssueThreadInteraction代码路径。packages/mcp-server/src/tools.test.ts— 覆盖该工具的请求体结构。packages/plugins/sdk/src/types.ts— 添加类型化的CreateIssueThreadInteraction变体,让插件作者能够使用自动补全。packages/plugins/sdk/src/worker-rpc-host.ts— 扩展创建调用中的类型 switch。packages/plugins/sdk/src/testing.ts— 扩展测试框架,让插件能够端到端模拟此新类型。packages/plugins/sdk/tests/testing-actions.test.ts— 测试通过测试框架对新类型进行往返处理。
5. Agent 指引
修改:
skills/paperclip/SKILL.md— 在交互类型表中添加一行,包括: 适用场景、不适用场景,以及可复制的请求体示例。skills/paperclip/references/api-reference.md— 完整的请求体和结果 schema、验证限制、创建/响应请求体及错误码。
技能文本会由运行时 Agent 读取。请保持简洁,每个类型用一到两句话与相近类型明确区分。
请求审查前要运行的测试
复选框 PR 在 NODE_ENV=test 下恰好运行了以下这组针对性测试。新增类型时也采用相同的测试模式,并替换为新的测试文件:
NODE_ENV=test pnpm run preflight:workspace-links
NODE_ENV=test pnpm exec vitest run \
packages/shared/src/issue-thread-interactions.test.ts \
server/src/__tests__/issue-thread-interaction-routes.test.ts \
server/src/__tests__/issue-thread-interactions-service.test.ts \
ui/src/components/IssueThreadInteractionCard.test.tsx \
ui/src/lib/issue-thread-interactions.test.ts \
cli/src/__tests__/issue-subresources.test.ts \
packages/mcp-server/src/tools.test.ts \
packages/plugins/sdk/tests/testing-actions.test.ts
如果 UI vitest 因 act is not a function 失败,说明 shell 正在使用
NODE_ENV=production(它会采用 React 的生产构建)。请显式设置
NODE_ENV=test 后重新运行。
合并前检查清单
- 新类型已添加到
ISSUE_THREAD_INTERACTION_KINDS并导出。 - 负载和结果接口已进行版本化(从
version: 1开始)。 - Zod 验证器会强制执行选项、标签和描述的限制,并确保 ID 唯一。
- 目标绑定(如有)使用共享的
RequestConfirmationTarget路径。 - 服务会处理创建、接受、拒绝/响应、目标过期、用户发表评论时取代、幂等性、活动日志和唤醒后续流程。
- 路由遵循仅限看板的解析规则和公司范围限制。
- UI 会呈现待处理、已解决、过期、已禁用/提交中和验证错误状态;已解决状态下的大量选择应优先按数量汇总。
- 新类型已有固定数据和 Storybook 条目。
- CLI、MCP 和插件 SDK 辅助函数均支持新的负载格式,并有测试覆盖。
- 已更新
skills/paperclip/SKILL.md和references/api-reference.md。 - 上述针对性测试集全部通过;CI 检查通过。
评审中发现的反模式
这些问题出现在复选框 PR 评审讨论中,下次值得避免:
- 复用其他类型的接受负载字段名(例如使用
selectedClientKeys,而不是引入selectedOptionIds)。每种类型都有自己的字段名。 - 不通过现有的
request_confirmation辅助函数,而是编写并行的过期或取代逻辑。这会导致行为悄然偏离。 - 在已解决状态下渲染数百个已选选项标签。大量选择必须优先按数量汇总。
- 认为“API 足够通用”,便跳过插件 SDK、MCP 或 CLI 的覆盖。没有类型化辅助函数,外部调用方就无法使用新类型;之后就会出现 Agent 流程故障。
- 在服务器路由接受该类型之前,就将其加入技能指南。Agent 会尝试使用新类型,并在生产环境中收到 400 错误。