Skip to content

feat(file-system): optimistic-concurrency guard on canvas publish#72365

Merged
adboio merged 2 commits into
masterfrom
posthog-code/canvas-publish-version-guard
Jul 20, 2026
Merged

feat(file-system): optimistic-concurrency guard on canvas publish#72365
adboio merged 2 commits into
masterfrom
posthog-code/canvas-publish-version-guard

Conversation

@adboio

@adboio adboio commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Problem

posthog desktop canvas publish action (behind the desktop-file-system-canvas-partial-update mcp tool) has no concurrency guard

this is fine right now because every canvas write is a full file replace, but i am changing that (PostHog/code#3594), so version control is more important

Changes

  • add expected_current_version_idas a concurrency guard on the PATCH; returns 409 on conflict

How did you test this code?

tested e2e manually, paired with PostHog/code#3594

Automatic notifications

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

Docs update

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Built with Claude (PostHog Code cloud task) while reworking desktop canvas generation to edit canvases as workspace scratch files (PostHog/code#3594). Skills invoked: /improving-drf-endpoints, /writing-tests. Considered a tool-side check-then-PATCH guard in the agent instead; rejected because only a server-side check inside the row lock is atomic, and this endpoint already owns version composition — one canonical writer beats three divergent ones.


Created with PostHog Code

@github-actions

Copy link
Copy Markdown
Contributor

Hey @adboio! 👋

It looks like your git author email on this PR isn't your @posthog.com address (adambowker98@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. 🙂

adboio added a commit to PostHog/code that referenced this pull request Jul 20, 2026
…as action

canvas_publish now calls the desktop-fs canvas action, which owns version
composition server-side, passing the checkout's version as
expected_current_version_id so a stale publish is rejected atomically
(409 version_conflict) instead of guarded by a non-atomic tool-side
check-then-PATCH. Deletes the tool-side compose logic and the raw meta
PATCH; backends predating the guard field ignore it and publish
unguarded, so the tool degrades gracefully.

Server side: PostHog/posthog#72365.

Generated-By: PostHog Code
Task-Id: 2b3d176e-3023-41d2-92e8-81eda7c12a8c
The desktop canvas publish action appends a version onto whatever the live
head is, so a publisher working from a stale read (a concurrent publish, or
a user's undo moving currentVersionId back) silently becomes the new head
over newer work. Add an optional expected_current_version_id to the publish
payload: when provided and the canvas has moved past it, the publish is
rejected with 409 version_conflict (carrying the live version id) inside
the existing row lock. Omitting the field keeps the previous unguarded
behavior for older callers.

Also mirror the client's linear-discard semantics: publishing after an
undo now truncates the redo tail past currentVersionId instead of leaving
unreachable versions in the history.

Generated-By: PostHog Code
Task-Id: 2b3d176e-3023-41d2-92e8-81eda7c12a8c
@github-actions

github-actions Bot commented Jul 20, 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.16 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.23 MiB · 22 files no change ███░░░░░░░ 28.6% of 4.29 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
8.18 MiB · 2,997 files no change █████████░ 88.4% of 9.25 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.3 KiB ../node_modules/.pnpm/posthog-js@1.404.1/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
235.5 KiB src/taxonomy/core-filter-definitions-by-group.json
223.4 KiB ../node_modules/.pnpm/posthog-js@1.404.1/node_modules/posthog-js/dist/module.js
164.0 KiB src/queries/validators.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
105.9 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
93.2 KiB ../packages/quill/packages/quill/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

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

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

Total: 1344.14 MiB · 🔺 +3.9 KiB (+0.0%)

Playwright — all passed

All tests passed.

View test results →

@adboio
adboio force-pushed the posthog-code/canvas-publish-version-guard branch from a28994d to 9bec143 Compare July 20, 2026 16:29

adboio commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@posthog

posthog Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

🦔 ReviewHog reviewed this pull request

Found no issues worth raising, so no review was posted.

@adboio
adboio marked this pull request as ready for review July 20, 2026 17:35
@adboio adboio added the stamphog Request AI approval (no full review) label Jul 20, 2026 — with Graphite App
@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

🦔 Hogbox preview · ✅ ready

▶ Open the preview

🔑 Login test@posthog.com / 12345678 (demo data)
🧩 Running this PR's backend and frontend, on the PostHog :master base
🔗 Link stable across rebuilds — a re-push swaps the box underneath, the URL stays
🔒 Access tailnet only (PostHog VPN)
🛠️ Admin inspect & debug state in hogland
💤 Idle sleeps after ~30 min idle (snapshot to S3, zero node cost) and wakes on your next visit in ~30s, behind a brief "waking up" screen

commit 9bec143 · box box-7d0736525e20 · ready in 807s (push → usable) · build log · rebuilds on every push, torn down on close

@greptile-apps

greptile-apps Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "chore: update OpenAPI generated types" | Re-trigger Greptile

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additive, backward-compatible API contract change (optional guard field, unguarded behavior preserved when omitted) with parameterized tests covering exactly the failure modes that matter (false-positive guard, fail-open on stale/null base, undo/redo truncation), and it's already been reviewed by Greptile (agent reviewer) plus a 👍 from the security bot with no concerns raised.

  • 👍 on the PR from greptile-apps[bot], hex-security-app[bot].
Gate mechanics and policy version
Gate Result
prerequisites all clear
deny-list no deny categories matched
size 109L, 2F substantive, 275L/9F incl. docs/generated/snapshots — within ceiling
tier T1-agent / T1d-complex (275L, 9F, cross-cutting, feat)
stamphog 2.0.0b3 .stamphog/policy.yml @ d5d244c · reviewed head 9bec143

@adboio
adboio merged commit f8967ac into master Jul 20, 2026
388 of 429 checks passed
@adboio
adboio deleted the posthog-code/canvas-publish-version-guard branch July 20, 2026 18:32
@deployment-status-posthog

deployment-status-posthog Bot commented Jul 20, 2026

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-07-20 19:08 UTC Run
prod-us ✅ Deployed 2026-07-20 19:22 UTC Run
prod-eu ✅ Deployed 2026-07-20 19:26 UTC Run

adboio added a commit to PostHog/code that referenced this pull request Jul 20, 2026
…as action

canvas_publish now calls the desktop-fs canvas action, which owns version
composition server-side, passing the checkout's version as
expected_current_version_id so a stale publish is rejected atomically
(409 version_conflict) instead of guarded by a non-atomic tool-side
check-then-PATCH. Deletes the tool-side compose logic and the raw meta
PATCH; backends predating the guard field ignore it and publish
unguarded, so the tool degrades gracefully.

Server side: PostHog/posthog#72365.

Generated-By: PostHog Code
Task-Id: 2b3d176e-3023-41d2-92e8-81eda7c12a8c
adboio added a commit to PostHog/code that referenced this pull request Jul 20, 2026
…as action

canvas_publish now calls the desktop-fs canvas action, which owns version
composition server-side, passing the checkout's version as
expected_current_version_id so a stale publish is rejected atomically
(409 version_conflict) instead of guarded by a non-atomic tool-side
check-then-PATCH. Deletes the tool-side compose logic and the raw meta
PATCH; backends predating the guard field ignore it and publish
unguarded, so the tool degrades gracefully.

Server side: PostHog/posthog#72365.

Generated-By: PostHog Code
Task-Id: 2b3d176e-3023-41d2-92e8-81eda7c12a8c
adboio added a commit to PostHog/code that referenced this pull request Jul 20, 2026
…as action

canvas_publish now calls the desktop-fs canvas action, which owns version
composition server-side, passing the checkout's version as
expected_current_version_id so a stale publish is rejected atomically
(409 version_conflict) instead of guarded by a non-atomic tool-side
check-then-PATCH. Deletes the tool-side compose logic and the raw meta
PATCH; backends predating the guard field ignore it and publish
unguarded, so the tool degrades gracefully.

Server side: PostHog/posthog#72365.

Generated-By: PostHog Code
Task-Id: 2b3d176e-3023-41d2-92e8-81eda7c12a8c
adboio added a commit to PostHog/code that referenced this pull request Jul 20, 2026
…as action

canvas_publish now calls the desktop-fs canvas action, which owns version
composition server-side, passing the checkout's version as
expected_current_version_id so a stale publish is rejected atomically
(409 version_conflict) instead of guarded by a non-atomic tool-side
check-then-PATCH. Deletes the tool-side compose logic and the raw meta
PATCH; backends predating the guard field ignore it and publish
unguarded, so the tool degrades gracefully.

Server side: PostHog/posthog#72365.

Generated-By: PostHog Code
Task-Id: 2b3d176e-3023-41d2-92e8-81eda7c12a8c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stamphog Request AI approval (no full review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant