Skip to content

Skill: pass the template source on MCP uploads #511

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 2), section "The skill's MCP upload"

Cast: Opus 5.5, high
Repository: PostHog/context-mill (a commit on the branch of PostHog/context-mill#433)

Question

Does an agent following the send-transactional-email skill record where a template's file lives when it uploads through MCP?

Delivers end to end.

  • Upload step. The skill's upload step tells the agent to pass source on every MCP upload:
    • repository from git remote get-url origin, normalized to host/owner/name without credentials;
    • path relative to the repository root;
    • ref from git rev-parse HEAD.
  • SDK note. The skill says posthog-node fills these on its own when the app sends with templateFile() (posthog-node: fill the template source repository, path and ref on upload #510).
  • Changing a template. The skill says to change a code-managed template by editing its file in the repository, never through PostHog's template update tools and never through workflows-request-email-template-change, which is for agents without repository access. A person may also start a change from the template page with "Edit with your agent"; the prompt names the file and the key.
  • Evals. If the skill has evals, its upload eval checks that source is passed.

Test seams. The skill's existing eval or trigger checks in context-mill. No new harness.

Write scope. The send-transactional-email skill files on #433's branch.

Proof.

  • Local: an agent session following the skill uploads a template from a git checkout through the local MCP. The template API then shows all three source fields. Paste the transcript excerpt and the API response in the PR.

Acceptance criteria

Blockers

Blocked by #510 (the SDK behavior the skill describes).

How to build (map #461)

Activity

  1. self-assigned this
    on Oct 17, 2026
  2. 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-511-1
    Cast: Opus 5.5, high. Cap 150 minutes.

  3. Silthus commented on Oct 17, 2026

    @Silthus
    OwnerAuthor

    Resolution

    Shipped: four commits on Silthus:feat/send-transactional-email-skill, the branch of draft PostHog/context-mill#433. The PR stays a draft. Head 8b236c30c6774e3b77163ff626f3c242c041902c. fleet proof gate passes (HEAD reviewed in final read 6).

    • 5127da7 feat(skills): record a template's file on upload and change it in the file
    • 99eaa9a fix(skills): cover undo, early publish and no-repository changes
    • c128595 fix(skills): scope undo to templateFile and order the by-key upload
    • 8b236c3 fix(skills): name a published revert in STEP 8 and split the fallback

    What shipped

    One file changed: context/skills/omnibus/send-transactional-email/description.md.

    • STEP 5, upload. Pass source on every MCP upload:
      • repository: the git remote get-url origin URL as host/owner/name, without the scheme, user name, token or .git ending. There is an SSH example and an HTTPS-with-token example.
      • path: from the repository root (git rev-parse --show-toplevel), with a monorepo example.
      • ref: git rev-parse HEAD.
      • Leave out a field git can't give.
    • SDK note (matches posthog-node: fill the template source repository, path and ref on upload #510). posthog-node fills the same fields on its own uploads with templateFile(), from CI and hosting variables or the git checkout. It always sends a path, which falls back to the app's working directory. A repository or ref it can't find keeps the stored value. I checked this against _write_source in template_uploads.py.
    • New section "Change an email the app already sends".
      • With the repository, edit the file. Never use update, patch or design (403 immutable), and leave the template managed by code.
      • Live timing:
        • With templateFile(), the first send from the changed code uploads it, and a local run publishes it too.
        • Upload early only when the person asks, and warn that every running copy then sends the new version.
        • An undo to published content needs an upload.
        • By key, the deploy step uploads, or the agent uploads after the change.
      • Without the repository, tell the person which file to change. Where the MCP has workflows-request-email-template-change, call it and share the task link. An agent that can edit the file never calls it. This is true before and after Request a change: start a PostHog Code task that opens a PR for a code-managed template #513.
      • A prompt from Edit with your agent names the key, and the file when PostHog knows it. This is true before and after Edit with your agent: copy a prompt for a code-managed template's file #503.
    • STEP 8 (HTTP upload, the revert exception) and the STEP 9 immutable line now point at the new section.

    Proof

    • Evals: the skill has none, and context-mill has no eval harness, so there was nothing to extend.
    • Bundle test and gate: pnpm test passed 220 tests and pnpm build passed at 8b236c3, both through fleet heavy. The bundle test is unchanged. I added no test of the wording.
    • Ranking: the MCP SkillCatalog over the overlaid catalog gives rank 1 for all five trigger prompts at 8b236c3. The output is byte-identical to Agent skill: verify a sender, upload the template from the repo, send by key #476's. The description is unchanged.
    • Local agent sessions: Sonnet 5.5, a clean config, only the skill and a local PostHog MCP. Django ran from spec/code-managed-templates 07d7e0ee544 with the flag forced on. Each run had its own project with a verified sender. The scratch repo was a monorepo with the app in apps/web, and origin was https://x-access-token:ghp_…@github.com/example/acme.git with a fake token. Red runs used the skill at 965a656.
    Scenario Red (965a656) Green
    Set up from the repo root github.com/example/acme, apps/web/emails/welcome.html, HEAD same
    Agent started in apps/web same same
    "Add a line under the greeting" edits the file, no upload edits the file, no upload, explains it goes live on the first send after deploy
    "... make it live right away", then "undo that line" uploads both times, no warning uploads both times, warns about running copies, explains why the undo needs an upload

    Template API read-back after the green run at c128595 (personal API key):

    GET /api/projects/18/messaging_templates/
    {"key": "welcome", "managed_by": "code", "source_repository": "github.com/example/acme", "source_path": "apps/web/emails/welcome.html", "source_ref": "ad86435b7521cff0dbb52a917644428b79a4cc0e", "content_hash": "v1:cee89c39…"}
    
    • None of the 9 proof projects stores a repository with a token, user name or scheme.
    • No run called an update, patch or design tool.
    • The red did not reproduce. Sonnet 5.5 already passed all three fields and edited the file with the old wording. So this is a proof that the behavior holds, not a red-green test. The new text adds the running-copy warning and the undo rule, which only the green runs showed.
    • Full evidence: ~/.local/state/fleet/proof/PostHog/context-mill/433/c128595…/live-run/.

    Review

    Opus 5.5 at high effort, each round in a fresh subagent, recorded with fleet proof review-start and review-finish.

    Calls made

    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