Skip to content

feat(canvas): typed canvas source tools and bundled authoring skills - #73725

Closed
k11kirky wants to merge 1 commit into
masterfrom
posthog-code/canvas-build-pipeline-phase-1
Closed

feat(canvas): typed canvas source tools and bundled authoring skills#73725
k11kirky wants to merge 1 commit into
masterfrom
posthog-code/canvas-build-pipeline-phase-1

Conversation

@k11kirky

@k11kirky k11kirky commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Note

Stacked PR chain (Graphite-style) — merge in this order:

  • posthog/posthog: #73725 (phase 1) → #73730 (phase 3) → #73731 (phase 4)
  • PostHog/code: #3824 (phase 1) → #3825 (phase 2) → #3826 (phase 3) → #3827 (phase 4)
  • Cross-repo: each posthog PR should deploy before the same-phase code PR ships (skills/tools/endpoints must exist before clients rely on them).

Problem

Canvas authoring today rides on a single tool (desktop-file-system-canvas-partial-update) driven by a large prompt the canvas composer embeds per generation. That locks canvas creation to the composer, gives agents no way to read or validate what they're editing, and validation happens only at runtime in the sandbox.

This implements phase 1 ("Contracts and skills") of the canvas application build pipeline plan (PostHog/code#3823): typed read/create/validate/publish canvas tools available to every task, the authoring contract extracted into bundled skills, and the existing single-file publish kept underneath a compatibility adapter so this ships before the build service.

Changes

API (posthog/api/file_system/) — new actions on DesktopFileSystemViewSet:

  • GET/POST /desktop_file_system/canvases/ — list canvases (optionally per channel) and create an empty canvas in a channel.
  • GET .../{id}/canvas/source/ — the canvas's source project plus current_version_id. Legacy meta.code canvases are presented as a synthetic web project (index.html + src/canvas.tsx).
  • POST .../{id}/canvas/validate/ — side-effect-free validation with structured diagnostics (severity, stable code, message, file, line): schema/paths/size limits, platform-pinned dependency checks, and the legacy runtime's import allowlist.
  • POST .../{id}/canvas/publish/ — publish a complete source project as the new head version, guarded by expected_current_version_id (409 version_conflict on a stale base; error diagnostics reject with 400 and leave the canvas untouched). Reduces to the same meta.code + version-history storage as the legacy PATCH endpoint (shared transactional core), so the composer path, undo/redo, and task-thread announcements behave identically.

The adapter/validation logic is pure and lives in posthog/api/file_system/canvas_source.py. No models or migrations — storage stays in FileSystem.meta until the build service ships.

MCP (services/mcp/definitions/core.yaml + regenerated tools/schemas) — five new enabled desktop-file-system-canvas* tools so any authorized task can resolve, read, validate, and publish canvases.

Skills (products/tasks/skills/) — the canvas authoring contract, previously an embedded mega-prompt in the desktop app, extracted into five bundled skills shipped through the existing skills distribution: building-canvases (routing + workflow), building-react-quill-canvases (+ starter scaffold reference), building-html-canvases, querying-canvas-data, validating-and-publishing-canvases.

Companion PR PostHog/code#3824 moves generation orchestration into core and swaps the composer prompt to skill routing. This PR should merge and deploy first (skills reach sandbox images, tools reach MCP) — the legacy tool and prompt keep working throughout.

How did you test this code?

Ran locally (I'm an agent; no manual app testing):

  • posthog/api/file_system/test/test_canvas_source.py (20 tests, no DB) — adapter contract: the synthetic project of a legacy canvas validates and round-trips to identical code (guards the read→edit→publish loop rejecting its own output); parameterized error diagnostics for traversal/absolute/backslash paths, extra files, missing component, unadmitted or drifted dependencies, non-whitelisted imports, dynamic import/require/script, and size limits; network calls warn without blocking (a comment mentioning fetch( must not brick a canvas).
  • posthog/api/file_system/test/test_canvas_source_api.py (11 tests) — the full generic-task loop (create → read source → validate → guarded publish → guarded edit), stale-base 409 leaving the canvas untouched, invalid-project 400 publishing nothing, cross-team and cross-channel list scoping (IDOR guard), legacy PATCH ↔ new source tools interop on one version history, first-publish task-thread announcement parity, and non-UUID channel ids mapping to 400/404 instead of a 500 (red/green: reproduced the 500 first).
  • posthog/api/file_system/test/test_canvas_publish.py (19 existing tests) — unchanged and green, proving the publish refactor preserved legacy behavior.
  • mypy --cache-fine-grained . repo-wide: no issues. ruff clean. hogli lint:skills clean. MCP schema snapshot + generate-tools suites green (92 tests). hogli build:openapi-schema --fail-on-warn passes (one new ENUM_NAME_OVERRIDES entry pins the shared error/warning severity enum).

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Docs update

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Authored by Claude (PostHog Code cloud task) from the plan in PostHog/code#3823, directed by @k11kirky. Skills invoked: /improving-drf-endpoints, /writing-tests, /writing-skills. Decisions worth knowing: phase 1 deliberately adds no models (the plan's source-version/build tables arrive with the cloud build service in phase 3), so guarded publishing reuses the existing meta.versions history via a shared transactional helper; canvas_validate is a POST but sits in the read-scope bucket because it is side-effect free; the severity enum collision with marketing analytics' UTM issues was resolved by pinning a shared DiagnosticSeverityEnum (both use error/warning), which renames that generated type — no non-generated consumers exist. The plan's "generic cloud task publishes via skills" integration test needs the sandbox eval harness and is left for the phase-2 PR.

Phase 1 of the canvas application build pipeline plan (PostHog/code#3823):

- Canvas source-project API on the desktop file system: list/create canvases,
  read the source project + current version, validate a candidate project
  (structured diagnostics), and publish guarded by
  expected_current_version_id. A compatibility adapter presents legacy
  meta.code canvases as a synthetic web project and reduces valid projects
  back to the single-file storage, so this ships before the build service
  with no storage changes.
- MCP tools (desktop-file-system-canvas-*) so any authorized local or cloud
  task can author canvases without the canvas composer's embedded prompt.
- Five bundled skills extracted from the canvas authoring contract:
  building-canvases, building-react-quill-canvases, building-html-canvases,
  querying-canvas-data, validating-and-publishing-canvases.

Generated-By: PostHog Code
Task-Id: 9e9a7b3c-f90d-4867-aa0d-b9acc83e26e1
@k11kirky k11kirky self-assigned this Jul 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hey @k11kirky! 👋

It looks like your git author email on this PR isn't your @posthog.com address (k11kirky@gmail.com). Since you're on the PostHog team, it's worth pointing your local git author email at your @posthog.com address. Why it matters:

  • Consistent work identity in git history — internal tooling that attributes commits to team members keys off your @posthog.com address.
  • Keeps team contributions easy to tell apart from external community ones when scanning history.

You can fix it for this repo with:

git config user.email "you@posthog.com"

Or set it globally with git config --global user.email "you@posthog.com". No need to redo this PR — just a nudge for next time. 🙂

@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

🤖 CI report

Bundle size — no change

Uncompressed size of every built .js bundle, compared against the base branch.

Total: 64.44 MiB · no change

No file changed by more than 1000 B.

Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report

Eager graph — within budget

How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.

Root Eager (shipped) Δ vs base Budget
entry (logged-out pages, app bootstrap)
src/index.tsx
1.24 MiB · 22 files no change ███░░░░░░░ 27.6% of 4.51 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
8.08 MiB · 3,014 files no change ████████░░ 83.2% of 9.71 MiB

🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx

Largest files eagerly shipped from src/index.tsx
Size File
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
24.6 KiB ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js
6.3 KiB ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js
4.5 KiB ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js
3.9 KiB ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js
1.4 KiB ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js
1.3 KiB src/RootErrorBoundary.tsx
912 B ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js
789 B src/scenes/ChunkLoadErrorBoundary.tsx
762 B src/index.tsx
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
281.5 KiB ../node_modules/.pnpm/posthog-js@1.407.2/node_modules/posthog-js/dist/rrweb.js
267.7 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
236.0 KiB src/taxonomy/core-filter-definitions-by-group.json
226.1 KiB ../node_modules/.pnpm/posthog-js@1.407.2/node_modules/posthog-js/dist/module.js
154.3 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
106.2 KiB src/lib/api.ts
94.0 KiB ../packages/quill/packages/quill/dist/index.js
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
90.6 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js

Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479

Toolbar bundle — eager 2.18 MiB within budget

What the toolbar ships to customer pages, measured from the esbuild output (minified, post-tree-shake). The eager set is the entry plus everything statically imported from it — fetched before any feature runs; deferred chunks load lazily. The eager guardrail is 5.72 MiB. Each output file must also stay below 10 MB, where CloudFront stops compressing it. The module boundary is enforced separately by check-toolbar-graph.

Metric Size Δ vs base Budget
Eager (shipped)
entry + static imports
2.18 MiB · 17 files no change ████░░░░░░ 38.1% of 5.72 MiB
Deferred (lazy) 2.07 MiB · 33 files no change n/a — loads on demand
Loader dist/toolbar.js 1.1 KiB no change █░░░░░░░░░ 5.8% of 19.5 KiB
Largest eagerly-shipped chunks
Size File
716.5 KiB dist/toolbar/toolbar-app-HMV4VZ5O.css
545.2 KiB dist/toolbar/chunk-chunk-3RADJBLD.js
484.3 KiB dist/toolbar/chunk-chunk-ZXJK34VQ.js
133.6 KiB dist/toolbar/chunk-chunk-6SQZIKIH.js
131.8 KiB dist/toolbar/chunk-chunk-T5KY5WYR.js
71.0 KiB dist/toolbar/toolbar-app-G6ARZY2E.js
69.0 KiB dist/toolbar/chunk-chunk-27JL52RE.js
35.6 KiB dist/toolbar/chunk-chunk-P4AKBHPC.js
20.9 KiB dist/toolbar/chunk-chunk-B7POBA4G.js
12.2 KiB dist/toolbar/chunk-chunk-PIK3PADE.js

Posted automatically by check-toolbar-size · sizes are toolbar output bytes (shipped, post-tree-shake) from the esbuild metafile

Dist folder size — 🔺 +29.7 KiB (+0.0%)

Total size of the built frontend/dist folder (all assets), compared against the base branch.

Total: 1355.90 MiB · 🔺 +29.7 KiB (+0.0%)

ℹ️ MCP UI apps size — 32 app(s), 17065.5 KB JS

Built size of each MCP UI app (main.js + styles.css).

App JS CSS
debug 599.5 KB 187.7 KB
action 457.8 KB 187.7 KB
action-list 564.3 KB 187.7 KB
cohort 456.8 KB 187.7 KB
cohort-list 563.3 KB 187.7 KB
email-template 456.6 KB 187.7 KB
error-details 472.4 KB 187.7 KB
error-issue 457.5 KB 187.7 KB
error-issue-list 564.2 KB 187.7 KB
experiment 561.5 KB 187.7 KB
experiment-list 565.1 KB 187.7 KB
experiment-results 563.2 KB 187.7 KB
feature-flag 567.1 KB 187.7 KB
feature-flag-list 570.9 KB 187.7 KB
feature-flag-testing 461.0 KB 187.7 KB
insight-actors 562.1 KB 187.7 KB
invite-email-preview 456.0 KB 187.7 KB
llm-costs 559.5 KB 187.7 KB
session-recording 458.6 KB 187.7 KB
session-summary 463.9 KB 187.7 KB
survey 458.4 KB 187.7 KB
survey-global-stats 562.2 KB 187.7 KB
survey-list 565.0 KB 187.7 KB
survey-stats 562.2 KB 187.7 KB
trace-span 457.2 KB 187.7 KB
trace-span-list 564.2 KB 187.7 KB
workflow 457.1 KB 187.7 KB
workflow-list 563.7 KB 187.7 KB
loops-review 461.2 KB 187.7 KB
query-results 745.5 KB 187.7 KB
render-ui 826.2 KB 187.7 KB
visual-review-snapshots 461.6 KB 187.7 KB

Copy link
Copy Markdown
Contributor Author

Superseded by #73874, which consolidates the full Canvas build pipeline into one reviewable PR.

@k11kirky k11kirky closed this Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant