AI USE CASES YOU CAN REUSE.English edition · Individuals, independents, small businesses
PostHog

Validation and publication of canvases

validating-and-publishing-canvases

In brief

Securely validate and publish a web source project: the form of the source project, the declared capabilities, the reading of the current version pointer, the iteration on validation diagnostics, the protected edition with expected current version id, the implementation of a construction project and its promotion, the waiting for the construction in queue, and the recovery from a 409 version conflict or a 429 capacity limit without overwriting the simultaneous work. Use every time a web edition is ready to record, a compilation version is desired, a canvas publishes or builds feedback diagnoses or conflict, or a task must include the history of the web version.

What it does

  • The source project
  • Declaring capabilities
  • Validate until cleaned

Who it is for, and an example

People whose task corresponds: Securely validate and publish a web source project: Liberation form, declared capabilities, reading of the current version pointer, iteration of validation diagnostics, secure publication with expective current version id, implementation of a construction and promotion project, waiting

Example request: "Validation and publication of canvases: Safely validate and publish a source web project: the form of the source project, declared capabilities, reading the current version pointer, l"

Securely validate and publish a web source project: the form of the source project, the declared capabilities, the reading of the current version pointer, the iteration on validation diagnostics, the protected edition with expected current version id, the setting up of Example written by iadujour.fr from the SKILL.md, not tested.

Install

Claude Code : /plugin marketplace add PostHog/skills, puis /plugin install posthog-all@posthog-skills (README).

/plugin marketplace add PostHog/skills
/plugin install posthog-all@posthog-skills

Works with

  • Claude Code

Source: K0: marketplace K1: K2: Other agents are not appointed.

Level: Intermediate

Supposes a project already in place at the publisher and the tools described in the K0.

Good to know

PostHog Official Deposit (github.com/PostHog/skills). Skills for product analytics, feature indicators, error tracking and team workflows.

SKILL.md excerpt

The original description, which the agent reads to decide whether to load the skill (in English):

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 d […]

Quoted from SKILL.md at commit 445764b .

The SKILL.md has 192 lines and 8 main sections:

  • The source project
  • Declare capabilities
  • Validate until clean
  • Publish guarded
  • After publishing: wait for the build
  • Draft, then promote
  • Recovering from 409 version_conflict
  • Version history semantics

Licence: MIT

Checked on 13 November 2026 : fichier de licence du dépôt.

The licence allows reuse: the full SKILL.md text is reproduced below, unmodified, with its licence notice.

Full SKILL.md text

Reproduced unmodified from PostHog/skills (commit 445764b) , © PostHog, under licence MIT (original licence file ).

Show the original SKILL.md (192 lines, in English)
---
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 `canvas-edit-create`:

- Use `str_replace` for a change inside a file: `old_string` is text copied from the file you read, with a few surrounding lines so it matches exactly one place.
  Set `replace_all: true` to change every match, for example a rename.
- Use `write` with the complete `content` for a new file or a full rewrite, `delete` to remove a file, and `rename` with `new_path` to move one.
- When the change needs a new capability, for example a `ph.state` scope, also send `capabilities` with the complete new capabilities (the current ones plus the addition) in the same edit.
  Do not switch to `canvas-publish-create` for that.
- Put every operation of one change into one call. They apply in order, and the whole edit is rejected if any operation fails.
- A 400 lists each failed operation by index. `edit_no_match` shows the closest lines of the file and `edit_ambiguous_match` lists the lines that match.
  Fix those operations from the diagnostic and send the edit again. If one replacement fails twice, `write` that whole file instead.
- The response returns the new `current_version_id`. Pass it to the next edit.
  When you published or edited the canvas earlier in this session, do not call `canvas-source-retrieve` before the next edit: you already know the source, and a 409 `version_conflict` tells you when the canvas changed.

For a whole-project publish with `canvas-publish-create`:

- Always pass `expected_current_version_id` — the `current_version_id` you read (or explicit
  `null` on a first publish). Unguarded publishes can silently clobber concurrent edits.
- Include a short `prompt` describing the change; it becomes the version-history entry's label.
- Pass `name` only to rename the canvas (e.g. a first build of an untitled canvas).
- Publish once per requested change. If the user asks for another edit afterwards, re-read the
  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.
- A 429 means the team's build capacity is temporarily exhausted. Wait ~30 seconds and retry the
  same publish; nothing was saved.

The response returns the new `current_version_id`.

## After publishing: wait for the build

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.** The publish or edit response
already carries `build.build_status`. When it is `ready` or `failed`, act on it without another call.
Only while it is `queued` or `building`, 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 publish
  superseded it first). The task's canvas work is done.
- `failed` — read the build's error diagnostics, fix the project, and publish again. A failed
  build 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.

Runtime error reports (filed on the authoring task when a rendered canvas throws) name the build
they came from. A report from an **older build id** is history, not evidence about your current
code — check it against the build you just published before acting on it. In particular, a report
that a documented `ph` API is undefined (e.g. `ph.state`) means that artifact was baked by an
older host runtime: republish so a current build replaces it. Never "fix" it by removing the API
or its capability declaration.

## Draft, then promote

Publishing goes live the moment its build is ready, and that is the default. Use a draft only
when the user asked for one: a preview to look at first, a review step before going live, or an
explicit "don't publish yet". A draft is a real, buildable version that is never the head: the
live canvas keeps rendering the current version until someone promotes the draft. This is
different from `canvas-validate-create`, which only compile-checks and produces no build or
preview.

1. **Stage** — `canvas-draft-create` with the complete `project` (same shape, capabilities, and
   validation as a publish). No `expected_current_version_id`: a draft is based on nothing and
   conflicts with nothing. The response returns the draft's `version_id`, its queued `build`, and
   `capability_widening` — the insights, capture events, inline queries, and network origins the
   draft declares beyond the live version. Surface a non-empty widening to the user before
   promoting; it is the access the change would newly grant.
2. **Wait for the build** — poll `canvas-builds-retrieve` until the draft's build is terminal, the
   same way you would after a publish. A failed draft build is fixed by staging a new draft, not by
   promoting.
3. **Preview** — read the draft's files with `canvas-source-retrieve` passing its `version_id`; once
   its build is `ready` the app renders that draft when the version is opened. The draft is **not**
   in `canvas-versions-retrieve` (that lists published history only) and cannot be reverted onto —
   list pending drafts with `canvas-drafts-retrieve`.
4. **Promote** — when the user approved the draft or asked to go live; a draft the user asked to
   review stays staged until they say so. `canvas-promote-create` makes the draft the live head.
   Pass
   `expected_current_version_id` (the live `current_version_id` from `canvas-source-retrieve`); it
   is guarded exactly like a publish and 409s on a moved head (recover as below). A draft whose
   build is still `ready` goes live with no rebuild; otherwise a fresh build is queued, so wait for
   it as in step 2. Promote is the only path from a draft to live.

## Recovering from 409 version_conflict

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:

1. Re-read the source with `canvas-source-retrieve`.
2. Re-apply your edits to the fresh source (the new head may contain the user's own edits —
   preserve them, and do not report them in your reply).
3. Publish again with the new `current_version_id`.

## Version history semantics

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.
Source. Card written by iadujour.fr from: Frontmatter, sections and body of the deposit. Checked on 13 November 2026. PostHog/skills .

In the directory

Use cases and guides that share a theme, industry or tool with this skill.

IN THE DIRECTORY

Related use cases.

Directory cards that share at least two precise keywords with this skill.

Dev

Assist 1,500 developers with a continuous open source fork at BPCE

Luc Barnaud, chief data and IA officer of BPCE, has deployed a fork of the open source continuous tool to 1,500 developers, after removing a post-based solution deemed too intrusive and insufficiently secure, before a generalization.

Tool: Continue (fork open source)Type: Spotted
SAME PUBLISHER

Other PostHog skills: DevOps and deployment

Whole category
PostHogDevOps and deployment

Add integration

instrument-integration

Add PostHog integration to your application.

Tools: Claude CodeLevel: Advancedupdated 12/11/2026
PostHogDevOps and deployment

Migrating since customer change.io

migrating-from-customer-io

Move Customer.io automations to PostHog as a workflow.

Tools: Claude CodeLevel: Intermediateupdated 12/11/2026
PostHogDevOps and deployment

LLM Call Management

managing-llm-prompts

Createlife, version, release and stream LLM prompts with PostHog fast management.

Tools: Claude CodeLevel: Advancedupdated 11/11/2026
DISCUSSION

Comments.

A field note, a question, a precision on this skill? Comments are reviewed before publication.

Published comments

Loading comments…

    Leave a comment

    Moderation charter. Every comment is reviewed before publication. We refuse hate speech, insulting or discriminatory language, advertising, spam and promotional links, off-topic posts and other people’s personal data.