AI Marketing Bench · Team Ops
Campaign intake
Turns a messy campaign request (an email, Slack thread, meeting notes or half-filled form) into a structured intake saved in intake/, covering objective and the business result it serves, audience matched to brand memory, offer, CTA, channels, deadline and key dates, budget as a number, assets that exist and assets needed, approvals and who decides, the success metric and where it's measured, risks, and the next pieces of work in order.
AI Marketing Bench · Team Ops
Campaign intake
Install this skill
Claude Code, the whole Team Ops capability
/plugin marketplace add ashleahwilson/ai-marketing-bench
/plugin install team-ops@ai-marketing-benchAny agent that supports Agent Skills, this skill only
npx skills add ashleahwilson/ai-marketing-bench --skill campaign-intakeclaude.ai
Download the skill folder from GitHub as a zip and upload it under Settings → Capabilities → Skills.
What it does
Turns a messy campaign request (an email, Slack thread, meeting notes or half-filled form) into a structured intake saved in intake/, covering objective and the business result it serves, audience matched to brand memory, offer, CTA, channels, deadline and key dates, budget as a number, assets that exist and assets needed, approvals and who decides, the success metric and where it's measured, risks, and the next pieces of work in order. Asks only the questions whose answers change the plan, in one short round, and records everything else as an assumption to confirm. Checks feasibility (the deadline against the work), checks offers, claims and quotes against brand memory, and surfaces conflicting asks from different stakeholders instead of merging them. Use when the user wants a campaign request turned into a plan or intake, for example "turn this thread into an intake", "scope this campaign", "what do we need to start on this", "can we do this by Friday". Not for writing the brief or the campaign itself.
Note from the repo. Recommended for large models. On small models it can occasionally assume a value without marking it (a budget currency), so check the Budget line. See evals/scorecard.json.
The skill, in full
SKILL.md as published in the repo. Reference files it links to open on GitHub.
Turns a request into an intake the team can start from without another meeting: what's being asked, whether it can be done by the date, what's still undecided, and what happens next. It doesn't write briefs, copy or ads. It decides what they need to be.
Ground rules
- Missing stays missing. Every value traces to the request or a line in
brand/. Anything not given isunknown: <what> (ask: <who>), orassumed: <value> (confirm: <who>)when a safe default exists. No invented prices, budgets, dates, conversion rates, forecasts, lead names or results. A plausible guess is still an invention. - Ask only what changes the plan. One round, at most four questions, each saying what changes with the answer. A clean request gets none. Everything else is an assumption to confirm.
- Say when it can't be done. If the scope can't be delivered by the deadline, the first line of the reply says so, with the reason in plain words, and offers a scoped-down version that can. "Tight" is for things that can be done.
- Surface conflicts, never merge them. When two people ask for different things, record both positions, attributed, mark the affected fields
open, and name who decides. Don't pick a side, blend them, or invent a compromise. - Brand memory decides what can be said. A held, banned or unsourced claim, an uncleared customer name, or an audience the brand doesn't serve stays out of the fields and is flagged with what would clear it. Push back in a line, offer what does work, and carry on with the rest of the intake. Never refuse, never lecture.
- Names come from the files. Every person in the intake is named in the request, the user's message or
brand/. If the user says "we decided" without naming who, write "you". Never use a name from anywhere else, including the environment you run in. - Never overwrite the user's files. The request files and
brand/are read, never edited. An intake with the same slug is updated only in Update mode, with a line under Updates.
Pick the mode
| The request… | Mode |
|---|---|
| A request to turn into an intake (default) | Draft: write the full intake, open items marked, questions in the reply |
| "Walk me through it", "ask me first", "before you write anything" | Guided: the questions only (at most four, each with the likely answer from brand memory), no file yet |
Answers or decisions on an existing intake/<slug>.md |
Update: apply them to that file, re-check everything they touch, log them under Updates |
Workflow
- [ ] 1. Load: the request files, brand/, any existing intake/ file
- [ ] 2. Build the fact list: every value with its source
- [ ] 3. Judge: questions, feasibility, conflicts, claims, audience, risks (references/judgment.md)
- [ ] 4. Write intake/<slug>.md (references/intake-format.md)
- [ ] 5. Self-check: run scripts/check_intake.py, then the six questions in judgment.md
- [ ] 6. Reply in the fixed shape
1. Load
List the project folder and look for brand/, even if the user didn't mention it. Read BRAND.md, audiences.md, offer.md, proof.md, claims.md and rules.md where they exist. Read every request file in full, including who wrote each message and when.
Today is the date the user gives. Otherwise use the current date. Write it in the file header: every date check depends on it.
No brand/ folder? Work from the request only. Write Brand memory: none (provisional), set Brand audience match to none: no brand memory, don't add any fact about the company that isn't in the request, and end the reply by offering to set up brand memory from what the request already says.
2. The fact list
Before writing, list each value and where it came from: a named person in the request with the date of their message, or brand/<file> with its status. Resolve dates to YYYY-MM-DD relative to today. Write budgets as digits ($4k → 4000, $2,500/month for two months → 2500 per month; 5000 total).
3. Judge
Work through references/judgment.md in order:
- Questions: which unknowns change the plan, and which become assumptions.
- Feasibility: working days available against the longest chain of work, any constraint in the request (a person away, footage that doesn't exist). Decide fits, tight or doesn't fit, and build the scoped-down version if needed.
- Conflicts: every point where two people want different things.
- Claims: every headline, claim, price, discount, quote and name the request wants used, against brand memory.
- Audience: match to a brand memory segment; narrow anything outside what the brand serves.
- Risks: consent, past dates, stale assets, people, measurement.
4. Write the intake
Follow references/intake-format.md exactly: the header, the 19 - Field: value lines under ## Fields, then Feasibility, Conflicts, Claims and brand checks, Assumptions to confirm, Questions, Risks, For the next step, Updates. Slug: lowercase, hyphenated, two to five words from the campaign name (intake/january-catch-up.md). Write exactly one intake file per request. Inside the file, describe the reasoning in plain words ("about 6 working days of work"), never the method ("the lead-time table", "the checker").
Route the work with the plain-language route terms in the format file, in order: unblocking work first (a brand memory update, a claims review), then briefs, then builds, then pre-launch checks. Work with no step here is other: <work>. Describe work in plain words; never write internal tool names in the file or the reply.
5. Self-check
python3 scripts/check_intake.py intake/<slug>.md --today <YYYY-MM-DD> --brand brand/ --request <request files>
Fix every FAIL and rerun. Act on each WARN: a floor above the days available means the feasibility verdict must say so. For every CLAIM line, make sure the intake handles it under Claims and brand checks (a discount or target the requester set is their fact; a performance claim, comparison or certification needs a source or legal review). Then answer the six self-check questions at the end of judgment.md. A "no" means fix the file.
6. Reply
**Intake:** written to intake/<slug>.md (<status>) | not written yet: <one line why>
**Recommendation:** <one or two lines: go as asked / go with the scoped version / decide X first. If the deadline can't be met, this line says so and why.>
**Questions** (only if any; at most 4)
1. <question>: <what changes with the answer>. Likely: <answer from brand memory> (only when brand memory actually suggests one; otherwise leave "Likely" out)
**Flags**
1. <feasibility, conflict, claim or risk, one line each, with where it comes from>
**Assumed until you say otherwise:** <one line>
**Next:** <the route, in plain words, in order>
- Lead with the decision the user has to make. The detail is in the file, not the reply.
- Length: a
readyintake: under 150 words (at most three flags). Update mode: under 200 words. Anything else: under 300 words. Count before sending; cut flags, not the recommendation. - Flags are only what changes a decision or could stop launch: at most five, one line each. Leave housekeeping (who owns a check, timing advice, footers the next step adds anyway) in the file. URLs the user or brand memory gives are real, even reserved test domains such as
.example. - Guided: the "Intake" line says not written yet, then the questions. Nothing else.
- Update: what changed (one line), the new status, and anything the answers broke or still leave open (for example a customer name that still needs permission). Under 200 words.
- Talk to the user in the second person. Never mention the checker, this skill, its references or other tools by name.
Keep tools out of the deliverable
Never name this skill, other skills, or their scripts and checks in a reply, report, draft or file the user may pass on: these often go to clients or the public. Describe what was checked, not the tool that checked it. If your environment asks you to say which skill you used, put that in one short line to the user, separate from the deliverable, never inside it.
What good looks like
Someone who wasn't in the thread reads the intake in three minutes and knows what's being built, by when, for whom, with what money, and what's still undecided and who decides it. Every number traces to a person or a brand memory line. Disagreements are visible, not smoothed over. If the date can't work, the team learns that today, along with what can be done instead.
A worked example (a Tallyhouse patio-season campaign that comes back ready, with its headline claim held) is in assets/examples/tallyhouse-patio/.
How it was tested
Meets the Bench Standard bar on Opus (75/76, every trap passed, no invented facts). On Haiku it beats no skill by 18 tests (75/76 vs 57/76) and passes every trap, but in the final round it wrote one unmarked currency assumption (the BFCM budget as '6000 USD' when the thread gave no currency), so the no-invented-facts part of the bar is met on large models only.
9 evals (4 traps), expectations written before any run. No comparison skill exists locally, so round 1 compared the skill with no skill on Haiku and Opus, in fresh headless agents graded blind by a separate Opus grader. Rounds 2 and 3 re-ran the skill as it was revised (round 3 on Haiku and Opus).
| Run | Expectations met |
|---|---|
| with skill, Opus (v1.2.0, round 3) | 75 / 76 |
| with skill, Opus (v1.0.0, round 1) | 74 / 76 |
| no skill, Opus | 57 / 76 |
| with skill, Haiku (v1.2.0, round 3) | 75 / 76 |
| no skill, Haiku | 57 / 76 |
Traps
- with skill, Haiku (round 3)
- 33/33
- with skill, Opus (round 3)
- 33/33
- no skill, Haiku
- 25/33
- no skill, Opus
- 27/33
Where it fell short
- Round 3, Haiku, eval 6: the Update reply ran 283 words against a 250 limit; replies tend to land near 300 words whatever the task
- Round 3, Opus, eval 9: asked four numbered questions, but one joined two decisions (success metric and decision owner), counted as five
- Round 3, Haiku, eval 2: recorded the budget as '6000 USD' with no currency in the thread and no assumption marker (an invented fact); also said no copy should go out until brand memory is updated, which over-gates
- Round 3, Haiku, eval 3: planned to record Hannah's unverified '80% of inbound' figure in brand memory; eval 1 working-day count off by one
- Round 3, Opus: cites public holidays (US Thanksgiving, Remembrance Day, Veterans Day) from general knowledge; correct, but not from the fixtures
- Round 2, Haiku: on the deadline trap said nothing could ship by the date; on the webinar it re-dated the series over a one-day gap against the rough lead-time floor (fixed in 1.2.0)
- Round 1, Haiku: three non-blocking questions on a complete request, picked one of two candidate success metrics, used a name from its environment ('Ashleah Wilson') in an update log, and wrote method words ('lead-time table') into intakes (fixed in 1.1.0)
- Rounds 1 and 2: the judgment reference's feasibility example resembled the deadline-trap answer; replaced before round 3, so round 3 is the clean result for eval 2
- Lead-time floors are planning defaults, not facts; they decide feasibility verdicts and should be replaced with the team's own
- Round 1, without the skill: Opus invented conversion and cost-per-review figures, a budget split and CPC claims, and facts about Ferncliff; Haiku skipped brand memory on eval 8. Both missed numeric budgets, over-asked, and ran long
- Every run, all configs, left the request and brand files unchanged
From evals/scorecard.json for version 1.2.0, graded 2026-10-27. The evals and scorecard on GitHub →
