Destinations: draft, publish, and revisions lifecycle for agent-driven management
@mayteio is already working on this.
Since Jul 28, 2026.
- Dominant language
- Python
- Stars
- 39.9k
- Forks
- 3.4k
- Avg merge
- 6h 51m
- Merged PRs (30d)
- 232
Description
Background
Workflows now has a full agent-first management lifecycle (#66380): content edits to active workflows stage as drafts instead of mutating live config, agents test drafts before going live, publishing requires an impact preview with a signed confirm token, and every live-content change lands in an append-only revision history with restore-to-draft rollback.
Destinations (hog functions) have none of this. The cdp-functions-* MCP tools see heavy real-world use, including a steady stream of cdp-functions-partial-update calls, and every one of those against an enabled destination is a hot edit: the serializer validates, filters bytecode recompiles, and the post_save signal reloads workers immediately. The change is live on the next matching event with no staging, no preview, and no rollback beyond hand-reverting from the activity log.
This issue brings destinations the core of that lifecycle - drafts, publish with a confirm gate, revisions with restore - plus the remediation surface agents need to monitor and fix destinations (cross-function stats, invocation history, rerun, sample events). It is deliberately smaller than the workflows effort: destinations are stateless per event, so none of the follow-live hardening (parked runs, skip-forward, reschedule sweeps) that dominated that project applies here. Publish impact is entirely "future traffic behaves differently from worker reload onward".
Current state
Already in place:
POST /hog_functions/{id}/invocationstest-invokes with mocked async functions and accepts a full unsavedconfiguration(MCP-exposed). This is the natural "test the draft" primitive.metrics,metrics/totals, andlogsvia the sharedAppMetricsMixin/LogEntryMixin.- 11 MCP tools in
products/cdp/mcp/with secret redaction and sensibleexclude_params. - Activity log records full config diffs for
hog,inputs, andfilters. - Validation layers (
posthog/cdp/validation.py,posthog/cdp/filters.py) covering inputs schema, hog/liquid templating, bytecode compilation, and cohort inlining.
Missing: any draft, version, or revision concept; publish/discard/restore endpoints; cross-function stats; a REST invocation-history endpoint (posthog/api/hog_invocation_results.py is wired only into the workflows viewset); an API-reachable sample-event source for test payloads.
Design decisions
- Draft lives on the model, revisions are a rollback log. Same shape as the shipped HogFlow pattern:
draftcolumns onHogFunction, an append-onlyHogFunctionRevisiontable that is never the source of truth. Revision-rows-as-source-of-truth was explored for workflows and deliberately not taken; destinations are the strongest case for the shipped pattern since they are pure follow-live. - Hog functions keep their own lifecycle rather than becoming one-node workflows. The execution engine is already shared (workflows embed hog functions as action nodes), a stateless per-event invocation is far cheaper than a workflow run, and the installed base of standalone destinations and
cdp-functions-*agent usage needs safety now. Convergence of the management surface is a possible future, not a prerequisite. - No tool renames. The existing
cdp-functions-*names have a large installed base. New tools extend the same prefix; existing tools gain draft-routing behavior behind the flag. - Draft routing is agent-only. Content edits from agent event sources (MCP, posthog-code, wizard, CLI) against an enabled destination stage into a draft. Web UI, headless API keys, and internal programmatic writers keep direct edits. Disabled destinations always edit directly. Event-source detection fails closed to direct-edit.
- Type scope:
destinationonly for v1. Transformations add execution-order semantics,site_*types add transpiled-JS concerns, andinternal_destinationrows are written by other products' code rather than agents. - Content vs live fields.
hog,inputs,inputs_schema,filters,mappings,maskingare draftable content.name,description,enabledalways apply live. - Enable with an open draft refuses.
enabled=trueon a destination with an open draft returns an error directing the caller to publish or discard first. No implicit publish, no silently enabling stale config. - One confirm token closes both races. The publish preview mints a
TimestampSignertoken over{id}:{draft_updated_at}:{live_updated_at}(15 min expiry). Either the draft changing or the live config changing between preview and publish invalidates the token and forces a re-preview. This is both the "you saw the preview for exactly this draft" guarantee and the live-clobber protection. - Preview is a slim field-level diff in v1. Which of filters/inputs/code/mappings/masking changed, computed from comparing draft to live. Consequence analysis (filters widening/narrowing direction, function health, matched-event-volume estimates) is a fast-follow, not a v1 blocker.
- Token gate only when enabled. Publishing a draft on a disabled destination has no live impact and skips the confirm requirement.
- Revision bumps compare user content only. Derived fields (
bytecode,transpiled,filters.bytecode) are stripped before comparison. Background re-saves (e.g.refresh_affected_hog_functionsrecompiling filters when an action or cohort changes) must never create revisions or touch drafts - enforced by a test that fails loudly if the content-comparison set widens, not by convention. - Revisions are append-only, forever. No pruning in v1; snapshots are small JSON. No backfill from the activity log: the first tracked write also snapshots the pre-change content, matching workflows.
- Feature flag
destinations-revisions, fail-closed. Flag off (or any error evaluating it) means today's behavior: direct live edits. The kill switch is always safe.
Plan
Track 1: draft, publish, revisions (behind destinations-revisions)
PR 1: model + migration (deployed ahead of the code PR)
HogFunction gains draft (JSON content snapshot), draft_updated_at, draft_encrypted_inputs, version. New append-only HogFunctionRevision (team, hog_function, version, content, created_by; unique on function+version). Secrets partitioned out of draft and revision snapshots. post_save skips worker reload when only draft columns changed.
PR 2: draft cycle + revisions API
Agent-source content edits to enabled destinations route to _write_draft (draft = live snapshot merged with existing draft merged with this edit, so publish is a plain copy). publish revalidates through the normal serializer (recompiles filters and hog bytecode), bumps the revision, nulls draft columns, then reloads workers. discard_draft is idempotent. invocations gains use_draft=true with secret rehydration. base_updated_at optimistic concurrency on both draft and live writes. Preview + confirm token per design decisions. revisions list, revisions/<version> detail, revisions/<version>/restore (revision content becomes the draft; 409 on an existing draft unless overwrite=true). Rollback is just another publish, so preview and confirm apply unchanged.
PR 3: MCP tools
cdp-functions-publish, cdp-functions-discard-draft, cdp-functions-list-revisions, cdp-functions-get-revision, cdp-functions-restore-revision, cdp-functions-enable / cdp-functions-disable (status-only; mixed status+content updates rejected, matching workflows). Guardrails: no create-as-enabled over MCP; summary serializer for MCP list responses. Tool descriptions and responses spell out the staging behavior ("draft staged; not live until cdp-functions-publish") so agents discover the cycle mid-conversation.
Rollout: destinations-revisions internal, then selected teams, then 100%. Flag off is always today's behavior.
Track 2: remediation surface (independent of track 1, no flag)
The monitor-and-fix loop: a scout checks overall destination health, digs into a failing destination, fixes it or notifies the user, and replays failed events. None of this depends on drafts, so it ships independently and in any order.
- Enable the existing
rerunandmetrics-totalsoperations for MCP (config only). metrics/globalendpoint +cdp-functions-global-stats(most-failing-first across the team's destinations).- Wire
hog_invocation_resultsinto the hog function viewset +cdp-functions-list-invocations/cdp-functions-get-invocation. cdp-functions-sample-event: server-side fetch of a recent event matching given filters, accepted in the request body so unsaved configs can use it; removes the need for agents to hand-constructglobals.
Follow-ups (cut from v1, additive on the same primitives)
- Preview consequence analysis: filters widening/narrowing direction (widening means more events flow to the third party from reload onward; narrowing means events silently stop being delivered), function health in the preview, matched-event-volume estimates.
- Builder awareness: emit
resource_editedon HogFunction saves and listen in the configuration scene - a "this destination was changed elsewhere" reload/keep-mine banner, plus a visible indicator when an agent has staged a draft. - A
building-destinationsskill (template selection, patch/test/publish loop, filters and inputs gotchas) and an error-copy papercut pass - after observing real agent usage of the cycle. Known hotspots from MCP analytics: template retrieval failing on guessed ids, test invocations failing on hand-rolled globals (largely addressed by the sample-event tool), and delete errors. - Revision backfill from the activity log, and revision pruning.
- Dedicated signing key for the publish confirm token (flagged by review:
TimestampSignerwith nokey=implicitly falls back toSECRET_KEY, against.agents/security.md). Fix jointly with the identical HogFlow confirm tokens the pattern was copied from; needs a fail-closed env-provisioned key alongside the code change.
Out of scope
- Transformations,
site_*types, andinternal_destinationin the draft cycle. - Web UI adoption of the draft/publish cycle (agent surfaces only, matching workflows).
- Template upgrade flow (migrating a function pinned to an old template sha via the draft cycle) - a natural follow-up built on these primitives, tracked separately.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.