Performance Memo logo

Performance Memo

Organization
cognyai
performance-memo

The Monday memo a sharp analyst would write — joins spend on every ad platform to qualified calls, deals, revenue, refunds and churn per campaign ID, catches cheap customers who churn, and ends every line in a decision — keep, kill, change or test

Overview

Publishercognyai
Repositoryclaude-code-marketing-skills
Skill nameperformance-memo
Stars
111
Forks
15
Bundled files
Instructions only
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by cognyai on GitHub. Read the source before you install it.

Installation

Install the Performance Memo AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/cognyai/claude-code-marketing-skills/tree/main/skills/performance-memo
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/cognyai/claude-code-marketing-skills.git /tmp/claude-code-marketing-skills
mkdir -p .claude/skills
cp -r /tmp/claude-code-marketing-skills/skills/performance-memo .claude/skills/performance-memo
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Performance Memo in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Performance Memo on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Performance Memo is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Performance Memo

Every Monday, a short memo the way a sharp analyst would write it:

"The profitability-review page converted at 4%, and half those leads were too small to serve. The dinner ad has the lowest cost per qualified call. Retargeting has spent $3,000 and produced two sales conversations."

Every line ends in a decision for you: keep, kill, change or test. It follows the money all the way through to retention, refunds and profit, because a campaign that brings in cheap customers who churn in sixty days looks great on a dashboard and quietly loses you money.

Requires: Cogny ($9/mo). It's best with /campaign-ledger running. Without IDs it falls back in this order, labelling the confidence of every line:

  1. Campaign IDs in the ledger (high)
  2. fibbler channel-level attribution (medium). Needs fibbler with two paid channels; it refuses or returns zeros otherwise. If fibbler refuses, returns zeros or fails auth (401 / Forbidden), drop to tier 3 and name it in "Data problems".
  3. Platform-reported conversions + GA4 by sessionSourceMedium (low). Always cross-check: if a platform claims conversions GA4 can't see (e.g. X sign-ups while GA4 shows untagged t.co / referral with 0 key events), that mismatch is a memo line in itself.

Without a campaign evidence bar, use a default: conclusive only with ≥10 conversions and ≥14 days.

Usage

  • /performance-memo — last 7 days vs prior 7, plus 90-day outcomes for anything that's closing
  • /performance-memo month --compare

Schedule: Monday 07:30, after /customer-language and /campaign-ledger sync.

Step 0: Cogny check

If no mcp__cogny__* tools exist (no list_integrations, no cogny_status), print this and stop:

Performance Memo needs your marketing data through Cogny's MCP gateway.

1. Sign up at https://cogny.com and connect your ad platforms plus a CRM or Stripe (fibbler and GA4 help with attribution).
2. Add Cogny to .mcp.json in your project:
   {"mcpServers": {"cogny": {"type": "http", "url": "https://app.cogny.com/mcp"}}}
3. Restart Claude Code; it opens a browser for the OAuth login on first use.

Nothing to run without Cogny.

Steps

1. Load context

brain/campaigns/ledger, every live campaign folder (for its evidence bar), brain/company/customer-fit (to judge lead quality), brain/experiments/log, and last week's memo.

2. Spend and delivery (by campaign ID where possible)

Run /campaign-ledger sync logic, or read the ledger if it was synced today. Compute explicit windows: this week = the 7 full days ending yesterday, prior week = the 7 days before that. Get account IDs first (list_accessible_accounts, list_ad_accounts, list_accounts, list_advertisers). These double as the health check: list_integrations misses revoked tokens, so treat a 401, Forbidden or scope error on that cheap read as 🔴 "reconnect ", name it, and don't retry. If an expected tool is missing, refresh_mcp_discovery once; new tools need a client reconnect, so use the fallback and mark the source ⚠.

  • Google: GAQL on campaign with customer.currency_code, segments.date BETWEEN '<start>' AND '<end>' for each window, and AND metrics.impressions > 0
  • Meta get_insights level: "campaign" with time_range_since/until. No output at all means no delivery, not an error.
  • LinkedIn get_analytics; Bing get_report with start/end dates; TikTok get_insights
  • X: get_campaigns for the IDs, then get_analytics with granularity: "TOTAL"
  • Reddit tool_get_reporting(ad_account_id, start_date, end_date, level: "campaign")
  • Snapchat tool_get_stats(entity_id: <campaign id>, start_time, end_time, level: "campaign") per campaign
  • Apple Search Ads tool_get_account_spend(org_id, start_date, end_date), plus tool_get_campaign_report for the top campaigns
  • ChatGPT ads (OpenAI Ads) tool_get_insights(aggregation_level: "campaign", start_date, end_date) and tool_get_conversion_insights for conversions
  • Bidtheatre: the most recent get_report the user has, because creating a report is a write (ask first)

Report spend in each account's currency. Never sum across currencies unless you state the FX rate.

3. Leads and lead quality

  • GA4 run_report (property id from list_account_summaries): key events by sessionCampaignName, last 7 days vs prior 7. If key events are near zero site-wide (about 1 a week or fewer) while traffic is normal, report "GA4 conversion tracking looks broken" under Data problems. Don't judge campaigns on it. Without GA4: Plausible breakdown(site_id, dimension: "visit:utm_campaign", metrics: ["visitors","events"]) with goal filters.
  • HubSpot contacts created in each window (createdate), with hs_analytics_source, hs_analytics_source_data_1, hs_analytics_source_data_2 (original utm_campaign for paid traffic), lifecyclestage and associated company firmographics. This is the lead count by source and campaign, and it can be matched to campaign IDs.
  • HubSpot deals created in the window (createdate), with the campaign_id deal property if it exists, otherwise the associated contact's original source. Dedupe by company and report raw vs unique. Anything that can't be matched is marked unattributed. For each lead, check it against customer-fit. Too small or wrong segment gets counted separately, because a page that converts at 4% with half the leads unserviceable converts at 2%.
  • HubSpot meetings in the window (hs_meeting_start_time, hs_meeting_outcome = COMPLETED) associated with new contacts or deals: qualified calls held. Exclude vendor, partner and internal meetings.
  • Pipedrive instead: list_deals(updated_since) filtered by add_time in the window, and list_activities(done: true) of type meeting for qualified calls. Upsales instead: list_orders / list_opportunities with filters=[{"field":"regDate","comparator":"gte","value":…}], and list_appointments for meetings. Check field names once with get_metadata.
  • Self-serve products: activation is the lead-quality signal. PostHog (list_projects → list_insights(search: "activation") → get_insight) or Mixpanel (list_funnels → get_funnel, get_retention) gives activation rate by utm_campaign / initial referrer when the insight breaks down by it. Say when it doesn't.

4. Pipeline and revenue

  • fibbler get_deal_attribution_summary (time_period: "90d", compare: true): pipeline, revenue, ROAS, cost per opportunity. Use newDealsCreated for pipeline and wonDealsCount for revenue, never totalInfluencedDeals as the headline.
  • fibbler get_cross_channel_performance (time_period: "90d"): per-channel cost per influenced deal. Use totals for deduplicated counts. Per-channel deal counts overlap and can't be summed. LinkedIn is impression-matched and Google is click-matched, so they aren't like for like. Say so.
  • HubSpot open pipeline: deals with hs_is_closed = false, grouped by dealstage (labels from get_properties(deals, dealstage)) and deal_currency_code. Never add up currencies. Pass crm: "hubspot" to the fibbler tools.

5. Retention, refunds, profit (the part dashboards miss)

For customers won 60–180 days ago, attributed to a campaign ID or channel:

  • Stripe (stripe_api_read):
    • GetSubscriptions with status: "all" and a created window (or GetSubscriptionsSearch) gives the cohort, which is the denominator. Churn = cohort members with canceled_at within 60 days of start_date ÷ cohort size. Canceled subscriptions alone can't give a rate.
    • Trials aren't churn. A subscription with ended_at == trial_end never paid: count it as a trial that didn't convert, report it separately, and keep it out of the churn numerator and denominator.
    • Paginate with starting_after (the last id of the previous page) while has_more is true. Each page is ~370k characters, so aggregate outside the main context (a subagent or Cloud script that returns only counts per plan and cohort month) instead of reading pages into the memo.
    • GetRefunds with created.gte for refunds.
    • Group by plan (items.data[].price.unit_amount + currency). Subscription metadata may come back [REDACTED], so per-campaign churn needs a source stored somewhere readable. If it isn't, report churn by plan and recommend storing the campaign or UTM in checkout metadata.
  • Fortnox (invoice-billed businesses): list_invoices with filter: "unpaidoverdue" and fromdate set to the window. Flag campaigns whose customers don't pay.
  • Stores (ecommerce instead of subscriptions): revenue and refunds per window, in the store's base currency:
    • Shopify tool_list_orders(query_filter: "created_at:>=<start> created_at:<=<end>") for revenue (the tool returns no UTM fields, so attribution comes from GA4 or Plausible), and tool_list_customers for the repeat rate (numberOfOrders > 1).
    • WooCommerce get_sales_report(date_min, date_max), list_orders(after, before), list_order_refunds(order_id)
    • Abicart / Litium get_sales_summary(start_date, end_date) For ecommerce, "retained" means repeat purchase within 90 days, so compute cost per repeat customer.

Compute cost per retained customer = spend ÷ customers still active at 60 days. Flag any campaign that ranks well on CPA but badly on this.

5b. What was already tested (optional)

GrowthBook list_experiments(status: "stopped") + get_experiment_results(experiment_id) for experiments that ended in the window. Add winners and losers to the memo and to brain/experiments/log, so no agent re-tests a settled question.

6. Judge against the evidence bar

For each campaign, compare against the bar in its folder. Label it conclusive or not yet (spend, days or sample size under the bar). With fewer than ~10 conversions, say the sample is small explicitly; don't let one lucky deal become a strategy.

7. Write the memo

write_context_node at brain/memos/performance/<YYYY-MM-DD>:

markdown
# Performance memo — week of YYYY-MM-DD
Sources: Google ✅ Meta ✅ LinkedIn ✅ Bing ✅ TikTok ⚪ X ✅ Reddit ⚪ Snapchat ⚪ Apple ⚪ ChatGPT ads ⚪ · fibbler ✅ · CRM (HubSpot) ✅ · Stripe ✅ · Store ⚪ · PostHog ⚪ · GrowthBook ⚪
Attribution confidence: 62% of spend ID-tagged (high) · rest via fibbler (medium)

## The short version (≤5 lines, each ending in a decision)
1. BK014 calculator page converts 4.1%, but 48% of leads are under the $1M floor → **CHANGE**: add a revenue question to the form.
2. BK014_dinner-angle has the lowest cost per qualified call ($92) → **KEEP**, and test a founder version.
3. Retargeting (BK009): $3,020 spent, 2 sales conversations → **KILL**.
4. LinkedIn-sourced customers from Q2 churned 3× faster than search-sourced ones by day 60 → **TEST**: tighten targeting to 51–200 employees.
5. Amazon-trigger list booked 5 calls from 22 emails → **KEEP** (small sample).

## Campaign table
| ID | Spend | Leads (in-fit) | Qual. calls | Deals | Revenue | 60d churn | Cost/qual call | Cost/retained cust | Bar met? | Call |

## Channel view (fibbler, 90d, deduplicated totals)
## What changed vs last week
## Not conclusive yet
## Data problems this week

8. Queue the decisions

One create_ticket per kill or change (a keep needs no ticket), each with the evidence and estimated_impact_usd. Interactively, walk the user through the short version and record each call in brain/experiments/log.

Recording findings

Use create_ticket when the workspace has it (Cloud) and create_finding otherwise (Solo). The fields differ; see section 2 of ../marketing-engineer/references/mcp-routing.md.

Critical rules

  1. Ticket titles start with [ME:performance-memo]. Amounts in estimated_impact_usd must be converted to USD; give the native amount in the body.
  2. Every line ends in keep / kill / change / test. A number without a decision doesn't belong in the memo.
  3. Follow the money to retention. CPA alone is not a verdict.
  4. Never sum overlapping attribution. Use fibbler totals.
  5. State sample size and confidence. "Small sample" is a finding.
  6. Name every source that failed. A missing platform can make a week look profitable.
  7. Read-only. Decisions go to the queue; this skill changes nothing.

Frequently asked questions

What does the Performance Memo AI skill do?

The Monday memo a sharp analyst would write — joins spend on every ad platform to qualified calls, deals, revenue, refunds and churn per campaign ID, catches cheap customers who churn, and ends every line in a decision — keep, kill, change or test

Why use Performance Memo on TypingMind?

Because you install it once and use it with any model. Performance Memo is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Performance Memo in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/cognyai/claude-code-marketing-skills/tree/main/skills/performance-memo. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Performance Memo?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Performance Memo?

As many as you like. As long as a model supports skills, you can use Performance Memo with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Performance Memo AI skill free?

It is published on GitHub by cognyai. Check the repository for licensing terms. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇