/validating-and-publishing-canvases
Validate and publish a canvas source project safely: the source-project shape, declared capabilities, reading the current version pointer, iterating on validation diagnostics, guarded publishing with expected_current_version_id, staging a draft build and promoting it, waiting
Claude Code
$ npx -y skills add posthog/posthog --skill validating-and-publishing-canvases --agent claude-code
Codex
$ npx skills add posthog/posthog --skill validating-and-publishing-canvases -a codex -y
Cursor
$ npx skills add posthog/posthog --skill validating-and-publishing-canvases -a cursor -y
Gemini CLI
$ npx skills add posthog/posthog --skill validating-and-publishing-canvases -a gemini-cli -y
GitHub Copilot
$ npx skills add posthog/posthog --skill validating-and-publishing-canvases -a github-copilot -y
OpenCode
$ npx skills add posthog/posthog --skill validating-and-publishing-canvases -a opencode -y
Windsurf
$ npx skills add posthog/posthog --skill validating-and-publishing-canvases -a windsurf -y
SKILL.md
validating-and-publishing-canvases.SKILL.mdname: validating-and-publishing-canvases
description: >
Validate and publish a canvas source project safely: the source-project shape, declared
capabilities, reading the current version pointer, iterating on validation diagnostics, guarded
publishing with expected_current_version_id, staging a draft build and promoting it, waiting out
the queued build, and recovering from a 409 version_conflict or a 429 capacity limit without
overwriting concurrent work. Use whenever a canvas edit is ready to save, a draft build is wanted,
a canvas publish or build returns diagnostics or a conflict, or a task needs to understand canvas
version history.
Validating and publishing canvases
A canvas's source lives in PostHog, versioned per publish. Publishing is guarded: every edit is based on a specific version, and the server refuses to overwrite newer work. Every publish queues a server-side build, and the canvas renders the last successful build.
The source project
`canvas-source-retrieve` returns:
- `project` — `schemaVersion` (1), `files` (path → content), `entryHtml` (`"index.html"`),
`dependencies` (exact platform-pinned versions), `canvasSdkVersion`, `capabilities`.
- `current_version_id` — the version your edits are based on. Keep it; the publish needs it.
It is `null` for a canvas that has never been published — pass that `null` on the first publish.
Keep `index.html` and `dependencies` exactly as returned. You may add relative source files and admitted assets to the project. Use `?worker` for a self-contained module worker and represent binary assets as base64 entries in `assets`; new npm dependencies or dependency-version drift fail validation.
Declare capabilities
The host enforces `project.capabilities` at runtime, so an undeclared `ph` call builds fine and then dies in the rendered canvas. Declare:
- `capabilities.posthog.insights` — every insight short id the canvas passes to `ph.loadInsight`.
- `capabilities.posthog.captureEvents` — every event name it passes to `ph.capture`.
- `capabilities.posthog.inlineQueries: true` — when it calls `ph.query` at all.
- `capabilities.posthog.agentRequests: true` — when it calls `ph.agent.request`.
- `capabilities.connectors` — one `{ "provider", "tools" }` entry per third-party provider the
canvas reads through `ph.connectors.call`, listing every tool it calls on that provider. A provider is a native id (`github`) or `mcp:<server host>` (`mcp:mcp.calendly.com`). Unknown providers, unregistered native tools, and private MCP hosts fail validation; every declared tool must have `is_read_only: true` in the catalog. An upstream hint alone does not grant access. A canvas with connectors cannot declare shared state.
- `capabilities.network.origins` — each exact HTTPS origin used by `fetch`, `XMLHttpRequest`, or an
external stylesheet, image, font, media file, or frame. Remote scripts remain blocked. Do not include paths, credentials, queries, fragments, or wildcards. The host must be public: loopback and private IPs, single-label names like `intranet`, and the `.local`, `.localhost`, `.internal`, and `.home.arpa` suffixes are all rejected, so a local dev host such as `https://localhost:8010` fails validation with an `invalid_network_origin` error. Data sent to a declared origin leaves PostHog and appears in the capability review before promotion.
Before validation, inventory every literal external URL in every source file. Classify navigation links and `ph.openExternal()` URLs as navigation; they do not need a network origin. For every request or resource URL, declare its scheme + host + optional port only. Include every origin a request redirects to and every secondary origin a stylesheet references for fonts or images. Never infer that one CDN hostname covers another.
Validation rejects undeclared literal calls and resource URLs (`capability_missing_*` diagnostics) so you can fix them before publishing. Dynamic URLs and redirect destinations cannot be inferred, so the inventory is still required even when validation is clean.
Validate until clean
`canvas-validate-create` is side-effect free; call it as often as needed. Diagnostics carry `severity`, a stable `code`, a `message`, and (for file-specific problems) `path` and `line`:
- `error` diagnostics block publishing — fix all of them. Common ones: `import_not_allowed`
(bare imports are limited to the dependencies returned in the source project), `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` (a CSS variable named like a Quill token that the platform stylesheet sets on every element, so the value never applies; prefix your own variables), and path/size violations.
- `warning` diagnostics don't block, but heed them: `network_fetch` / `network_xhr` mean the code
reaches for the network directly. Declare the exact HTTPS origin or use the `ph` bridge.
Publish guarded
Publishing goes live immediately and is the default way to save a change, for a canvas's first version and for every follow-up edit. Every version records who published it and which task did the work, so the history stays reviewable after the fact. Stage a draft instead only when the user asked for a draft, a preview, or a review step — see "Draft, then promote" below.
Two ways to publish, both guarded:
- **Edits** — `canvas-edit-create` with `operations`. This is the default for any change to a canvas that already has source.
The guard is mandatory here because an edit's meaning depends on its base.
- **Whole project** — `canvas-publish-create` with the complete `project`, for a first version or to replace everything.
For an edit with `canva
Read more
name: validating-and-publishing-canvases description: > Validate and publish a canvas source project safely: the source-project shape, declared capabilities, reading the current version pointer, iterating on validation diagnostics, guarded publishing with expected_current_version_id, staging a draft build and promoting it, waiting out the queued build, and recovering from a 409 version_conflict or a 429 capacity limit without overwriting concurrent work. Use whenever a canvas edit is ready to save, a draft build is wanted, a canvas publish or build returns diagnostics or a conflict, or a task needs to understand canvas version history.
Validating and publishing canvases
A canvas's source lives in PostHog, versioned per publish. Publishing is guarded: every edit is based on a specific version, and the server refuses to overwrite newer work. Every publish queues a server-side build, and the canvas renders the last successful build.
The source project
`canvas-source-retrieve` returns:
- `project` — `schemaVersion` (1), `files` (path → content), `entryHtml` (`"index.html"`),
`dependencies` (exact platform-pinned versions), `canvasSdkVersion`, `capabilities`.
- `current_version_id` — the version your edits are based on. Keep it; the publish needs it.
It is `null` for a canvas that has never been published — pass that `null` on the first publish.
Keep `index.html` and `dependencies` exactly as returned. You may add relative source files and admitted assets to the project. Use `?worker` for a self-contained module worker and represent binary assets as base64 entries in `assets`; new npm dependencies or dependency-version drift fail validation.
Declare capabilities
The host enforces `project.capabilities` at runtime, so an undeclared `ph` call builds fine and then dies in the rendered canvas. Declare:
- `capabilities.posthog.insights` — every insight short id the canvas passes to `ph.loadInsight`.
- `capabilities.posthog.captureEvents` — every event name it passes to `ph.capture`.
- `capabilities.posthog.inlineQueries: true` — when it calls `ph.query` at all.
- `capabilities.posthog.agentRequests: true` — when it calls `ph.agent.request`.
- `capabilities.connectors` — one `{ "provider", "tools" }` entry per third-party provider the
canvas reads through `ph.connectors.call`, listing every tool it calls on that provider. A provider is a native id (`github`) or `mcp:<server host>` (`mcp:mcp.calendly.com`). Unknown providers, unregistered native tools, and private MCP hosts fail validation; every declared tool must have `is_read_only: true` in the catalog. An upstream hint alone does not grant access. A canvas with connectors cannot declare shared state.
- `capabilities.network.origins` — each exact HTTPS origin used by `fetch`, `XMLHttpRequest`, or an
external stylesheet, image, font, media file, or frame. Remote scripts remain blocked. Do not include paths, credentials, queries, fragments, or wildcards. The host must be public: loopback and private IPs, single-label names like `intranet`, and the `.local`, `.localhost`, `.internal`, and `.home.arpa` suffixes are all rejected, so a local dev host such as `https://localhost:8010` fails validation with an `invalid_network_origin` error. Data sent to a declared origin leaves PostHog and appears in the capability review before promotion.
Before validation, inventory every literal external URL in every source file. Classify navigation links and `ph.openExternal()` URLs as navigation; they do not need a network origin. For every request or resource URL, declare its scheme + host + optional port only. Include every origin a request redirects to and every secondary origin a stylesheet references for fonts or images. Never infer that one CDN hostname covers another.
Validation rejects undeclared literal calls and resource URLs (`capability_missing_*` diagnostics) so you can fix them before publishing. Dynamic URLs and redirect destinations cannot be inferred, so the inventory is still required even when validation is clean.
Validate until clean
`canvas-validate-create` is side-effect free; call it as often as needed. Diagnostics carry `severity`, a stable `code`, a `message`, and (for file-specific problems) `path` and `line`:
- `error` diagnostics block publishing — fix all of them. Common ones: `import_not_allowed`
(bare imports are limited to the dependencies returned in the source project), `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` (a CSS variable named like a Quill token that the platform stylesheet sets on every element, so the value never applies; prefix your own variables), and path/size violations.
- `warning` diagnostics don't block, but heed them: `network_fetch` / `network_xhr` mean the code
reaches for the network directly. Declare the exact HTTPS origin or use the `ph` bridge.
Publish guarded
Publishing goes live immediately and is the default way to save a change, for a canvas's first version and for every follow-up edit. Every version records who published it and which task did the work, so the history stays reviewable after the fact. Stage a draft instead only when the user asked for a draft, a preview, or a review step — see "Draft, then promote" below.
Two ways to publish, both guarded:
- **Edits** — `canvas-edit-create` with `operations`. This is the default for any change to a canvas that already has source.
The guard is mandatory here because an edit's meaning depends on its base.
- **Whole project** — `canvas-publish-create` with the complete `project`, for a first version or to replace everything.
For an edit with `canva
:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.
More skills from other plugins.
paddleocr-doc-parsing
Use this skill to extract structured Markdown/JSON from PDFs and document images—tables with cell-level precision, formulas as…
13c-metabolic-flux
Estimates intracellular metabolic fluxes from steady-state carbon-13 isotope-tracing measurements using validated atom maps,…
Questions about validating-and-publishing-canvases.
- Which coding agents can use validating-and-publishing-canvases?
- validating-and-publishing-canvases installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

