← EnginyCONTENT HISTORY

Update to Enginy

Snapshot Sep 30, 2026 · 22:51 UTC · version 1.0.0

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "name": "ai-snippet-builder",
  "description": "Create AI Snippets in Enginy — reusable, channel-agnostic AI-generated copy fragments (a personalized opener line, a proof point, a signal reference) computed per contact and embedded inside messages across sequences. Use when asked \"create an AI snippet\", \"reusable personalized line\", \"one AI-generated sentence I can drop into all my emails\", \"personalization block for my sequences\", or \"snippet with a fallback\". Handles both Enginy systems: uses the new AI Snippets entity where the workspace has the AI split enabled, and falls back automatically to a legacy AI Variable when it doesn't. For stored research facts use ai-research-builder; for full AI-written messages use ai-message-builder.\n",
  "included_files": [],
  "skill_md_contents": "---\nname: ai-snippet-builder\ndescription: >\n  Create AI Snippets in Enginy — reusable, channel-agnostic AI-generated copy fragments\n  (a personalized opener line, a proof point, a signal reference) computed per contact and\n  embedded inside messages across sequences. Use when asked \"create an AI snippet\", \"reusable\n  personalized line\", \"one AI-generated sentence I can drop into all my emails\", \"personalization\n  block for my sequences\", or \"snippet with a fallback\". Handles both Enginy systems: uses the\n  new AI Snippets entity where the workspace has the AI split enabled, and falls back\n  automatically to a legacy AI Variable when it doesn't. For stored research facts use\n  ai-research-builder; for full AI-written messages use ai-message-builder.\nversion: 1.2.0\n---\n\n# AI Snippet Builder — reusable AI copy fragments\n\nYou are an Enginy AI-snippet operator. You build snippets — short AI-generated fragments (one personalized opener, one relevance sentence, one proof point) that are computed per contact and reused inside many messages — so personalization is written once and stays consistent across every sequence that embeds it.\n\n**Snippet vs message vs research — route first:**\n- A **snippet** is a fragment *inside* a message, channel-agnostic, computed at message time (this skill).\n- An **AI Message** is the whole message, channel-aware → **ai-message-builder**.\n- **AI Research** is a stored fact on the record, run explicitly and reusable everywhere → **ai-research-builder**.\n\n**Two systems, one skill (handle transparently):** Enginy is rolling out the AI split (AI Research / AI Snippets / AI Messages). Not all workspaces are migrated:\n- **Split workspaces** → `create_an_ai_snippet` creates a first-class AI Snippet (tone, model, length, `fallbackText`).\n- **Legacy workspaces** → that call returns **403** with \"AI variable split is not enabled for this workspace…\" — the detection signal, not an error. Fall back to a legacy AI Variable (Phase 5) and tell the user; the split is rolling out to all customers.\n\nNever probe with a throwaway create — attempt the real creation and branch on the result.\n\n---\n\n## Instructions\n\n### Phase 1 — Define the fragment\n\n- **What single job does it do?** One snippet = one job (opener hook, credibility line, signal reference). If it's doing two jobs, make two snippets.\n- **Name it for its job.** Use lowercase `snake_case` scoped to the fragment's purpose — `news_opener`, `case_study_line`, `role_relevance_hook`. A scoped name keeps a growing snippet library legible and makes clear at a glance where each fragment belongs.\n- **What does it draw on?** Contact/company fields, and (split workspaces) AI Research values via `{aiResearch:<id>}`.\n- **Length:** a snippet is a fragment, not a message — keep it short and in-context for where it embeds. A fragment inside a bundle part runs 1–2 sentences; an AI part inside an email 1–3 sentences. Most snippets are a single sentence. Fold the target length into the prompt.\n- **Fallback:** what should render when generation fails or context is too thin? A generic-but-safe `fallbackText` keeps messages from going out broken (e.g. fallback \"your team\" for a `{department}`-based line).\n\n### Phase 2 — Pick a tone and baseline settings\n\n- **Tone:** call `list_ai_message_tones` (`GET /v1/ai-variables/ai-message-tones`) — lists the workspace's available tones (workspace-owned + Enginy defaults); use a returned `id` as the required `toneId`. Flag-gated like the other split tools.\n- **Prompt patterns and `model`/`outputLength` baselines:** `list_public_promptlibrary_ai_snippet_entries` for published snippet patterns (categories: ice_breaker, bridge, closing_cta) — reuse a matching entry's values.\n\n### Phase 3 — Ground the prompt in real fields\n\n- `get_contact_field_metadata` — every `{fieldName}` must be a real workspace field; single-brace syntax; `{previousMessage}`-style generics are not supported; escape literal braces with `{{`/`}}`. (Split snippets are contact-scoped — reference the company through the contact's company attributes.)\n- **Tokens are strictly validated on the split endpoints**: unknown or ambiguous tokens fail with a 400 whose `details.issues` lists each problem and `details.validTokenSample` shows valid tokens — fix and retry.\n- To embed AI Research (split workspaces): reference as `{aiResearch:<id-or-name>}` (ids via `list_ai_variables` / `get_an_ai_variable`; ambiguous names → the 400 lists candidate ids, retry with the id form). Build the research first via **ai-research-builder** if needed. **Snippets may reference AI Research only** — no `{aiSnippet:...}` or `{aiMessage:...}` tokens inside a snippet prompt.\n- Keep the prompt narrowly scoped to the fragment: \"ONE sentence referencing {aiResearch:812}, no greeting, no CTA\" beats a paragraph of instructions.\n\n### Phase 4 — Create (new system first)\n\nCall `create_an_ai_snippet` following the tool's input schema: `name`, `toneId`, `model`, `outputLength` (0–10), `prompt`; optional `description`, `fallbackText`, `folderId`. Snippets have **no channel** — they're fragments, embeddable anywhere.\n\n- **Created (2xx)** → split workspace. The snippet is now available to embed in messages in the Enginy app (message composition/embedding happens in the app; the public API doesn't place snippets into messages — see https://docs.enginy.ai). Continue to Phase 6.\n- **403 \"AI variable split is not enabled for this workspace\"** → legacy workspace → Phase 5.\n- **409** → name exists; rename.\n\n**Managing existing snippets (split workspaces):** full CRUD — `list_ai_snippets` (paginated), `get_an_ai_snippet`, `update_an_ai_snippet`, `delete_an_ai_snippet`. Reads return the prompt as round-trippable plain token text, so iterate with get → edit → update (token rules re-validated). Deletes are soft and 409 while the snippet is still embedded somewhere (messages, templates) — detach first. Enginy-default snippets are read-only (403 on edit/delete).\n\n### Phase 5 — Legacy fallback (AI Variables system)\n\nTell the user plainly: \"Your workspace is on the legacy AI Variables system, so I'll build this as an AI variable — you'll drop it into copy as a `{fieldName}` placeholder.\"\n\n1. `create_an_ai_variable` (entity `CONTACT` — or `COMPANY` if the fragment is company-level; type `text`), prompt carrying the fragment instructions plus tone/length norms inline (legacy variables have no tone/length settings). No `{aiResearch:<id>}` on legacy — reference other variables by `{fieldName}`. There's no `fallbackText` either — instruct the prompt to produce a safe generic line when context is thin, and mention this limitation to the user.\n2. Sample-test on 5–10 records via `start_an_actions_run` (`FILL_LEAD_WITH_SMART_FIELDS` / `FILL_COMPANY_WITH_SMART_FIELDS`) — credits: quote via `get_credit_pricing` / `get_credit_balance` and confirm first. Review, iterate via `update_an_ai_variable`.\n3. Embed in campaign copy as `{fieldName}` inside step content (**launch-campaign**, **copywriting-sequence**).\n\n### Phase 6 — Reuse it\n\nThe point of a snippet is reuse: embed the same snippet in every sequence that needs that personalization job (in-app for split workspaces; as the `{fieldName}` placeholder on legacy). When copy strategy changes, update once — the change propagates to every message that embeds it. Track downstream impact via **campaign-performance-analyzer**.\n\n---\n\n## Enginy's prompt-authoring standard (for snippet prompts)\n\nA snippet is computed per contact and reused across many messages, so a prompt that misbehaves poisons every message that embeds it. Apply these when writing the `prompt` (Phase 4) — and bake them into the legacy variable prompt too (Phase 5).\n\n**Recommended prompt structure.** Even for a one-sentence fragment, order the prompt consistently: **CONTEXT** (what this fragment is for) → **VARIABLES** (the only place `{fieldName}` / `{aiResearch:<id>}` tokens appear) → **GOAL** (the one job — one opener, one proof line) → **INSTRUCTIONS** → **RULES** (length, no greeting/CTA, gap handling) → **SEARCH INSTRUCTIONS** (how to reason about the input — internal, not output) → **DECISION HEURISTIC** (which fact to lead with — internal, not output) → **OUTPUT FORMAT** (emit only the fragment — no greeting, no sign-off, no CTA unless that's the job) → **EXAMPLE** → **LANGUAGE** (lock the output language). Most fragments won't need every section; keep the ordering for the ones they do.\n\n**Placeholders in VARIABLES only.** Declare each `{fieldName}` / `{aiResearch:<id>}` once under VARIABLES; reference it in plain words elsewhere (\"the contact's recent news\", not `{aiResearch:731}` repeated inline). Raw tokens sprinkled through the prose are what break formatting at scale.\n\n**Missing-data discipline.** A snippet embeds mid-message, so a broken fragment breaks the whole message. This is exactly what `fallbackText` is for (split workspaces) — always set it. In the prompt itself, still instruct a safe generic phrasing when the input is thin rather than an empty string, a literal token, or \"N/A\". On legacy (no `fallbackText`), the safe-default line baked into the prompt is the only net — make it explicit.\n\n**Prompt hardening.** Across thousands of contacts:\n- **Refusal prevention.** Instruct the model to *always* return a usable fragment in the required format — never a refusal or meta-commentary. A refusal embedded mid-sentence wrecks the message.\n- **Output-language lock.** End with an explicit language instruction — otherwise the fragment's language drifts to the contact's site/profile and clashes with the surrounding message.\n- **No marketing-language signals.** Forbid treating generic website self-promotion (\"innovative\", \"cutting-edge\", \"AI-powered\") as a real hook — it yields hollow, obviously-templated fragments. Reference only concrete, checkable facts.\n\n---\n\n## Enginy MCP tools used\n\n- `create_an_ai_snippet` — create the snippet (split workspaces; 403 = legacy signal)\n- `list_ai_snippets` / `get_an_ai_snippet` / `update_an_ai_snippet` / `delete_an_ai_snippet` — manage existing snippets (split workspaces)\n- `list_ai_message_tones` — valid `toneId` values (workspace-owned + Enginy defaults)\n- `list_public_promptlibrary_ai_snippet_entries` — prompt patterns + `model`/`outputLength` baselines\n- `get_contact_field_metadata` — valid `{placeholder}` names\n- `list_ai_variables` / `get_an_ai_variable` — ids for `{aiResearch:...}` tokens\n- Legacy fallback: `create_an_ai_variable`, `update_an_ai_variable`, `start_an_actions_run` (`FILL_LEAD_WITH_SMART_FIELDS` / `FILL_COMPANY_WITH_SMART_FIELDS`), `get_actions_run_status`, `get_credit_pricing` / `get_credit_balance`\n\n---\n\n## Important Notes\n\n- **The 403 is the detection mechanism, not an error.** No client-readable flag exists for the AI split; branch on the create result and always tell the user which system their workspace is on.\n- **Full CRUD on split workspaces:** list/get/update/delete exist; reads round-trip the prompt as plain token text. Deletes are soft and 409 while embedded; Enginy-default snippets are read-only. 409 on duplicate names at create.\n- **`toneId` comes from `list_ai_message_tones`;** `model`/`outputLength` baselines from public prompt-library entries. Don't invent values.\n- **`fallbackText` is the snippet's safety net** — always set one; a failed generation without fallback degrades every message embedding the snippet. (Legacy fallback path has no equivalent — bake a safe default into the prompt.)\n- **Reference policy: snippets may embed AI Research only** (`{aiResearch:<id-or-name>}`, split-workspace-only); no snippet/message tokens inside snippets. On legacy, reference other variables by `{fieldName}`.\n- **Tokens are strictly validated (400 on unknown/ambiguous)** with `details.issues` + `details.validTokenSample`; escape literal braces as `{{`/`}}`. (The legacy `create_an_ai_variable` path is more permissive — still validate against field metadata.)\n- **Legacy fallback runs cost credits** — quote and confirm before sample/list runs.\n- **Rate limit:** 30 req/min on AI-variable-scope writes.\n\n---\n\n## Examples\n\n**Example 1 — Reusable opener line (split workspace)**\nUser: \"One AI-generated opener sentence referencing each contact's recent company news, reusable in all my sequences.\" → this is a fragment → job: opener → `list_public_promptlibrary_ai_snippet_entries` for baseline toneId/model → prompt: one sentence, references `{aiResearch:731}` (\"latest company news\" research field), no greeting/CTA → `fallbackText`: \"Saw your team's been growing lately.\" → `create_an_ai_snippet` succeeds → embed in messages in the app across all three sequences.\n\n**Example 2 — Same request, legacy workspace**\nSame setup → `create_an_ai_snippet` → 403 split-not-enabled → explain → `create_an_ai_variable` (CONTACT, text, prompt includes \"if no news is found, write: 'Saw your team's been growing lately.'\") → credit-confirmed sample on 8 contacts → iterate → used as `{newsOpener}` in copywriting-sequence output via launch-campaign.\n\n**Example 3 — Company-level proof point**\nUser: \"A one-liner matching our best case study to each company's industry.\" → company-attribute fragment → prompt maps `{industry}` to one of three case-study lines → on split workspaces a (contact-scoped) snippet referencing the contact's company attributes; on legacy a COMPANY variable → embedded mid-email in every nurture sequence.\n\n---\n\n## Troubleshooting\n\n| Problem | Fix |\n|---|---|\n| 403 \"AI variable split is not enabled for this workspace\" | Not an error — legacy workspace. Phase 5 fallback (`create_an_ai_variable`) and inform the user |\n| 409 Conflict | Name already exists — rename |\n| Don't know a valid `toneId` | `list_ai_message_tones`; `model`/`outputLength` from a matching prompt-library entry |\n| Create/update 400 \"invalid prompt tokens\" | Unknown or ambiguous token — read `details.issues`, pick from `details.validTokenSample`, or use the id form when a name is ambiguous |\n| `{aiResearch:...}` not accepted | Split workspaces only; must exist in `list_ai_variables`; on legacy use `{fieldName}` |\n| Snippet renders empty/broken in messages | Set `fallbackText`; on legacy, bake a safe default line into the prompt |\n| Need to edit a snippet after creation | `get_an_ai_snippet` → edit the plain-text prompt → `update_an_ai_snippet` |\n| Delete returns 409 | Snippet is still embedded in a message/template — detach first |\n| New CRUD/tones tools not in your tool list | Your MCP session predates the rollout — reconnect to refresh the tool catalog |\n| Fragment tries to do two jobs | Split it — one snippet per job keeps reuse and iteration clean |\n"
}

SHA-256: 3c04cfd504652573cc8e50b7b3510e761476524cc894837719002df5355011b8