posthog/validating-and-publishing-canvases
> capabilities, reading the current version pointer, iterating on validation diagnostics, guarded publishing with expected_current_version_id, 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 canvas publish or build returns diagnostics or a conflict, or a task needs to understand canvas version history.
npx skills add https://github.com/PostHog/posthog --skill 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.
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.
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.Validation rejects undeclared literal calls (capability_missing_* diagnostics) so you can fix
them before publishing; dynamic ids it can only warn about, so keep the declarations complete.
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 react, react-dom, @posthog/quill, recharts, lucide-react, and dayjs),
forbidden_dynamic_import / forbidden_require / forbidden_inline_script,
invalid_path, capability_missing_insight / capability_missing_capture_event /
capability_missing_inline_queries,
dependency_not_admitted / dependency_version_mismatch, and path/size violations.
warning diagnostics don't block, but heed them: network_fetch / network_xhr mean the codereaches for the network directly — the sandbox will block it at runtime; use the ph bridge.
Two ways to save, both guarded:
canvas-publish-create with the complete project.canvas-edit-create with operations (each sets afile's complete content, or deletes it with content: null). Prefer this for small changes to a
large project; the guard is mandatory here because a diff's meaning depends on its base.
For a whole-project publish with canvas-publish-create:
expected_current_version_id — the current_version_id you read (or explicitnull on a first publish). Unguarded publishes can silently clobber concurrent edits.
prompt describing the change; it becomes the version-history entry's label.name only to rename the canvas (e.g. a first build of an untitled canvas).source (the head may have moved) and publish again — don't batch unrelated changes into one
version, and don't publish work-in-progress after every micro-edit.
same publish; nothing was saved.
The response returns the new current_version_id.
A publish queues a server-side build of the version. **The canvas does not update until the build
is ready, and nobody else is watching the result — you own it.** Poll canvas-builds-retrieve
every few seconds (up to ~2 minutes) until the build you queued is terminal:
queued/building — in progress; poll again shortly.ready — the canvas's published_build_id advances to this build (unless a newer publishsuperseded it first). The task's canvas work is done.
failed — read the build's error diagnostics, fix the project, and publish again. A failedbuild never replaces the last good one, so the canvas keeps rendering the previous version —
finishing the task here would leave the user with a stale canvas and a silent failure.
A 409 means the canvas moved past your base — a concurrent publish or a revert. The response
includes the live current_version_id. Never retry unguarded to force your version through:
canvas-source-retrieve.preserve them).
current_version_id.Each publish appends a full source version and moves the head pointer; users can revert to older
versions in the app (which republishes and rebuilds them). The guard matters because basing your
publish on the version you actually read is what keeps a user's revert, another agent's publish,
and your edit from silently erasing each other.
Take posthog/validating-and-publishing-canvases from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.