关于 validating-and-publishing-canvases
这项技能说明如何验证并发布 PostHog canvas,适用于编辑或维护 canvas 的人员。它涵盖能力声明、版本保护、构建检查,以及按需暂存草稿和审核后发布。
主要功能
- 01检查源文件与项目依赖
- 02声明运行时能力和网络来源
- 03运行验证并处理诊断
- 04基于版本保护发布或编辑
- 05构建完成后检查状态
- 06暂存草稿并经审核提升
适用场景
- 01发布新建的 canvas 项目
- 02在保留并发修改的前提下编辑
- 03在上线前预览并审核草稿
MCP 相关
文档指出 PostHog AI(Max)的 MCP 工具可用于 canvas 相关能力声明中的连接器调用;未说明此技能本身通过 MCP 提供操作。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ posthog/ ai-plugin --skill validating-and-publishing-canvases安装后在 Claude Code 中输入/validating-and-publishing-canvases确认已加载。
详细介绍
验证和发布画布
画布的源代码存储在 PostHog 中,每次发布都会创建一个版本。发布受到保护:每次编辑都基于特定版本,服务器会拒绝覆盖更新的内容。每次发布都会将构建任务加入队列,由服务器端执行,画布会呈现最近一次成功构建的结果。
源项目
canvas-source-retrieve 会返回:
project—schemaVersion(1)、files(路径 → 内容)、entryHtml("index.html")、dependencies(平台固定的精确版本)、canvasSdkVersion、capabilities。current_version_id— 编辑所基于的版本。请保留此值;发布时需要用到。对于从未发布过的画布,此值为null— 首次发布时请传入此null。
请完整保留返回的 index.html 和 dependencies。你可以向项目添加相对路径的源文件和获准使用的资源。对于独立模块 Worker,请使用 ?worker;二进制资源应作为 base64 条目放在 assets 中。新增 npm 依赖或依赖版本漂移都会导致验证失败。
声明能力
主机会在运行时强制执行 project.capabilities,因此未声明的 ph 调用虽然能成功构建,却会在渲染后的画布中失败。请声明:
capabilities.posthog.insights— 画布传给ph.loadInsight的所有 insight 短 ID。capabilities.posthog.captureEvents— 画布传给ph.capture的所有事件名称。capabilities.posthog.inlineQueries: true— 只要调用了ph.query,就需要声明。capabilities.posthog.agentRequests: true— 只要调用了ph.agent.request,就需要声明。capabilities.connectors— 画布通过ph.connectors.call读取第三方提供方的数据时,每个提供方都需要一个{ "provider", "tools" }条目,并列出该提供方下调用的所有工具。提供方可以是原生 ID(github)或mcp:<server host>(mcp:mcp.calendly.com)。未知提供方、未注册的原生工具和私有 MCP 主机都会导致验证失败;目录中的每个已声明工具都必须满足is_read_only: true。仅有上游提示并不会授予访问权限。带有连接器的画布不能声明共享状态。capabilities.network.origins—fetch、XMLHttpRequest或外部样式表、图像、字体、媒体文件或框架所使用的每个 HTTPS 来源(origin)。远程脚本仍会被拦截。不要包含路径、凭据、查询参数、片段或通配符。主机必须是公开的:回环地址和私有 IP、intranet这类单标签名称,以及.local、.localhost、.internal和.home.arpa后缀都会被拒绝,因此https://localhost:8010这类本地开发主机会因invalid_network_origin错误而无法通过验证。发送到已声明来源的数据会离开 PostHog,并在提升前的能力审核中显示。
验证前,请清点每个源文件中的所有字面量外部 URL。将导航链接和 ph.openExternal() URL 归类为导航;它们不需要网络来源。对于每个请求或资源 URL,只声明协议 + 主机 + 可选端口。包括请求重定向到的每个来源,以及样式表引用字体或图像时涉及的每个次级来源。切勿假定一个 CDN 主机名可以覆盖另一个。
验证会拒绝未声明的字面量调用和资源 URL(capability_missing_* 诊断),让你能在发布前修复问题。动态 URL 和重定向目标无法推断,因此即使验证通过,仍必须进行清点。
验证直至无问题
canvas-validate-create 不会产生副作用;可根据需要随时调用。
诊断信息包含 severity、稳定的 code、message,以及(针对文件相关问题的)
path 和 line:
error诊断会阻止发布 — 请全部修复。常见问题:import_not_allowed(裸导入仅限源项目返回的依赖),forbidden_dynamic_import/forbidden_require/forbidden_inline_script、invalid_path、capability_missing_insight/capability_missing_capture_event/capability_missing_inline_queries/capability_missing_agent_requests/capability_missing_network_origin、dependency_not_admitted/dependency_version_mismatch、platform_token_redeclared(CSS 变量名称与 Quill 令牌相同,而平台样式表会在每个元素上设置该变量,因此该值不会生效;请为自定义变量添加前缀),以及路径/大小违规。warning诊断不会阻止发布,但请留意:network_fetch/network_xhr表示代码会直接访问网络。请声明准确的 HTTPS 源,或使用ph桥接接口。
发布防护
发布会立即上线,并且是保存更改的默认方式,适用于画布的第一个版本和之后的每次编辑。每个版本都会记录发布者以及完成这项工作的任务,因此事后仍可查看历史记录。只有当用户要求草稿、预览或审核步骤时,才暂存草稿——请参阅下文“先创建草稿,再提升”。
两种发布方式,均带防护:
- 编辑 — 使用
canvas-edit-create和operations。对于任何已有源代码的画布更改,这是默认方式。 此处必须使用防护,因为编辑的含义取决于其基础版本。 - 整个项目 — 使用
canvas-publish-create和完整的project,适用于发布第一个版本或替换全部内容。
使用 canvas-edit-create 编辑时:
- 在文件中修改内容时使用
str_replace:old_string是从你读取的文件中复制的文本,需包含几行上下文,以确保它只匹配一个位置。 设置replace_all: true可更改所有匹配项,例如重命名。 - 对于新文件或完整重写,使用带有完整
content的write;使用delete删除文件;使用带有new_path的rename移动文件。 - 如果更改需要新增功能(例如
ph.state作用域),还要在同一次编辑中发送完整的新capabilities(即当前项加上新增项)。 不要因此改用canvas-publish-create。 - 将一次更改的所有操作放在同一个调用中。操作会按顺序应用;任何操作失败,整个编辑都会被拒绝。
- 400 响应会按索引列出每个失败的操作。
edit_no_match会显示文件中最接近的几行,edit_ambiguous_match会列出匹配的行。 根据诊断结果修正这些操作,然后重新发送编辑请求。如果同一项替换连续失败两次,就改用write重写整个文件。 - 响应会返回新的
current_version_id。将它传给下一次编辑。 如果你在本次会话中已经发布或编辑过该画布,在下一次编辑前不要调用canvas-source-retrieve:你已经知道源代码;画布发生变化时,409version_conflict会告知你。
使用 canvas-publish-create 发布整个项目时:
- 始终传入
expected_current_version_id——你读取到的current_version_id(首次发布时明确传入null)。不带防护的发布可能会悄悄覆盖并发编辑。 - 添加简短的
prompt描述更改;它会成为版本历史记录的标签。 - 仅在重命名画布时传入
name(例如,为未命名画布首次构建时)。 - 每项用户要求的更改发布一次。如果用户之后又要求编辑,请重新读取源代码(头部版本可能已变化)并再次发布——不要将无关更改合并到一个版本中,也不要在每次微小编辑后发布尚未完成的工作。
- 429 表示团队的构建容量暂时已满。等待约 30 秒,然后重试相同的发布操作;不会有任何内容被保存。
响应会返回新的 current_version_id。
发布后:等待构建完成
发布会将该版本的服务端构建加入队列。构建就绪前,画布不会更新,而且没有其他人会关注结果——你需要负责到底。 发布或编辑响应中已包含 build.build_status。如果状态为 ready 或 failed,无需再次调用即可据此处理。只有在状态为 queued 或 building 时,才每隔几秒轮询一次 canvas-builds-retrieve(最多约 2 分钟),直到你加入队列的构建进入终态:
queued/building— 正在进行;稍后再次轮询。ready— 画布的published_build_id会更新为此构建(除非有更新的发布先将其取代)。该画布任务已完成。failed— 阅读构建的错误诊断信息,修复项目,然后重新发布。失败的构建绝不会取代上一个成功构建,因此画布会继续渲染之前的版本——如果此时就结束任务,用户看到的仍是过时的画布,而且失败不会有任何提示。
渲染后的画布发生错误时,系统会在创作任务上记录运行时错误报告,并注明其来源构建。来自较旧构建 ID 的报告只是历史记录,不能作为当前代码存在问题的证据——采取行动前,请核对它与刚发布的构建是否相符。尤其是,如果报告指出文档中记载的 ph API 未定义(例如 ph.state),说明该产物由较旧的宿主运行时生成:重新发布,让当前构建替换它。切勿通过移除该 API 或其能力声明来“修复”此问题。
先创建草稿,再发布上线
构建就绪后,发布会立即上线,这是默认行为。只有用户要求时才使用草稿:例如先预览、上线前先审核,或明确要求“暂时不要发布”。草稿是一个真实且可构建的版本,但不会成为当前版本:有人将草稿提升为当前版本之前,线上画布会继续渲染当前版本。这与 canvas-validate-create 不同;后者只进行编译检查,不会生成构建或预览。
- 暂存 — 使用完整的
project调用canvas-draft-create(形状、能力和验证要求均与发布相同)。无需提供expected_current_version_id:草稿不基于任何版本,也不会与任何版本冲突。响应会返回草稿的version_id、排队中的build,以及capability_widening——草稿声明的、超出当前线上版本范围的洞察、捕获事件、内联查询和网络来源。在提升草稿之前,向用户说明所有非空的扩展权限;这些是变更将新增授予的访问权限。 - 等待构建 — 轮询
canvas-builds-retrieve,直到草稿的构建进入终态,方式与发布后相同。草稿构建失败时,应通过暂存新草稿来修复,而不是提升失败的草稿。 - 预览 — 调用
canvas-source-retrieve并传入草稿的version_id,读取草稿文件;构建就绪后,打开该版本时,应用会渲染此草稿。草稿不会出现在canvas-versions-retrieve中(该接口仅列出已发布的历史版本),也不能回退到草稿——使用canvas-drafts-retrieve列出待处理的草稿。 - 提升 — 用户批准草稿或要求上线时再执行;如果用户要求先审核,就应继续暂存草稿,直到用户明确同意。
canvas-promote-create会将草稿设为线上当前版本。传入expected_current_version_id(从canvas-source-retrieve获取的线上current_version_id);其保护机制与发布完全相同,如果当前版本已变更就会返回 409(按下文说明处理)。如果草稿的构建仍为ready,则无需重新构建即可上线;否则会将新构建加入队列,因此需按照步骤 2 等待。提升是将草稿上线的唯一途径。
从 409 version_conflict 中恢复
409 表示画布已超出你的基准版本——发生了并发发布或回退。响应中包含当前的 current_version_id。切勿在不加保护的情况下重试,强行发布你的版本:
- 使用
canvas-source-retrieve重新读取源内容。 - 将你的修改重新应用到最新源内容上(新的头版本可能包含用户自己的修改——请保留这些修改,不要在回复中提及)。
- 使用新的
current_version_id再次发布。
版本历史语义
每次发布都会追加一个完整的源版本并移动头指针;用户可以在应用中回退到较早的版本(这会重新发布并重新构建这些版本)。版本保护很重要,因为以你实际读取的版本为基础进行发布,才能避免用户的回退、其他 Agent 的发布和你的修改在不知不觉中互相覆盖。