Copy Editing
coreyhaines31/marketingskills
When the user wants to edit, review, or improve existing marketing copy, or refresh outdated content.
Agent skill
by growthenginenowoslawski in growthenginenowoslawski/coldoutboundskills
Produces a copy-ready clause about someone who recently started or changed into their current job title.
$ npx skills add growthenginenowoslawski/coldoutboundskills --skill playbook-new-in-role -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install growthenginenowoslawski/coldoutboundskills playbook-new-in-role --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/growthenginenowoslawski/coldoutboundskills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/playbooks/playbook-new-in-role .claude/skills/playbook-new-in-role && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "playbook-new-in-role" agent skill from https://github.com/growthenginenowoslawski/coldoutboundskills/tree/main/skills/playbooks/playbook-new-in-role into .claude/skills/playbook-new-in-role/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playbook-new-in-role", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/growthenginenowoslawski/coldoutboundskills/tree/main/skills/playbooks/playbook-new-in-roleType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add growthenginenowoslawski/coldoutboundskills --skill playbook-new-in-role -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install growthenginenowoslawski/coldoutboundskills playbook-new-in-role --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/growthenginenowoslawski/coldoutboundskills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/playbooks/playbook-new-in-role .agents/skills/playbook-new-in-role && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "playbook-new-in-role" agent skill from https://github.com/growthenginenowoslawski/coldoutboundskills/tree/main/skills/playbooks/playbook-new-in-role into .agents/skills/playbook-new-in-role/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playbook-new-in-role", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add growthenginenowoslawski/coldoutboundskills --skill playbook-new-in-role -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install growthenginenowoslawski/coldoutboundskills playbook-new-in-role --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/growthenginenowoslawski/coldoutboundskills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/playbooks/playbook-new-in-role .cursor/skills/playbook-new-in-role && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "playbook-new-in-role" agent skill from https://github.com/growthenginenowoslawski/coldoutboundskills/tree/main/skills/playbooks/playbook-new-in-role into .cursor/skills/playbook-new-in-role/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playbook-new-in-role", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/growthenginenowoslawski/coldoutboundskills.git --path skills/playbooks/playbook-new-in-role--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add growthenginenowoslawski/coldoutboundskills --skill playbook-new-in-role -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install growthenginenowoslawski/coldoutboundskills playbook-new-in-role --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/growthenginenowoslawski/coldoutboundskills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/playbooks/playbook-new-in-role .gemini/skills/playbook-new-in-role && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "playbook-new-in-role" agent skill from https://github.com/growthenginenowoslawski/coldoutboundskills/tree/main/skills/playbooks/playbook-new-in-role into .gemini/skills/playbook-new-in-role/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playbook-new-in-role", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install growthenginenowoslawski/coldoutboundskills playbook-new-in-roleInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add growthenginenowoslawski/coldoutboundskills --skill playbook-new-in-role -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/growthenginenowoslawski/coldoutboundskills.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/playbooks/playbook-new-in-role .github/skills/playbook-new-in-role && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "playbook-new-in-role" agent skill from https://github.com/growthenginenowoslawski/coldoutboundskills/tree/main/skills/playbooks/playbook-new-in-role into .github/skills/playbook-new-in-role/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playbook-new-in-role", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add growthenginenowoslawski/coldoutboundskills --skill playbook-new-in-role -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install growthenginenowoslawski/coldoutboundskills playbook-new-in-role --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/growthenginenowoslawski/coldoutboundskills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/playbooks/playbook-new-in-role .opencode/skills/playbook-new-in-role && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "playbook-new-in-role" agent skill from https://github.com/growthenginenowoslawski/coldoutboundskills/tree/main/skills/playbooks/playbook-new-in-role into .opencode/skills/playbook-new-in-role/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "playbook-new-in-role", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
playbook-new-in-roleProduces a copy-ready clause about someone who recently started or changed into their current job title.
Playbook New In Role is an agent skill from growthenginenowoslawski/coldoutboundskills. Produces a copy-ready clause about someone who recently started or changed into their current job title. Triggers on "who just started", "new in role", "recently promoted", "new VP of X", "job change signal", "people who just took the seat". Outputs newinroleline, a lowercase clause that completes "Saw <line."
Its SKILL.md is about 6k tokens, which your agent loads only when the skill is triggered. The skill folder holds 2 other files (for example `clay-table.md` and `clay-workflow.md`).
The repository describes itself as: Open-source Claude Code skills for cold email and outbound sales. Grade campaigns, export Prospeo searches, scrape Google Maps — all from Claude Code. The licence is MIT.
7 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 25c5d85. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are json).
From the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
api.prospeo.ioapi.openai.comFrom URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
PROSPEO_API_KEYFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Playbook New In Role loads about 6k tokens when it runs. Until then it costs about 84 tokens; SKILL.md has 3,028 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from growthenginenowoslawski/coldoutboundskills at commit 25c5d85, republished under its MIT licence (© growthenginenowoslawski). 3,028 words, ~6,034 tokens.
.claude/skills/playbook-new-in-role/SKILL.md (or your agent's skills folder). This skill also uses 2 other files; get the full folder from GitHub.All rules here are best practice, not law. Override any of them when the campaign calls for it; note the best practice once and move on.
Use when: the campaign angle depends on the buyer being new in the seat, because a new leader is rebuilding their stack, has budget to reallocate, and has not yet formed a vendor preference.
Do not use when: you want people who moved to a new company regardless of title, or you
want the company-level hiring story. Use playbook-hiring-surge for headcount growth and
playbook-fundraising for the "they just raised, so they are buying" angle.
One-line output: new_in_role_line = "you stepped into the COO seat at Northwind in April"
This playbook answers one question: which people in my target market took their current job title in the last N months, and what one sentence can I open an email with?
It is a filter-at-source signal, not an enrichment. You do not buy a list and then ask a vendor "is this person new?". You ask the people database to only return people whose current role started recently, and the answer comes back with the start date already attached. That is why it costs essentially nothing and why the false-positive rate on tenure is near zero.
Two things it explicitly does not do. It does not tell you whether the move was a step up, only that the person changed titles — a President who becomes COO trips the same signal, and the locked prompt is written so the copy stays true either way. It does not find the person's email; run your email waterfall first so you never spend enrichment on rows that will fail the email gate.
Default: 90 days, expressed as person_time_in_current_role: {min:0, max:3} (the filter is
month-level, so 3 months is the month-level expression of 90 days).
Know the volume cost before you build the lane, because it is steep. Measured on one base
(US, VP of Operations, headcount 50 to 1000):
| Window | Rows |
|---|---|
| unfiltered | 1,662 |
| 0 to 6 months | 22 |
| 0 to 3 months | 2 |
Tightening from 6 months to 90 days cost about 90% of the rows on that base. A single title at 90 days is a handful of people, not a campaign.
Widen when the list is too thin, in this order:
Record the widening and the reason in the campaign brief.
Decide before you build. These are not the same product.
| Cheap pass (default) | Robust pass (opt in) | |
|---|---|---|
| Source | Prospeo /search-person | a LinkedIn Sales Navigator scraper on Apify |
| Cost | effectively $0 for the data, $0.053 per 1,000 rows for the model | metered: roughly $3.20 per 1,000 profiles discovered plus $4 per 1,000 hydrated to full profiles |
| Tenure precision | month-level. {min:0, max:6} is an exact month range | coarse. A "changed jobs in the last 90 days" boolean, and a bucket that bottoms out at "less than 1 year". You cannot ask for 0 to 6 months |
| Freshness | database snapshot, can lead a public announcement by about a month | LinkedIn-live, but LinkedIn keeps stale results, so a cleanup pass is mandatory |
| Use it when | almost always | the client is high value, the vertical is one the database is thin on, or the campaign's whole premise is the job change |
Worth internalizing, because it generalizes past this playbook: the expensive LinkedIn-live source has a coarser tenure filter than the free database. You buy freshness and coverage, and you pay for it in precision plus a mandatory cleanup pass. Never run the robust pass silently because the cheap pass came back thin. Say what it will cost, get a yes, then run it.
| Field | Type | Source | Required? |
|---|---|---|---|
| target job titles | string[] | campaign brief / ICP | yes, this is the search input |
| headcount range | {min,max} int | ICP | yes |
| geography | string[] | ICP, as location strings like "United States #US" | yes |
| recency window (months) | int | operator, default 3, ceiling 9 | yes |
company_domain (bare, lowercase, no www) | string | returned by the search | produced, not supplied |
Rows are produced by this playbook, not fed into it. If you already have a list and want to
know which of those people are new in role, restrict the same call with
company: { websites: { include: [ ...up to 500 domains... ] } }.
| Field | Type | Example | Max length | Null allowed? |
|---|---|---|---|---|
new_in_role_line | string | you stepped into the COO seat at Northwind in April | 90 chars | yes, empty string |
role_change_type | enum | promotion or new_hire | 9 | no |
role_start_month | string | April 2026 | 20 | no |
months_in_role | int | 4 | n/a | no |
prior_title | string | Chief Business Officer | 120 | yes, empty string |
role_change_type, role_start_month, months_in_role and prior_title are computed
deterministically from the source's job_history array. The model never decides them. Only
new_in_role_line is written by the model.
Abstain value: "" (empty string). Never "N/A", never "null", never a guess.
Read these as three separate numbers. They are not the same thing, and campaign sizing depends on telling them apart. Measured on 10 rows:
| Metric | Measured | What it means |
|---|---|---|
| Produced rate (non-empty line) | 10/10 | Of the rows that clear the title gate, essentially all get a line. This is not a rate over the raw pull — the title-gate drop rate was not recorded, so size the campaign off rows that survive the gate |
| Factual-error rate | 1/10 | About 1 in 10 prospects could receive a confident but wrong statement about their own job title. This is the real exposure |
| Empty rate (model abstained) | 0/10 | The abstain path exists but did not fire |
| Usable rate (produced and correct) | 9/10 | The headline number |
The single error was not a missing value and not a tenure error. The database asserted a title the company's own site contradicts, and the model faithfully turned that wrong fact into polished copy. No downstream verifier over your own output can catch this, because the output is a correct rewrite of a wrong input. See §6 for the optional cross-check.
The "zero false positives" result below refers only to tenure. It is not a statement about title accuracy.
Because the signal is a source-side filter, coverage on the rows it returns is high by construction. The number that varies is volume, not hit rate.
Saw {{new_in_role_line}}. as the first sentence of email 1.If new_in_role_line is empty: drop the personalized clause via spintax and keep the row — the
row still matches the ICP. Do not leave a blank gap in the email. If the campaign's entire premise
is the job change (a "congrats on the new seat" campaign), exclude the row instead and say so in
the brief.
The empty case is the cheap case. The expensive case is the ~10% wrong-title case above, which arrives as a non-empty, well-formed line and therefore passes every structural QC.
There are two controls and they address different failures. Do not confuse them:
person_job_title is a
loose contains match that can hit a past role, so the pull returns people whose current
title is unrelated to the search (measured: a Chief Operating Officer query returned an
"Adult Basic Education Instructor"). The gate drops those. It cannot catch a wrong source
title — the one observed factual error had a job_history current title that did contain
the target keyword, so the gate passed it and the wrong line shipped. Running the gate does not
reduce the ~10% factual-error rate at all.playbook-google-site-search). Turn this on for any client sensitive to being wrong about a
prospect's title. If you skip it, budget for roughly 1 in 10 prospects receiving a confident but
wrong statement about their own job.| # | Source | Cost | Exact call | Stop rule |
|---|---|---|---|---|
| 1 | Prospeo /search-person | FREE to CHEAP | POST https://api.prospeo.io/search-person, header X-KEY: $PROSPEO_API_KEY, body below | always start here |
| 2 | Clay Find people source, built in a new workbook through the UI | FREE to METERED | set job title, location, headcount and the recent-role-change filter in the UI, run, then export or webhook the rows out | only if step 1 returned fewer rows than the campaign needs |
| 3 | Apify LinkedIn Sales Navigator scraper (the robust pass) | EXPENSIVE | actor with recentlyChangedJobs: true, currentJobTitles, locations, companyHeadcount, profileScraperMode: "Full" | only when the operator chose the robust pass and knows the cost |
| 4 | Model line writer | CHEAP ($0.053 / 1,000 rows) | POST https://api.openai.com/v1/chat/completions, params in §6 | always runs, on whichever source produced the facts |
Step 1's body:
{ "page": 1,
"filters": {
"person_location_search": { "include": ["United States #US"] },
"person_job_title": { "include": ["Chief Operating Officer"] },
"company_headcount_custom": { "min": 50, "max": 2000 },
"person_time_in_current_role": { "min": 0, "max": 3 }
} }max:6 is what was graded; max:3 is the default. The hit rate does not move with the window,
only the volume does.
role_change_type, prior_title and prior_company cannot be derived at all and
the promotion-versus-new-hire branch disappears. Worth generalizing: "needs no browser" is not
automatically the better path. When a substitution changes what comes back, that is a contract
change, not a convenience.person_time_in_current_company as the primary. It answers "how long at the company", which
misses every internal promotion — and internal promotions were 8 of the 10 rows in the live
test. Use it only as a secondary filter to separate new hires from promotions.VERDICT: PASS 9/10 (90%) | best call = Prospeo /search-person with
person_time_in_current_role → model line writer at minimal reasoning effort | p50 latency
1.1s/row | ~$0.05 per 1,000 rows | tested on 10 graded rows.
A separate true-negative test confirmed 3 of 3 long-tenure incumbents (30, 43 and 145 months in seat) are present in the database and correctly excluded by the filter. Zero false positives on tenure.
This verdict covers exactly one path: the script path calling Prospeo /search-person and then
the model at minimal reasoning effort, graded end to end against a second source per row.
| Path | State |
|---|---|
| Script: Prospeo → model API | GRADED, PASS 9/10 |
Clay table recipe (clay-table.md) | ⚠️ specification, never built |
Clay workflow recipe (clay-workflow.md) | ⚠️ specification, never built |
| Clay "Find people" source (#2) | ⚠️ never built — row shape and tenure granularity unknown |
| Apify robust pass (#3) | ⚠️ never run — filter grammar read from the actor schema only |
Re-test if the usable rate drops below 60% for two consecutive campaigns, if the filter schema changes, or if a campaign reports a "you just started" line landing on a long-tenured incumbent.
Two ways to run this continuously:
clay-table.md — build it as columns on a table. Read clay-playbooks/clay-table-harness.md first.clay-workflow.md — build it as a workflow from the CLI. Read clay-playbooks/clay-cli-harness.md first.Both are unbuilt specifications. The script path in §4 is the verified one.
Model: a small reasoning model (gpt-5-nano class). On a measured 732 input and 42 output tokens
per row it is roughly 3x cheaper than a mini-tier model with no quality gap on a pure rewrite.
Params: max_completion_tokens=2000, reasoning_effort="minimal", no temperature, and a
flex/batch service tier for overnight runs.
You write one short opening clause for a cold email, about a person who recently changed jobs.
You will be given verified facts about one person. The facts are already true. Your only job is to turn them into one natural clause.
Return JSON only, exactly these keys:
{"new_in_role_line": "...", "role_change_type": "promotion|new_hire", "confidence": "high|low"}
Rules for new_in_role_line:
- It must read correctly inside this sentence: "Saw <new_in_role_line>."
- Start with a lowercase letter. No period at the end. No quotation marks.
- Maximum 90 characters.
- Say the seat and the company and roughly when. Use the month name given, or say "earlier this year" if the month is more than 4 months ago.
- Only say "earlier this year" if the start year given is the CURRENT year. If the start year is any earlier year, say the month and the year, for example "in September 2026". Never say "earlier this year" about a date in a previous year.
- If role_change_type is promotion, say they stepped into or took over the seat. Do not say they joined the company. Never say "moved up", "got promoted", or "was promoted": an internal move is not always a step up and we cannot prove it was.
- If role_change_type is new_hire, you may say they joined.
- Use the shortest natural form of the company name. Drop anything inside parentheses, drop legal suffixes like LLC, Inc, PLC, Ltd, and drop trailing descriptive phrases after a comma. "ATG (Auction Technology Group)" becomes "ATG". "A.Y. Strauss, LLC" becomes "A.Y. Strauss".
- 5th-grade reading level. Short words.
- No em dashes. No en dashes. Hyphens are fine only inside a number range.
- Never invent a fact. Only use the facts given. Do not mention headcount, industry, funding, or anything not in the facts.
- If the facts are missing the title, the company, or the start month, return "" for new_in_role_line and "low" for confidence.
Examples:
Facts: first_name=Dana | current_title=VP of Operations | company_name=Gymshark | role_start_month=March 2026 | months_in_role=3 | role_change_type=new_hire | prior_title=Director of Supply Chain | prior_company=Represent
Output: {"new_in_role_line": "you joined Gymshark as VP of Operations back in March", "role_change_type": "new_hire", "confidence": "high"}
Facts: first_name=Marcus | current_title=Chief Operating Officer | company_name=Irby Utilities, LLC | role_start_month=February 2026 | months_in_role=6 | role_change_type=promotion | prior_title=Senior Vice President | prior_company=Irby Utilities, LLC
Output: {"new_in_role_line": "you stepped into the COO seat at Irby earlier this year", "role_change_type": "promotion", "confidence": "high"}
Facts: first_name=Priya | current_title= | company_name=Northwind Labs | role_start_month= | months_in_role= | role_change_type=new_hire | prior_title= | prior_company=
Output: {"new_in_role_line": "", "role_change_type": "new_hire", "confidence": "low"}
PER-ROW DATA (appended last, as the user message, never merged into the block above)
Facts: first_name={{First Name}} | current_title={{Current Title (from job_history)}} | company_name={{Company Name Clean}} | role_start_month={{Role Start Month Label}} | months_in_role={{Months In Role}} | role_change_type={{Role Change Type}} | prior_title={{Prior Title}} | prior_company={{Prior Company}}The cross-year guard is load-bearing. The "earlier this year" rule was originally unqualified,
which generates factually false copy for any role that started in the previous calendar year.
With a 6-month window, every run between January and May hits those rows: a run in February sees
roles that started in September, five months earlier, and the model would write "earlier this
year" about last year. Both months_in_role and the year are already in the facts, so the guard
is free. This case is unmeasured — the graded run happened in August, so no graded row had a
previous-year start. Re-grade 5 rows the first time you run a window that crosses a year boundary.
Date format, one rule: role_start_month is always the month-name form (April 2026),
never 2026-04. The prompt says "use the month name given", so a numeric month would force the
model to convert it silently, which is exactly the drift the locked prompt exists to prevent.
The model's role_change_type is discarded. The prompt still asks for it — that is the exact
prompt that was graded, so it stays byte-identical — but it is an echo, not a decision. The real
value is computed deterministically from job_history. If the model's value were ever allowed
through, a flipped promotion to new_hire would make the copy claim someone "joined" a company
they have worked at for years.
Everything above the PER-ROW DATA marker is the static prefix and stays static.
⚠️ No prompt-cache discount applies here, and the cost math must not assume one. Automatic prompt caching engages at 1,024+ prompt tokens. This prompt is 732, so it never qualifies and the effective floor stays $0.053 per 1,000 rows. Padding the prefix past 1,024 tokens costs more input tokens than the discount returns at this size. Keep the static-prefix-first structure anyway — it starts paying the moment the prompt grows.
Verifier pass: not needed, by design. The model is never given the open web and is never asked
to establish a fact. Every fact in its input was already proven by the structured job_history,
and the deterministic fields are computed in code before the call. The residual risk is that the
source is wrong about the title, which no verifier over your own output would catch. If a
client is sensitive to that, add the leadership-page cross-check rather than a verifier over the
generated line.
Truncation guard: finish_reason=length means retry, never abstain. This fired on 10/10
rows at default reasoning effort, returning empty content while burning the full budget on
reasoning. reasoning_effort:"minimal" is the fix and is not optional.
| Symptom | Cause | Fix |
|---|---|---|
Every call returns HTTP 400 INVALID_FILTERS | You used person_time_in_current_position | The real key is person_time_in_current_role, value {min,max} integer months, 0 to 600. There is no string range form; "0-6" is rejected |
| Filter silently does nothing, list is full of 10-year incumbents | A script swallowed the 400 above and shipped the unfiltered result | Assert that pagination.total_count dropped versus the unfiltered baseline, and fail the run on any non-200 |
| A made-up filter name returns the same error as a real one | INVALID_FILTERS does not distinguish "unknown key" from "bad value" | Do not discover keys by probing. Read the filter docs |
error_code: NO_RESULTS kills the whole run | An empty result set comes back as HTTP 400 with {"error":true,"error_code":"NO_RESULTS"}, so if (j.error) throw crashes on a normal empty pull — common on the narrow windows this playbook recommends | Branch on error_code === "NO_RESULTS" before the generic error branch and return zero rows. Do not retry it |
company_domain filter returns INVALID_FILTERS | Wrong key shape | Use company: { websites: { include: ["acme.com"] } }, max 500 domains |
| Result has a current title unrelated to the search | person_job_title is a loose contains match and can hit a past role | Gate in code on job_history[current].title, never on person.current_job_title. The gate list must include abbreviations ("COO", "VP Ops"), not just the searched titles |
| Line says "moved up" but the prior title was more senior | positions_at_company > 1 proves an internal move, never its direction | The locked prompt bans "moved up" and always says "stepped into". Do not relax this |
| Line contains a legal suffix or a parenthetical | Raw company name reached the prompt | Run playbook-company-name-cleaning first. The prompt strips them as a second line of defence |
| Copy says a month one off the press release | Start month can precede the public announcement by about a month | Acceptable. The prompt may say "earlier this year" for anything over 4 months old |
| LinkedIn URL does not resolve, or resolves to a different person | Stale slug (measured on 1 of 10 rows) | Never key the email waterfall solely on the returned LinkedIn URL; work from name plus domain too |
| Model returns empty strings for every row | finish_reason=length, reasoning ate the budget | reasoning_effort:"minimal". Retry on length, never record it as an abstain |
| Volume collapses to almost nothing | Expected at the 90-day default | Widen the title list first, then headcount or geography, then the window to 6 months. Never past 9 |
| The robust pass returns people who changed jobs a year ago | LinkedIn keeps stale results. The 90-day flag is LinkedIn's own and it is not aggressively retired | Cleanup is mandatory on every robust-pass run, budget for it before you quote the job. Hydrate to full profiles, read the current experience start date, drop every row outside your window. Treat the filter as a cheap pre-filter, not the gate |
curl. Send a browser User-Agent on every call, or your batch silently
403s while your manual test passes.email_status: "VERIFIED" is not send-ready. Everything still goes through
your own validation waterfall.© growthenginenowoslawski, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 2 other files in skills/playbooks/playbook-new-in-role of growthenginenowoslawski/coldoutboundskills.
Open the folder on GitHubat commit 25c5d85
Playbook New In Role next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Playbook New In Role this skillgrowthenginenowoslawski/coldoutboundskills | 753 | — | ~6k | Automated safety check: Pass | MIT | |
| Copy Editingcoreyhaines31/marketingskills | 54k | 1 repos | ~4.5k | Automated safety check: Pass | MIT | |
| Aria Rolesthedaviddias/Front-End-Checklist | 74k | — | ~515 | Automated safety check: Pass | MIT | |
| Azure Role Selectorgithub/awesome-copilot | 40k | 2 repos | ~246 | Automated safety check: Pass | MIT | |
| Aria Deprecated Rolethedaviddias/Front-End-Checklist | 74k | — | ~441 | Automated safety check: Pass | MIT | |
| Bio Copy Number Subclonal Copy NumberGPTomics/bioSkills | 1.2k | 2 repos | ~3.5k | Automated safety check: Pass | MIT |
coreyhaines31/marketingskills
When the user wants to edit, review, or improve existing marketing copy, or refresh outdated content.
thedaviddias/Front-End-Checklist
A skill your agent uses when reviewing rendered HTML, interactive components, or design-system patterns related to Use valid ARIA role values.
github/awesome-copilot
When user is asking for guidance for which role to assign to an identity given desired permissions, this agent helps them understand the role that will meet the requirements with least privilege…
thedaviddias/Front-End-Checklist
A skill your agent uses when reviewing rendered HTML, interactive components, or design-system patterns related to Avoid using deprecated ARIA roles.
GPTomics/bioSkills
Resolve subclonal copy number, whole-genome doubling, and copy-number tumor evolution from bulk sequencing with Battenberg, TITAN, and MEDICC2.
mukul975/Anthropic-Cybersecurity-Skills
Runs a hypothesis-driven threat hunt for Volume Shadow Copy deletion (T1490) by querying SIEM/EDR telemetry for vssadmin, wmic shadowcopy, and PowerShell shadow-copy-deletion commands.
growthenginenowoslawski/coldoutboundskills
Diagnostic audit for a running cold email program. An agent skill from growthenginenowoslawski/coldoutboundskills.
growthenginenowoslawski/coldoutboundskills
Conversational intake for cold email campaigns. An agent skill from growthenginenowoslawski/coldoutboundskills.
growthenginenowoslawski/coldoutboundskills
META skill — build the largest possible qualified lead list for any request, end to end.
growthenginenowoslawski/coldoutboundskills
Autonomous cold email campaign launcher. An agent skill from growthenginenowoslawski/coldoutboundskills.
growthenginenowoslawski/coldoutboundskills
Use the Blitz API to find decision-makers at specific companies when you already have a list of company domains.
growthenginenowoslawski/coldoutboundskills
Compare reply rates, bounce rates, and positive reply rates broken down by inbox type (SMTP / Gmail / Outlook) for a Smartlead account.
Produces a copy-ready clause about someone who recently started or changed into their current job title. Playbook New In Role is an agent skill from growthenginenowoslawski/coldoutboundskills. Produces a copy-ready clause about someone who recently started or changed into their current job title.
Playbook New In Role fits situations like: who just started; recently promoted; job change signal; people who just took the seat.
Run `npx skills add growthenginenowoslawski/coldoutboundskills --skill playbook-new-in-role -a claude-code`. Or copy the skill folder (skills/playbooks/playbook-new-in-role in growthenginenowoslawski/coldoutboundskills) into .claude/skills/playbook-new-in-role in your project. Claude Code loads it when a task matches its description.
Run `npx skills add growthenginenowoslawski/coldoutboundskills --skill playbook-new-in-role -a codex`. Or copy the skill folder (skills/playbooks/playbook-new-in-role in growthenginenowoslawski/coldoutboundskills) into .agents/skills/playbook-new-in-role in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add growthenginenowoslawski/coldoutboundskills --skill playbook-new-in-role -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/playbook-new-in-role, .gemini/skills/playbook-new-in-role, .github/skills/playbook-new-in-role and .opencode/skills/playbook-new-in-role in your project.
Going by SKILL.md and its folder, Playbook New In Role needs credentials named PROSPEO_API_KEY. Our summary lists: A credential in PROSPEO_API_KEY.
SKILL.md names 2 domains. In commands or code: api.prospeo.io and api.openai.com; the agent is likely to contact these when it follows the instructions. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Playbook New In Role is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 6k tokens (SKILL.md is roughly 24k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Playbook New In Role: Copy Editing (coreyhaines31/marketingskills, 54k stars), Aria Roles (thedaviddias/Front-End-Checklist, 74k stars), Azure Role Selector (github/awesome-copilot, 40k stars) and Aria Deprecated Role (thedaviddias/Front-End-Checklist, 74k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
growthenginenowoslawski (a GitHub user) maintains it in growthenginenowoslawski/coldoutboundskills, which has 753 GitHub stars. The repository holds 49 skills in this directory. The repository was last updated on October 5, 2026.
Source: growthenginenowoslawski/coldoutboundskills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.