fix(agent): let MCP publish channel CONTEXT.md and canvases#3451
fix(agent): let MCP publish channel CONTEXT.md and canvases#3451jakesciotto wants to merge 3 commits into
Conversation
A channel's CONTEXT.md and freeform canvases are not files on disk — they're
stored in PostHog and published by the agent via the PostHog MCP sub-tools
`desktop-file-system-instructions-partial-update` and
`desktop-file-system-canvas-partial-update`.
The destructive-sub-tool gate classified any `*-partial-update` sub-tool as
destructive, so these publishes were forced behind an extra "modify live PostHog
data" approval on top of the plan approval the user already gave — redundant on
desktop and inconsistent in cloud runs. That gate exists to protect PostHog
product data (flags/experiments/notebooks), but these two sub-tools write the
app's own artifacts, which are the explicit output of the generation task.
Exempt exactly those two sub-tool names from `isPostHogDestructiveSubTool` so
they ride the same silent-allow path as create-family exec sub-tools. Uses an
exact-name allowlist, not a prefix, so the broader
`desktop-file-system-{partial-update,destroy}` sub-tools that rename/delete whole
channels stay gated. Plan mode still blocks the write until the plan is approved,
so CONTEXT.md co-design is unchanged.
Generated-By: PostHog Code
Task-Id: ab56d0cc-fb60-4554-a576-88307e823e67
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
|
React Doctor found no issues in the changed files. 🎉 Reviewed by React Doctor for commit |
|
| "desktop-file-system-canvas-partial-update", // freeform canvas publish | ||
| ]); |
There was a problem hiding this comment.
When mcp__posthog__exec is already allowed, these exact subtool names now skip the destructive prompt for any model-supplied id. A call to desktop-file-system-instructions-partial-update or desktop-file-system-canvas-partial-update can publish to a different channel or canvas than the approved generation task, so the live PostHog write is no longer scoped to the user-approved artifact.
Rule Used: When implementing new features, ensure that owners... (source)
Learned From
PostHog/posthog#31236
…ifact id The previous approach exempted the CONTEXT.md/canvas publish sub-tools from the destructive gate by name, which let the agent publish to ANY artifact id without approval — it could silently overwrite an arbitrary channel's CONTEXT.md or any canvas. Bind the auto-allow to the exact artifact the task owns instead: - Revert `isPostHogDestructiveSubTool` to a pure classifier (these publishes are destructive again). - The generation prompts now embed a machine-readable authorized-write marker (sub-tool + id) via a shared helper. The agent captures those ids off the *user* message only (it can't forge one) onto the session. - The permission gate skips the destructive prompt for a sanctioned publish only when the call's `id` matches an authorized id — and never in plan mode, so CONTEXT.md co-design is preserved. Any other id still prompts. This carries the target id through the app-authored prompt, the one trusted source available on both local and cloud runs without backend changes. Generated-By: PostHog Code Task-Id: ab56d0cc-fb60-4554-a576-88307e823e67
…ctions tag The freeform authorized-write marker was appended after `</canvas_generation_instructions>`, so it leaked into `extractCanvasInstructions(...).stripped` and broke freeformPrompt.test.ts, which asserts the stripped text is exactly the user's instruction. Move the marker inside the tag body. It's still in the raw prompt text the agent scans, and the conversation UI already collapses that tag — so the visible message stays the bare instruction. Generated-By: PostHog Code Task-Id: ab56d0cc-fb60-4554-a576-88307e823e67
Problem
A channel's CONTEXT.md and its freeform canvases are not files on disk — they live in PostHog, and the agent publishes them via the PostHog MCP sub-tools
desktop-file-system-instructions-partial-updateanddesktop-file-system-canvas-partial-update.The destructive-sub-tool permission gate classified every
*-partial-updateas destructive, so after the user already approved the generation plan, publishing hit a second, redundant "modify live PostHog data" approval (and behaved inconsistently in cloud runs). That gate exists to protect the user's PostHog product data (flags, experiments, notebooks) — not the app's own artifacts, which are the whole point of the generation task.The hard part: the write must stay bound to the artifact the task actually owns. A blanket exemption by sub-tool name would let the agent publish to any id — silently overwriting an arbitrary channel's CONTEXT.md or any canvas. And the target id can't be threaded through structured session metadata without PostHog backend changes, because these tasks default to cloud runs whose session metadata is rebuilt server-side.
Changes
sub-tool+id) via a shared helper (@posthog/shared, rendered as an invisible HTML comment). This is the one trusted, in-repo source of the target id that works on both local and cloud runs.idmatches an authorized id — and never in plan mode, so CONTEXT.md co-design is preserved. Any other id still prompts.isPostHogDestructiveSubToolstays a pure classifier (these publishes are destructive; the id-scoped exemption lives in the handler).How did you test this?
posthog-exec-gate.test.ts— id extraction from the exec command, the sanctioned-sub-tool predicate, and destructive classification (18 passed).permission-handlers.test.ts— matching id auto-allows without a prompt; a different id still prompts; plan mode never auto-allows (30 passed).authorized-write-marker.test.ts(shared) — build/parse round-trip, multiple markers, malformed markers (4 passed).pnpm --filter @posthog/{shared,agent,ui} typecheckand Biome on all changed files — clean.Automatic notifications
Created with PostHog Code