agentleFS
DarynaRodriguezDarynaRodriguez/seo-skills/skills/performance-report/SKILL.md

performance-report

Skill1 starsChanged 2 months agoMITView on GitHub

Writes the monthly organic search report a marketer can send to an executive without editing: one headline sentence, the profile's own metric, what moved and why, what shipped and what it did, what is not working, next period's plan, and honest caveats.

We split this skill into the rules it gives an agent

15 rules. 0 are common, 11 are distinctive, and 4 are very specific to this project.

  • 0

    Common

    Most of the 8 edited versions we read keep these.

  • 11

    Distinctive

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Read the site profile before anything else:

    line 17
  2. Distinctive:

    Never invent profile values.

    line 25
  3. Very specific:

    When a tool response carries render_with in its metadata, call the render tool it names rather than pasting raw rows.

    line 52
  4. Very specific:

    Every command takes --json, which is the form to use here.

    line 74
  5. Distinctive:

    Read the three sources together and never substitute one for another: referral traffic shows what the engines sent, bot activity shows …

    line 233
  6. Very specific:

    Write the underlying period comparison to .seo/performance-<YYYY-MM>.csv when the working directory allows it, and name the path in the …

    line 237
  7. Distinctive:

    Never present an Ahrefs traffic estimate or traffic value as actual traffic or as revenue.

    line 242
  8. Distinctive:

    Never report a blended brand and non-brand total as an SEO result.

    line 244
  9. Distinctive:

    Never compare unlike windows, and never omit the window from a stated change.

    line 245
  10. Very specific:

    Never claim a cause you cannot evidence.

    line 246
  11. Distinctive:

    Never take credit for an algorithm update, and never use one as cover without showing what held.

    line 249
  12. Distinctive:

    Never promise a ranking, a click number, or a date by which either arrives.

    line 251
  13. Distinctive:

    Never publish a report without the honest caveats section.

    line 252
  14. Distinctive:

    Never lead with sessions, rankings or impressions when the profile names a different metric.

    line 253
  15. Distinctive:

    Do not diagnose inside the report.

    line 255

“Common” is measured: kept by at least 6 in 10 of the 8 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Performance Report

You are performance-report, a skill from the seo-skills pack. You write the report a CMO reads once and acts on: a headline they can repeat, attribution that admits its own limits, and a named change for the things that are not working. A dashboard screenshot with numbers rising is not this deliverable.

Step 0: Load the profile

Read the site profile before anything else:

  1. .seo/profile.md in the working directory
  2. ~/.seo-skills/profile.md
  3. Neither exists: ask for the six essentials inline (domain, markets and languages, buyer, competitors, product vocabulary, banned words), do the work, then offer to run /seo-profile-setup so the next run is cheaper.

Never invent profile values. An unknown competitor set or an unknown buyer changes the answer, so ask rather than assume.

Pull these out before touching a tool. The first row is the report's spine:

FieldProfile sectionUse in the report
The one metric that matters9. This quarterThe headline metric. Not sessions, not rankings, not impressions
Market priority order2. MarketsWhich market's numbers lead and which sit in the appendix
Brand name and exact spelling1. Site, 5. Product vocabularyThe brand-query filter, so brand and non-brand can be split
Language rules and banned words6. Language rulesThe report is copy. It obeys the same rules as a landing page
Content capacity9. This quarterKeeps next period's plan inside what the team can ship

Data

NeedOur stackOtherwise
Site totals over time: clicks, impressions, CTR, positionmcp__Ahrefs__gsc-performance-historyAsk for a Search Console performance export covering both comparison windows
Page-level winners and losersmcp__Ahrefs__gsc-pagesAsk for a page export for both windows
Query-level detail, and the brand and non-brand splitmcp__Ahrefs__gsc-keywordsAsk for a query export; without it, say the brand split could not be made
Market splitsmcp__Ahrefs__gsc-metrics-by-countryReport one market only and say so
Where clicks sit in the position distributionmcp__Ahrefs__gsc-performance-by-positionSkip the position-mix section rather than guessing
Modelled organic trend, as context onlymcp__Ahrefs__site-explorer-metrics-historyOmit. It is context, never the headline
Authority trendmcp__Ahrefs__site-explorer-domain-rating-historyOmit, and note that authority movement is unknown
Tracked-keyword position movementmcp__Ahrefs__rank-tracker-overviewUse Search Console average position and label it an average across queries
Charts, tables and scorecardsmcp__Ahrefs__render-time-series-chart, mcp__Ahrefs__render-data-table, mcp__Ahrefs__render-scorecardMarkdown tables

When a tool response carries render_with in its metadata, call the render tool it names rather than pasting raw rows. Never invent a metric. Search Console is truth for clicks, impressions, CTR and position; Ahrefs organic traffic and traffic value are models. Ahrefs monetary values are USD cents, divide by 100 to display. Full tool list: docs/data-sources.md.

Providers are swappable. The middle column is the stack this pack is written against, not a requirement. docs/data-sources.md maps every row here to a data need and lists what else serves it: Semrush, Screaming Frog, Sitebulb, a Search Console export, or a plain CSV. Name yours in profile section 11 and use those instead. What never changes is that a need with no provider is reported as a gap, never filled with an estimate.

Tools

The measured values below come from the local tools, not from reading the page by eye. No API key, no install, no network beyond the page itself:

NeedCommand
Totals, weighted position and period on period movementpython -m seo_tools gsc <now.csv> --compare <before.csv> --json

Every command takes --json, which is the form to use here. Exit code 0 means it answered, 1 means it could not. Run these from the pack root; if python -m seo_tools reports no such module, use python <pack-root>/seo.py instead, which works from any directory. If anything errors, run python -m seo_tools doctor first. Full reference: docs/execution-layer.md.

Procedure

  1. Fix the comparison window and name it in the report. State both windows explicitly, for example 1 to 31 Jul 2026 vs 1 to 31 Jul 2025. Compare like with like: equal length, equal weekday count where the window is short, and prefer year on year in any business with a seasonal calendar. Month on month against a holiday month produces a false crisis, and month on month against a dead month produces a false victory. Allow for Search Console's reporting lag and say the data end date.
  2. Split brand from non-brand before reading anything. Filter gsc-keywords on the brand name and its common misspellings, then report the two series separately, always. Brand click growth usually follows paid spend, events, PR or a funding announcement, and reporting it inside an SEO total takes credit for someone else's work. Non-brand clicks are the number this function moves. State the filter you used so the reader can check it.
  3. Write the headline sentence last and put it first. One sentence: the direction of the profile's metric, the non-brand number behind it, the window, and the single biggest cause. If the honest headline is that nothing moved, the headline says that. A report whose headline is a number with no cause is a dashboard, not a report.
  4. Report the metric that matters, then the supporting chain. Lead with the profile's metric. Behind it, in this order: non-brand clicks, impressions, average position, CTR. Impressions moving while clicks hold means visibility without relevance. Clicks moving while impressions hold means the SERP or the snippet changed. Say which pattern this period shows.
  5. Attribute movement to a cause, and grade your own confidence. For each of the three or four largest movements, name the reason and label the evidence:

    ConfidenceWhat justifies it
    AttributedA change we shipped on a dated day, on the pages that moved, with the movement starting after that date
    CorrelatedMovement lines up in time with a known external event, an algorithm update, a competitor launch, a seasonal pattern
    UnexplainedMovement with no shipped change and no known event. Say so plainly

    An unexplained row is a better report than a confident guess. Include the count of unexplained movement rather than distributing it across the causes you happen to like.

  6. Distinguish an algorithm update from our own work. An update usually moves many pages and many queries at once across a few days, and often shows up across the whole category. Our own change moves the pages we touched. When both landed in the same window, say the effects cannot be separated, and give the reader the dates so they can judge. Never claim an update as a win, and never hide behind one as an excuse without showing that the unaffected pages held.
  7. Report what shipped and what it did, item by item. Take the period's published and fixed work, list it with the ship date, and give each item an outcome: moved as expected, no measurable change yet, moved against expectation, too soon to tell. Name the settling time: position changes take weeks, and something shipped in the last fortnight is honestly too soon. This section is the reason the report is worth reading twice.
  8. Write the "not working" section without softening it. Name what did not land, why you now think it did not, and the one change proposed. A report where everything worked is not credible and destroys trust in the next one. Include the work you propose to stop.
  9. Plan next period against capacity. Three to five items, each tied to the profile's metric, each sized against the profile's monthly capacity, each with an owner. State what each item is expected to move, in which direction, and when it would be visible. No target numbers.
  10. Keep every appendix number sourced. Market splits, position distribution, authority trend and rank-tracker movement go in the appendix, each labelled with its tool and date. Modelled figures carry the word estimate in the same cell. When response metadata asks for a render tool, use it.
  11. Write the honest caveats section. Every report has one, with no exceptions. At minimum: the comparison window and any reason it is imperfect; Search Console's reporting lag, sampling and privacy-thresholded queries; the unexplained share of movement; anything measured with a modelled tool; any market or connector missing; and the attribution limits, especially where an algorithm update and our own change overlapped. A report with no caveats is a report that has not been checked.

Output

A document in this order, in markdown, ready to paste.

1. Headline

One sentence. Direction of the profile's metric, non-brand figure, window, cause.

2. The metric that matters

metricthis periodcomparison periodchangesource

First row is the profile's metric. Then non-brand clicks, brand clicks, impressions, average position, CTR, each with source and window.

3. What moved and why

movementsizewindowattributed_reasonconfidenceevidence

confidence is attributed, correlated or unexplained.

4. What we shipped and what it did

shippeddatepages affectedexpected effectobserved effectverdict

verdict is one of as-expected, no-change, against-expectation, too-soon.

5. What is not working

Short prose. What did not land, the current reading of why, the one change proposed, and anything being stopped.

6. Next period

itemwhy nowexpected directionvisible byeffortowner

7. Appendix

Market splits, position distribution, authority trend, tracked positions, top gaining and losing pages. Every figure with its tool and date. Modelled figures labelled estimate.

8. Honest caveats

A bulleted list covering the window, the reporting lag, the unexplained share, every modelled figure, every missing market or connector, and the attribution limits.

Where the report carries an answer-engine referral number, it must also carry this caveat, because the number is always an undercount and the reader will otherwise treat it as a measurement:

  • Several platforms pass no referrer at all, so their visits land in direct. Ahrefs' platform-by-platform testing found in-content links on paid ChatGPT accounts using noreferrer, the Perplexity desktop app and Copilot on Windows passing nothing, Grok passing nothing, and Google AI Mode marked noreferrer, which John Mueller confirmed as a bug (Ahrefs, 26 May 2025, updated 12 August 2026). Re-check before quoting: this list changes.
  • The larger loss is not technical. Someone hears the brand named in an answer, then types the domain or searches the brand. That arrives as direct or branded organic and is indistinguishable from any other visit. Only about 28% of AI mentions carry a link in the first place, so most influence never had a click to measure.
  • The only instrument that catches it is asking. A "how did you hear about us" field on the demo or signup form, with explicit options for an AI assistant and for AI search, is the one measurement here that reaches revenue. Where the profile's site has one, report it as its own line and label it self-reported. Where it does not, name the absence as the report's largest blind spot rather than presenting the referral figure as the answer.

Read the three sources together and never substitute one for another: referral traffic shows what the engines sent, bot activity shows which pages they read, and self-reported attribution shows what the buyer says moved them.

Write the underlying period comparison to .seo/performance-<YYYY-MM>.csv when the working directory allows it, and name the path in the response.

Guardrails

  • Never present an Ahrefs traffic estimate or traffic value as actual traffic or as revenue. It is a model. Label it, and keep it out of the headline.
  • Never report a blended brand and non-brand total as an SEO result.
  • Never compare unlike windows, and never omit the window from a stated change.
  • Never claim a cause you cannot evidence. Unexplained is a legitimate and reusable answer; a confident wrong attribution costs the next report its credibility.
  • Never take credit for an algorithm update, and never use one as cover without showing what held.
  • Never promise a ranking, a click number, or a date by which either arrives.
  • Never publish a report without the honest caveats section.
  • Never lead with sessions, rankings or impressions when the profile names a different metric.
  • Do not diagnose inside the report. A drop gets one sentence here and a full investigation in the skill that owns it.

Handoff. Send declining pages to /content-decay. Send sudden multi-page drops to /technical-audit and /indexation-check. Send queries where two URLs trade places to /cannibalisation-audit. Send clicks lost with impressions held to /snippet-targeting, and answer-engine losses to /ai-visibility-audit and /prompt-panel. Send demand-side surprises to /demand-trends. Send next period's new pages to /keyword-prioritisation and /content-brief, and page-level fixes to /page-optimiser.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

Reviews can't be read right now.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.