Skip to content

Edit with your agent: copy a prompt for a code-managed template's file #503

Closed

Description

@Silthus

Map: Map: send code-managed email templates with one PostHog call
Spec: Spec: copy-as-code and open-a-PR for code-managed templates (revision 3), sections "Edit with your agent (stack A)" and "Usage events"

Cast: Opus 5.5, high
Repository: PostHog/posthog (frontend only)
Stack: A, second slice

Question

Can a person start a change to a code-managed template from its page, and hand their coding agent a prompt that names the file, the contract, and how the change goes live?

Delivers end to end.

Test seams.

  • Kea logic test at the API boundary:
    • the starter comes from the API field;
    • copy is disabled until the sentence changes;
    • the clipboard holds the sentence plus the context verbatim;
    • no write request.
  • A story for the popover with a path, without a path, and expanded.
  • Prior art: messageTemplateLogic.test.ts; clipboard and pending handling of feat(workflows): copy a workflow as @posthog/workflows code PostHog/posthog#104452 (fork branch feat/workflows-copy-code).

Write scope. products/workflows/frontend/TemplateLibrary/: a new popover component and its story, MessageTemplate.tsx, messageTemplateLogic.ts, MessageTemplateMenu.tsx, templateCopy.ts.

Proof.

  • Red: the "copy holds the sentence plus the context" logic test fails.
  • Green: the logic tests pass.
  • Local:
    1. Upload a template from a file inside a git checkout through posthog-node (with posthog-node: fill the template source repository, path and ref on upload #510, or with an explicit source).
    2. Open the template, complete the sentence, and copy the prompt.
    3. Paste it into a coding agent session in that checkout. The agent edits the file.
    4. Send through the SDK. The dev mail catcher shows the change, and the first send uploads (template_outdated, then success).
  • Screenshots at 1280px with the side panel open and at full width, in the PR.

Acceptance criteria

  • The copied prompt starts with the person's sentence and carries the server's context block verbatim.
  • A template without a path still gets a usable prompt, with no dialog.
  • No request leaves the page.
  • Screenshots and the agent round trip in the PR.

Blockers

Blocked by #512 (the context and starter fields).

How to build (map #461)

  • /tdd red-green-refactor, outside-in, at the seams named above. Real Postgres, Valkey and ClickHouse; mock only what the seam section allows.
  • Repo skills apply where the diff touches their area: /writing-tests, /writing-ui-components, /writing-kea-logics, /writing-user-facing-copy, /writing-code-comments, /improving-drf-endpoints, /routing-outbound-api-calls.
  • Read code from origin/spec/code-managed-templates (the fork's integration branch for feat(messaging): upload, send and lock code-managed email templates PostHog/posthog#118924), not from master.
  • Public repo: no usage numbers, customer names, internal metrics, Slack content or security reasoning in code, commits or the PR. Fixture addresses use example.com. The private note the map points at is background only; never quote it.
  • Shipped means a draft PR handed off for review (see the map's Notes for remotes and branches). Never mark ready, enqueue or merge. The conductor names the stack's integration branch.

Activity

  1. changed the title [-]Copy as code: copy a code-managed template as its HTML file[/-] [+]Edit with your agent: copy a prompt for a code-managed template's file[/+] on Oct 17, 2026
  2. self-assigned this
    on Oct 17, 2026
  3. Silthus commented on Oct 17, 2026

    @Silthus
    OwnerAuthor

    Runtime: T3 child task
    Task: node:delegated-task:command%3Amcp%3A1778d880-5a25-404c-80d0-25fc5ccca3b2%3Adelegate-task%3Awf-email-impl-503-1
    Cast: Opus 5.5, high.

  4. Silthus commented on Oct 17, 2026

    @Silthus
    OwnerAuthor

    Note

    🤖 Opus 5.5 responding on behalf of Michael

    Resolution

    Landed 62c695af6cb on spec/template-agent-edit as a fast-forward from 0ba8f7972c2. The ship ticket #516 owns the PR and the review.

    • Button: a code-managed template shows "Edit with your agent" in the Save slot. The read-only preview, Duplicate and "Stop managing in code" are unchanged.
    • Notice: "This template is managed by code, so it can't be changed here. To change it, edit its file, or let your coding agent do it."
    • Popover (EditWithAgentButton.tsx), anchored to the button:
      • "Describe the change" text area, focused, prefilled with change_prompt_starter, caret at the end;
      • collapsed "Show full prompt" with the read-only prompt;
      • "Copy prompt", disabled with "Describe the change first" while the text is blank or equals the starter.
    • Prompt: the sentence as typed, a blank line, then change_prompt_context verbatim. The frontend builds nothing else.
    • State: in messageTemplateLogic only. The sentence survives closing and reopening the popover.
    • After copy: the spec's toast, and the popover closes. No request leaves the page.
    • Events: message template agent prompt opened {template_id, has_path, has_repository}, message template agent prompt copied (plus variable_count), message template duplicated {template_id, code_managed}, message template released from code {template_id}. No prompt text, path or repository in properties.

    Proof

    Red (red.log), the new tests on the base tree: 7 failed, 30 passed.

    • copies the sentence and the server context verbatim, and sends nothing: TypeError: logic.actions.setAgentPromptOpen is not a function.
    • The duplicate and release cases: Expected: "message template duplicated" … Received: "$pageview".

    Green:

    • messageTemplateLogic.test.ts: 37 passed (green.log).
    • Every suite under TemplateLibrary/ plus lib/components/Git: 7 suites, 88 passed, nothing skipped (green-library.log).
    Seam: messageTemplateLogic, API mocked at HTTP Asserts
    starter as served, starter without its trailing space, blank copyAgentPromptDisabledReason is "Describe the change first"
    starter from the API field, then close and reopen the sentence is kept, copy is enabled, opened event with exact properties
    copy navigator.clipboard.writeText gets the sentence, \n\n, the context verbatim; no PATCH or POST; popover closed; spec toast; copied event with variable_count: 2
    existing duplicate and release tests one added assertion each for the new events

    Local gate on the landed tree:

    • tsgo --noEmit --checkers 1 in frontend/ through fleet heavy: exit 0.
    • pnpm --filter=@posthog/frontend fix: exit 0.
    • pnpm install --frozen-lockfile: consistent.
    • The pre-push hook (hogli ci:preflight --strict): "failures": 0.
    • No backend change, so no build:openapi or mypy run.

    Browser check (Storybook from this worktree, real App scene, API mocked at HTTP, log): the text area is focused with the caret at 33 of 33 characters; the clipboard holds the expected prompt; the popover closes; reopening keeps the sentence; zero non-GET /api/ requests.

    Screenshots (MockHog project, example.com repository, invented copy, no customer data). Narrow is a 560px window, about the scene of a 1280px window with the side panel open. Wide is 1440px.

    Narrow (560px) Wide (1440px)
    Page: button and new notice page page
    Popover: starter, copy disabled starter starter
    Popover: change described described described
    Full prompt shown full prompt full prompt
    After copy: toast, popover closed after copy after copy

    Committed stories, Products/Workflows/Template library/Edit with your agent: with a path, without a path, full prompt shown.

    Agent round trip, partial:

    1. A scratch git checkout (origin https://github.com/example/app.git) with emails/welcome.html committed.
    2. The real server builder template_change_prompt.change_prompt ran on that file, with its commit as source_ref. That is the function Code-managed templates: the change prompt on the template API #512 proved through the API.
    3. Its starter and context fed the page. Copied through the UI: the clipboard text.
    4. Pasted into a claude -p session (Sonnet) in the checkout, with only read and edit tools. The agent changed the button color in the file and nothing else (diff). It did not upload, and it said that a send-by-key app needs a re-upload (session).

    Gaps

    Calls made

    • Starter as the default. The sentence is null until the person types, and a selector falls back to the starter. So a template that loads after the popover mounts still shows its starter.
    • Disabled rule trims both sides. The starter with its trailing space deleted still counts as "not described".
    • variable_count counts the distinct {{ variables.<name> }} in the server's context, with the server's name pattern. The frontend doesn't scan the template content.
    • No pending state. The copy is a local clipboard write, with no network request. Failure uses copyToClipboard's toasts ("Could not copy prompt to clipboard", or the HTTPS warning), and the popover stays open with the sentence.
    • Cmd+Enter copies, the same as the button.
    • The button keeps Lemon's dropdown chevron, the convention for a button that opens a popover. It is disabled while the template loads.
    • Events in the logic, not in MessageTemplateMenu.tsx. The page menu and the notice both dispatch duplicateTemplate, so one event covers both. templateCopy.ts and MessageTemplateMenu.tsx are unchanged.
    • change_prompt_* joined the server-owned fields, so a released template's save never sends them.
    • Dependencies: the story's play uses @testing-library/dom, which the lint rule requires. products/workflows now declares it and user-event as devDependencies, the way products/legal_documents does. The lockfile gains 6 lines. I dropped an unrelated unlayer-types bump that pnpm install added.
    • No review ran in this ticket. Ship stack A: edit a code-managed template with your agent #516 owns review.

    Follow-ups

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions