Roam Evidence Hardening
Cranot/roam-code
Investigate and harden Roam detectors, CLI/MCP result contracts, and evidence consumers when dogfooding or correcting incomplete, misleading, or inconsistent analysis.
Agent skill
by Othmane-Khadri in Othmane-Khadri/YALC-the-GTM-operating-system
A skill your agent uses when the user says "create a lemlist campaign for X", "build a lemlist campaign from this ICP", "spin a lemlist campaign for Y", "ICP to lemlist", "natural language to…
$ npx skills add Othmane-Khadri/YALC-the-GTM-operating-system --skill lemlist-campaign-from-icp -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install Othmane-Khadri/YALC-the-GTM-operating-system lemlist-campaign-from-icp --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/Othmane-Khadri/YALC-the-GTM-operating-system.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/lemlist-campaign-from-icp .claude/skills/lemlist-campaign-from-icp && 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 "lemlist-campaign-from-icp" agent skill from https://github.com/Othmane-Khadri/YALC-the-GTM-operating-system/tree/main/.claude/skills/lemlist-campaign-from-icp into .claude/skills/lemlist-campaign-from-icp/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "lemlist-campaign-from-icp", 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/Othmane-Khadri/YALC-the-GTM-operating-system/tree/main/.claude/skills/lemlist-campaign-from-icpType 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 Othmane-Khadri/YALC-the-GTM-operating-system --skill lemlist-campaign-from-icp -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install Othmane-Khadri/YALC-the-GTM-operating-system lemlist-campaign-from-icp --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Othmane-Khadri/YALC-the-GTM-operating-system.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.claude/skills/lemlist-campaign-from-icp .agents/skills/lemlist-campaign-from-icp && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "lemlist-campaign-from-icp" agent skill from https://github.com/Othmane-Khadri/YALC-the-GTM-operating-system/tree/main/.claude/skills/lemlist-campaign-from-icp into .agents/skills/lemlist-campaign-from-icp/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "lemlist-campaign-from-icp", 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 Othmane-Khadri/YALC-the-GTM-operating-system --skill lemlist-campaign-from-icp -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install Othmane-Khadri/YALC-the-GTM-operating-system lemlist-campaign-from-icp --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Othmane-Khadri/YALC-the-GTM-operating-system.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.claude/skills/lemlist-campaign-from-icp .cursor/skills/lemlist-campaign-from-icp && 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 "lemlist-campaign-from-icp" agent skill from https://github.com/Othmane-Khadri/YALC-the-GTM-operating-system/tree/main/.claude/skills/lemlist-campaign-from-icp into .cursor/skills/lemlist-campaign-from-icp/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "lemlist-campaign-from-icp", 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/Othmane-Khadri/YALC-the-GTM-operating-system.git --path .claude/skills/lemlist-campaign-from-icp--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 Othmane-Khadri/YALC-the-GTM-operating-system --skill lemlist-campaign-from-icp -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install Othmane-Khadri/YALC-the-GTM-operating-system lemlist-campaign-from-icp --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Othmane-Khadri/YALC-the-GTM-operating-system.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.claude/skills/lemlist-campaign-from-icp .gemini/skills/lemlist-campaign-from-icp && 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 "lemlist-campaign-from-icp" agent skill from https://github.com/Othmane-Khadri/YALC-the-GTM-operating-system/tree/main/.claude/skills/lemlist-campaign-from-icp into .gemini/skills/lemlist-campaign-from-icp/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "lemlist-campaign-from-icp", 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 Othmane-Khadri/YALC-the-GTM-operating-system lemlist-campaign-from-icpInstalls 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 Othmane-Khadri/YALC-the-GTM-operating-system --skill lemlist-campaign-from-icp -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/Othmane-Khadri/YALC-the-GTM-operating-system.git skills-src && mkdir -p .github/skills && cp -r skills-src/.claude/skills/lemlist-campaign-from-icp .github/skills/lemlist-campaign-from-icp && 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 "lemlist-campaign-from-icp" agent skill from https://github.com/Othmane-Khadri/YALC-the-GTM-operating-system/tree/main/.claude/skills/lemlist-campaign-from-icp into .github/skills/lemlist-campaign-from-icp/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "lemlist-campaign-from-icp", 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 Othmane-Khadri/YALC-the-GTM-operating-system --skill lemlist-campaign-from-icp -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install Othmane-Khadri/YALC-the-GTM-operating-system lemlist-campaign-from-icp --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Othmane-Khadri/YALC-the-GTM-operating-system.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.claude/skills/lemlist-campaign-from-icp .opencode/skills/lemlist-campaign-from-icp && 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 "lemlist-campaign-from-icp" agent skill from https://github.com/Othmane-Khadri/YALC-the-GTM-operating-system/tree/main/.claude/skills/lemlist-campaign-from-icp into .opencode/skills/lemlist-campaign-from-icp/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "lemlist-campaign-from-icp", 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.
lemlist-campaign-from-icpA skill your agent uses when the user says "create a lemlist campaign for X", "build a lemlist campaign from this ICP", "spin a lemlist campaign for Y", "ICP to lemlist", "natural language to…
Lemlist Campaign From Icp is an agent skill from Othmane-Khadri/YALC-the-GTM-operating-system. Use when the user says "create a lemlist campaign for X", "build a lemlist campaign from this ICP", "spin a lemlist campaign for Y", "ICP to lemlist", "natural language to lemlist campaign", "draft a lemlist campaign", "lemlist campaign for VPs/Managers/ICs at Z", or any variant indicating they want to turn a natural-language ICP description into a paused, ready-to-review lemlist campaign with sourced leads and a seniority-routed sequence. Orchestrates 24 lemlist atomic skills (ICP, persona, sourcing…
Its SKILL.md is about 6.5k tokens, which your agent loads only when the skill is triggered. The skill folder holds 11 other files (for example `CHANGELOG.md`, `README.md` and `examples/dryrun-output-sample.json`).
It sits in Writing & Content, covering MCP servers, Copywriting and End-to-end testing. It works with Model Context Protocol. The repository describes itself as: YALC 1.0, the open-source Clay alternative. MIT, CLI-first, self-hosted, runs in Claude Code. Yalc today is an intelligent orchestration layer that runs pre configured GTM agents…. The licence is MIT.
9 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 5686d1f. 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.
Shell commands in SKILL.md call:
claudeFrom the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
github.comlemlist.comget.lemlist.comyalc.aiFrom URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
LEMLIST_API_KEYFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Lemlist Campaign From Icp loads about 6.5k tokens when it runs. Until then it costs about 243 tokens; SKILL.md has 3,072 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 Othmane-Khadri/YALC-the-GTM-operating-system at commit 5686d1f, republished under its MIT licence (© Othmane-Khadri). 3,072 words, ~6,471 tokens.
.claude/skills/lemlist-campaign-from-icp/SKILL.md (or your agent's skills folder). This skill also uses 9 other files; get the full folder from GitHub.Turn a natural-language ICP prompt into a paused live lemlist campaign — leads sourced from lemlist's People Database, agentically enriched, routed by seniority, sequenced, refined, scored against the 244K-campaign benchmark, and pushed into a real lemlist account in PAUSED state for human review.
The user types one prompt. The skill chains 24 lemlist atomic skills + the lemlist MCP server. Approval gates fire before the campaign is created in lemlist. Nothing auto-sends.
LEMLIST_API_KEY= # https://app.lemlist.com → Settings → IntegrationsOAuth alternative (interactive use): claude mcp add --transport http lemlist https://app.lemlist.com/mcp.
The lemlist MCP must be connected. Verify with /mcp in Claude Code — you should see operations like get_lemleads_filters, lemleads_search, create_campaign_with_sequence, add_sequence_step, add_lead_to_campaign, set_campaign_state, validate_campaign_readiness, campaign stats, etc.
This skill depends on the 24 atomic lemlist skills bundled under .claude/skills/lemlist/. They were imported from lemlist's open-source skill library under MIT. See .claude/skills/lemlist/README.md for the attribution and update procedure.
This skill creates a campaign in a real lemlist account using paid lemlist credits (sourcing + agentic enrichment). It must never auto-send. Safeguards:
create_campaign_with_sequence. The orchestrator MUST NOT call set_campaign_state with action start. Verify with validate_campaign_readiness before reporting readiness to the user. The campaign stays in DRAFT in the user's lemlist account until the user starts it manually in the lemlist UI.~/.gtm-os/lemlist-campaign-from-icp/dryrun-{timestamp}.json. Quote the file path back to the user."Approve creating campaign '{title}' in lemlist with {N} leads and the sequence above? Type 'approve' to push, anything else to abort." Block on the user response. Do not call any campaign-creation or lead-add MCP tool until the user types approve.set_campaign_state action archive can return HTTP 500 intermittently — never retry blindly; surface the error and let the user clean up via the lemlist UI.When Claude invokes this skill on a user's behalf:
approve before calling any campaign-creation or lead-add MCP tool.Each substrate skill returns prose. Claude must extract the listed fields at each stage and persist them in a working memory object that the orchestrator threads forward. If a field cannot be extracted, apply the fallback. Never proceed past stage 17 with empty emails[].
| Stage | Substrate skill | Field(s) to extract | Fallback if missing |
|---|---|---|---|
| 1 | icp-definer | industries[], geo[], size_range_employees[], active_signals[] | Ask user to confirm before stage 2 |
| 2 | persona-definer | personas[].title_patterns[], personas[].seniority_tier (must be VP+ / Manager / IC), personas[].pains_identified[] | If seniority_tier ambiguous, map titles: VP*/SVP*/Chief* → VP+; Manager*/Head of*/Director* → Manager; else IC. |
| 3 | pain-identifier | pains[] (text) | Continue without; mark as pains: [] in dryrun |
| 4 | value-prop-lister | value_props[] | Ask user once |
| 5 | offer-definer | offer (text) | Derive from value_props[0] |
| 6 | competitor-finder | competitors[].{name, differentiation} | Skip; mark competitors: [] |
| 7 | trigger-finder | triggers[] | Skip; mark triggers: [] |
| 8 | company-finder | firmographic_filters[] (as filterId+in/out triplets) | Map from ICP fields with the active filter registry from stage 11a |
| 9 | list-builder | combined_filters[] | Equal to firmographic_filters if no extra signals |
| 10 | people-finder | persona_filters[] (currentTitle, seniority) | Derive from persona.title_patterns |
| 13 | linkedin-outbound-angle | lead.angle (text, per-lead) | Default to generic offer-led angle |
| 14 | campaign-angle-finder | chosen_angle | Pick first if user does not respond |
| 15 | outbound-campaign-architect | sequence_shape: { steps: [{delay_days, channel}] } | Default: 3 emails, delays [0, 3, 6] |
| 17 | copywriting-{vp,manager,ic}-sequence | emails[].{subject, body} (length 3) | Block; do not proceed without |
| 18 | copywriting-first-touch | emails[0] rewrite | Keep original if no rewrite |
| 19 | copywriting-follow-up | emails[1..2] rewrites | Keep originals |
| 20 | cta-designer | emails[].cta overrides | Keep originals |
| 21 | copywriting-refiner | emails[].refined | Keep originals |
| 22 | copywriting-analyzer | score (0-100), improvement_notes[] | Mark score: null; surface to user |
| 23 | gtm-action-thinker | weakest_assumption (text) | Skip |
The skill walks Claude Code through the following chain. Each stage names the lemlist atomic skill or MCP operation it invokes.
For each step below, invoke the lemlist atomic skill at .claude/skills/lemlist/{skill-name}/SKILL.md. Capture the structured output and feed it forward.
icp-definer — turn the user's natural-language prompt into a structured ICP (firmographics, geography, signal triggers).persona-definer — derive specific buyer personas from the ICP. Each persona carries a seniority tier (VP+ / Manager / IC).pain-identifier — for each persona, identify evidence-based pain points based on growth signals, tech stack, hiring activity.value-prop-lister — extract value props from the user's product context (if shared) or ask the user to confirm 2–3 anchor value props.offer-definer — transform value props into outcome-focused offers, one per persona.competitor-finder — identify 2–3 obvious competitors per persona; generate differentiation angles to weave into the copy.trigger-finder — identify buying triggers (funding rounds, exec hires, tool changes) that the messaging can reference.Lemlist's filter registry can change. Always discover filterIds at runtime rather than hardcoding.
company-finder — translate the ICP into a lemlist firmographic search configuration (industry, size, geography, technographics).
list-builder — combine the firmographic config with signal filters (e.g., active hiring, funding signals) into a single search shape.
people-finder — translate persona seniority + role into lemlist People Database search filters.
11a. MCP call: get_lemleads_filters — fetch the active filter registry.
Payload size warning (verified live, 2026-05-19): the response is ~93K chars / 3,091 lines and cannot fit in an LLM context window in a single tool result. When the MCP returns "result exceeds maximum allowed tokens, output saved to <path>", grep that file for the specific filterIds you need (currentTitle, seniority, country, region, currentCompanyHeadcount, department, currentCompanySubIndustry, etc.) plus their values arrays. Do NOT attempt to load the full registry into chat.
Use the returned filterId values to shape the search filters array. UI-only filters (notInContacts, notInCampaign) are not available over this transport — drop them if surfaced by upstream skills.
Industry-filter leak warning (verified live, 2026-05-20): currentCompanySubIndustry has no clean "B2B SaaS" bucket. The two closest values are Software Development and Technology, Information and Internet, but the latter contains marketplace and consumer-internet companies. When the user's ICP says "B2B SaaS" (or "B2B software"), the orchestrator MUST layer a keywordInCompany filter on top of the subindustry filter — recommended seed terms: ["B2B", "SaaS", "B2B software", "B2B platform"]. Without this layer, non-SaaS leads will leak through and waste sourcing credits.
11b. MCP call: lemleads_search with mode: "people" (or "companies", never "leads") and a filters array of {filterId, in, out} objects derived from stage 11a. Cap size at the lead count ceiling (default 50, max 100 per page). Dedupe results by linkedin_url and email before storing in working memory.
**Company-level dedup (verified live, 2026-05-20):** for VP+ persona targeting where the buyer is the only decision-maker per company, also dedupe by `current_exp_company_name`. This is governed by the `dedupe_by_company` knob, defaulted ON for `seniority_tier: VP+` and OFF for `Manager` and `IC` tiers (where multiple champions per company are useful). When the knob drops a lead, surface the dropped lead in the dryrun's `deduped_leads[]` array with the kept lead's id so the user can override.
**Payload size warning (verified live, 2026-05-19):** each lead returns ~24K chars; a 5-lead call already exceeds 122K chars, a 50-lead call would exceed 1MB. Two mitigations the orchestrator MUST apply:
1. **Pass `excludes`** to drop heavyweight nested objects you don't need at this stage (recommended baseline: `excludes: ["experiences", "interests", "languages", "inferred_skills", "lead_logo_url", "company_description", "techno_used_array"]`).
2. **Expect the response to be saved to a file** by the MCP host when over the context limit. Grep/parse that file with a small script to extract only the per-lead fields needed for stage 12 forward: `full_name`, `potential_email`, `lead_linkedin_url`, `current_exp_company_name`, `country`, `headline`, `seniority`, `department`. Do NOT attempt to load the full response into chat.lemleads_search returns search results, not imported leads. Search results include a potential_email field for most (but not all) leads. The orchestrator does NOT toggle the per-call enrichment flags (findEmail, verifyEmail, linkedinEnrichment, findPhone) on add_lead_to_campaign by default — those cost credits per flag per lead.
Lead email coverage (verified live): in real searches, 70-85% of leads ship with a potential_email; the rest have null or missing. The orchestrator must compute and surface an email_coverage_percent figure in the dryrun (stage 24) so the user can decide what to do with the gap.
Three paths to fill the gap, in order of preference:
fullenrich-plg-reverse-lookup, fullenrich-content-engagers, fullenrich-network-activation, fullenrich-event-attendees, or enrich-with-signals) — these run through the fullenrich MCP server and resolve emails from LinkedIn URL + name + company. Recommended path: enrich the missing-email subset via the appropriate fullenrich skill BEFORE stage 25d, then re-merge.findEmail: true on add_lead_to_campaign — fastest but per-lead-per-flag credit cost. Only use when the user explicitly opts in via an instruction like "enrich emails through lemlist" before approval. Quote the projected credit cost back to the user during the dryrun.linkedin-outbound-angle — for each lead, analyze the enriched LinkedIn profile and pick the strongest personalization angle. Store per-lead.campaign-angle-finder — generate 3 distinct campaign angles for the target persona using the ICP, pains, offer, and triggers. Pick the top angle (or surface the 3 to the user for a one-line decision).outbound-campaign-architect — design the high-level sequence structure (number of touches, channel mix, timing) using lemlist's benchmark data (244K campaigns, 249M emails).persona-definer output:copywriting-vp-sequencecopywriting-manager-sequencecopywriting-ic-sequencecopywriting-first-touch — tighten the opener of each sequence using the per-lead angle from stage 13.copywriting-follow-up — write the follow-up email(s) with fresh angles, citing the pain points from stage 3 and the triggers from stage 7.cta-designer — design reply-worthy CTAs per email. Replace generic asks with value-based prompts.copywriting-refiner — audit the full sequence against the quality checklist. Rewrite any failing elements.
copywriting-analyzer — score the final copy against lemlist's 244K-campaign benchmark. Surface the score and the top 2 improvement notes to the user.
gtm-action-thinker — final challenge pass: "what's the weakest assumption in this campaign? what would break the reply rate?" Surface the answer; rewrite if the user agrees.
Optional auto-apply (verified live, 2026-05-20): the skill input accepts a gtm_thinker_autoapply: true flag. When set, the orchestrator inspects the critique and applies the strongest mechanical fix automatically — typically dropping a saturated filter value (e.g. currentCompanyLastFundingRoundAt: "Less than 1 month" is inbound-saturated and reduces reply rate), tightening a leaky keyword, or removing a duplicated angle from the sequence. Surface the applied change in the dryrun's gtm_thinker_auto_applied[] array (with the original vs. new payload diff). Default is false — the critique stays advisory unless the user opts in.
Render the full dryrun to ~/.gtm-os/lemlist-campaign-from-icp/dryrun-{timestamp}.json:
campaign_title (proposed, user can override)icp (structured)personas[].title_patterns[]personas[].seniority_tier — must be one of "VP+", "Manager", "IC"personas[].routed_sequence_skill — one of copywriting-vp-sequence / copywriting-manager-sequence / copywriting-ic-sequenceproposed_filters[] — array of {filterId, in, out} objects (real filterIds from stage 11a)leads[] — each with linkedin_url, email, email_status, enrichment_planned: bool, angle (per-lead text from stage 13), persona_tiersequence_steps[] — 3 emails with delay_days, subject, body per stepcopywriting_analyzer_score (0-100, may be null if stage 22 failed)email_coverage_percent — (count of leads with potential_email) / total × 100, rounded to nearest intleads_without_email[] — list of {full_name, linkedin_url, company} for leads missing potential_emailenrichment_plan — "skip" | "fullenrich" | "lemlist_findEmail"; default "skip" unless user opts inestimated_lemlist_credits — {sourcing, enrichment} breakdown (enrichment cost is zero unless enrichment_plan = "lemlist_findEmail")mcp_call_plan[] — ordered list of the exact MCP calls + payloads that will fire on approval (campaignId and sequenceId shown as placeholders; real IDs only known after stage 25a fires)coverage_warnings[] — top-line array of ICP→registry-bucket mismatches the user must see before approving. Promoted from buried filter_caveats[] so the user knows which slices of the ICP got lost. Each entry: {axis, requested, registry_bucket_used, lost_segment, est_impact}. Example: {axis: "headcount", requested: "10-80", registry_bucket_used: "11-50", lost_segment: "50-80 (companies in this range will not be sourced)", est_impact: "~25% of TAM excluded"}.deduped_leads[] — leads dropped by the dedupe_by_company knob (stage 11b). Each entry: {dropped_lead_id, dropped_full_name, kept_lead_id, reason}. Empty array if knob is off or no duplicates found.gtm_thinker_auto_applied[] — populated when gtm_thinker_autoapply: true. Each entry: {change_type, before, after, rationale_from_thinker}. Empty array if knob is off.post_push_manual_steps[] — required user actions in the lemlist UI AFTER the MCP push completes but BEFORE the campaign can launch. The MCP transport does not reliably expose every campaign-config endpoint, so some setup must happen in the UI. Standard entries: ["attach a sender (Settings → Senders on the campaign page) — without one, validate_campaign_readiness fails with 'No senders configured on this campaign'"]. Surface this array prominently in the chat summary, NOT buried in the JSON.Quote the file path back to the user. Print a one-paragraph summary in chat: N leads sourced, M personas, X-touch sequence, score Y/100. Approve to push as draft campaign '{title}'? Below the summary, list post_push_manual_steps[] and coverage_warnings[] verbatim so the user sees both before typing approve.
On approve, execute the following ordered MCP calls. Capture returned IDs and thread them forward. Stop immediately on the first failure.
25a. MCP call: create_campaign_with_sequence
Payload: { name: <campaign_title>, subject: <sequence_steps[0].subject>, body: <sequence_steps[0].body>, emoji: <optional>, timezone: <user-supplied or "Europe/Paris"> }
Capture: campaignId (cam_xxx), sequenceId (seq_xxx)
Result: campaign created in DRAFT state with step 1 in place.
Response field warning (verified live, 2026-05-19): the response includes a campaign.status field that may return "running" even though the campaign is actually stored as draft. DO NOT trust the status field from the create response. The actual stored state is draft by default. To verify, you can call set_campaign_state with action: "pause" — if the response is {"success": false, "previousStatus": "draft", ...}, that confirms the campaign is already in draft. The add_sequence_step responses (stages 25b/c below) DO include an accurate campaignStatus: "draft" field — trust those instead.
25b. MCP call: add_sequence_step (for step 2)
Payload: { campaignId, sequenceId, type: "email", delay: <sequence_steps[1].delay_days>, delayType: "within", message: <sequence_steps[1].body>, subject: <omit to send as thread reply>, userConfirmed: true }
25c. MCP call: add_sequence_step (for step 3)
Payload: { campaignId, sequenceId, type: "email", delay: <sequence_steps[2].delay_days>, delayType: "within", message: <sequence_steps[2].body>, subject: <omit to send as thread reply>, userConfirmed: true }
25d. For each lead in leads[]: MCP call: add_lead_to_campaign
Payload: { campaignId, email, firstName, lastName, linkedinUrl, companyName, customVariables: { angle, persona_tier, headline, country }, deduplicate: true }. Enrichment flags (findEmail, verifyEmail, linkedinEnrichment, findPhone) are OFF by default — see stage 12 enrichment posture.
Field naming translation (verified live, 2026-05-19): lemleads_search returns snake_case fields; add_lead_to_campaign expects camelCase. Map them explicitly:
From lemleads_search result (snake_case) | To add_lead_to_campaign (camelCase) | Transform |
|---|---|---|
full_name | firstName + lastName | Split on first space; if no space, firstName = full_name and omit lastName |
potential_email | email | May be null or missing for ~15-30% of leads — see stage 12 |
lead_linkedin_url | linkedinUrl | URL-encoded special chars (e.g. é) round-trip OK |
current_exp_company_name | companyName | If contains ®, ™, etc., consider stripping (lemlist tolerates but UI display can be ugly) |
headline | customVariables.headline | Useful Liquid variable for personalization |
country | customVariables.country | Same |
seniority | customVariables.persona_tier | Map: "Executive Leadership" → "VP+", "Department Leadership" → "VP+", "People Management / Leadership" → "Manager", else "IC" |
Leads with missing potential_email: still call add_lead_to_campaign (the API requires at least one field, not specifically email) but flag them in the dryrun leads_without_email[] array. Email steps will silently skip for those leads at send time.
Rate-limit and partial-failure handling:
failed_leads[] array and continue.succeeded_count, failed_count, and the failed_leads[] summary to the user before stage 25e.failed_count > 0.2 * succeeded_count, STOP and surface the failure summary; do not validate readiness.25e. MCP call: validate_campaign_readiness
Payload: { campaignId }
Surface the result (ready or has_errors + the errors[] array) to the user verbatim.
25f. Print the campaign URL so the user can review in the lemlist UI before starting:
https://app.lemlist.com/campaigns/{campaignId} (verify URL format against lemlist docs at publish time).
The orchestrator NEVER calls set_campaign_state with action start. The campaign stays in DRAFT until the user starts it manually.
outbound-analyst — once the campaign has run for a week, benchmark its stats against lemlist's 244K-campaign data.reply-handler — on every reply event from the lemlist MCP, draft a categorized response. Surface in Notion for human review.These run from a separate orchestration skill (forthcoming) so that lemlist-campaign-from-icp stays focused on the one-prompt-to-paused-campaign flow.
reply-handler companion (above).A paused campaign live in the user's lemlist account, with:
Plus a local dryrun JSON file at ~/.gtm-os/lemlist-campaign-from-icp/dryrun-{timestamp}.json for audit.
.claude/skills/lemlist/ (each has its own SKILL.md)https://app.lemlist.com/mcp (declared in repo-root .mcp.json)Built in partnership with lemlist. Lemlist atomic skills under MIT (lemlist's claude-skills repo README). Orchestration logic open-source under MIT as part of YALC.
© Othmane-Khadri, 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 9 other files in .claude/skills/lemlist-campaign-from-icp of Othmane-Khadri/YALC-the-GTM-operating-system.
Open the folder on GitHubat commit 5686d1f
Lemlist Campaign From Icp 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 |
|---|---|---|---|---|---|---|
| Lemlist Campaign From Icp this skillOthmane-Khadri/YALC-the-GTM-operating-system | 318 | — | ~6.5k | Automated safety check: Pass | MIT | |
| Roam Evidence HardeningCranot/roam-code | 517 | — | ~1.5k | Automated safety check: Pass | Apache-2.0 | |
| Qwen Code E2E TestingQwenLM/qwen-code | 28k | — | ~2.1k | Automated safety check: Pass | Apache-2.0 | |
| Edt MCP AutopilotDitriXNew/EDT-MCP | 296 | — | ~2.5k | Automated safety check: Pass | AGPL-3.0 | |
| Cloud API Recipe AuthoringTencentCloudBase/CloudBase-AI-Toolkit | 1.1k | — | ~3.7k | Automated safety check: Pass | MIT | |
| Ainiee Next TranslationShadowLoveElysia/AiNiee-Next | 177 | — | ~3.3k | Automated safety check: Pass | AGPL-3.0 |
Cranot/roam-code
Investigate and harden Roam detectors, CLI/MCP result contracts, and evidence consumers when dogfooding or correcting incomplete, misleading, or inconsistent analysis.
QwenLM/qwen-code
Guides end-to-end testing of the Qwen Code CLI in headless mode with real model calls, MCP test servers and inspection of raw API traffic.
DitriXNew/EDT-MCP
Autonomous, spec-driven, multi-agent pipeline that takes an EDT-MCP task or issue end-to-end — research → critics → architect → parallel development → review loop → tests → live-stand check →…
TencentCloudBase/CloudBase-AI-Toolkit
Author or revise a cloud-api-operations recipe (config/source/skills/cloud-api-operations/references/recipes/).
ShadowLoveElysia/AiNiee-Next
在用户要求翻译小说、文档、字幕或其他文件内容时,按 AiNiee-Next 的输入优先、长文本询问和 MCP 边界执行;进入 AiNiee 工作流后,原文获取与术语预分析也必须走受控读取工具,禁止自行解包解析。
openfootmanager/openfootmanager
Expose new backend behaviour to the frontend over Tauri IPC.
Othmane-Khadri/YALC-the-GTM-operating-system
Turns a week of Claap-recorded sales calls into action items and focus blocks on a Notion Kanban, delivered with a Slack summary.
Othmane-Khadri/YALC-the-GTM-operating-system
A skill your agent uses when the user says "set up FullEnrich reverse lookup for trial signups", "PLG enrichment webhook", "identify free trial signups via FullEnrich", "reverse email lookup for…
Othmane-Khadri/YALC-the-GTM-operating-system
A skill your agent uses when the user says "enrich people who engaged with this post", "qualify post engagers with FullEnrich", "scrape and enrich LinkedIn post {URL}", "engagers from this post into…
Othmane-Khadri/YALC-the-GTM-operating-system
A skill your agent uses when the user says "enrich this LinkedIn event", "enrich attendees of this event", "enrich this attendees CSV", "scrape and enrich LinkedIn event {URL}", "FullEnrich event…
Othmane-Khadri/YALC-the-GTM-operating-system
Reply to comments on a LinkedIn post via Unipile — drafts thread-aware replies grounded in the user's voice and the conversation context, then sends on approval.
Othmane-Khadri/YALC-the-GTM-operating-system
A skill your agent uses when the user says "activate co-founder network with FullEnrich", "enrich LinkedIn connections export", "qualify my LinkedIn connections CSV", "turn Connections.csv into a…
Works with
Categories
A skill your agent uses when the user says "create a lemlist campaign for X", "build a lemlist campaign from this ICP", "spin a lemlist campaign for Y", "ICP to lemlist", "natural language to…. Lemlist Campaign From Icp is an agent skill from Othmane-Khadri/YALC-the-GTM-operating-system. Use when the user says "create a lemlist campaign for X", "build a lemlist campaign from this ICP", "spin a lemlist campaign for Y", "ICP to lemlist", "natural language to lemlist campaign", "draft a lemlist campaign", "lemlist campaign for VPs/Managers/ICs at Z", or any variant indicating they want to turn a natural-language ICP description into a paused, ready-to-review lemlist campaign with sourced leads and a seniority-routed sequence.
Lemlist Campaign From Icp fits situations like: the user says create a lemlist campaign for X; build a lemlist campaign from this ICP; spin a lemlist campaign for Y; natural language to lemlist campaign.
Run `npx skills add Othmane-Khadri/YALC-the-GTM-operating-system --skill lemlist-campaign-from-icp -a claude-code`. Or copy the skill folder (.claude/skills/lemlist-campaign-from-icp in Othmane-Khadri/YALC-the-GTM-operating-system) into .claude/skills/lemlist-campaign-from-icp in your project. Claude Code loads it when a task matches its description.
Run `npx skills add Othmane-Khadri/YALC-the-GTM-operating-system --skill lemlist-campaign-from-icp -a codex`. Or copy the skill folder (.claude/skills/lemlist-campaign-from-icp in Othmane-Khadri/YALC-the-GTM-operating-system) into .agents/skills/lemlist-campaign-from-icp 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 Othmane-Khadri/YALC-the-GTM-operating-system --skill lemlist-campaign-from-icp -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/lemlist-campaign-from-icp, .gemini/skills/lemlist-campaign-from-icp, .github/skills/lemlist-campaign-from-icp and .opencode/skills/lemlist-campaign-from-icp in your project.
Going by SKILL.md and its folder, Lemlist Campaign From Icp needs the command-line tools its instructions call (claude) and credentials named LEMLIST_API_KEY. Our summary lists: A credential in LEMLIST_API_KEY.
SKILL.md names 4 domains. As links in the text: github.com, lemlist.com, get.lemlist.com and yalc.ai. 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.
Lemlist Campaign From Icp is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 6.5k tokens (SKILL.md is roughly 26k 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 Lemlist Campaign From Icp: Roam Evidence Hardening (Cranot/roam-code, 517 stars), Qwen Code E2E Testing (QwenLM/qwen-code, 28k stars), Edt MCP Autopilot (DitriXNew/EDT-MCP, 296 stars) and Cloud API Recipe Authoring (TencentCloudBase/CloudBase-AI-Toolkit, 1.1k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
Othmane-Khadri (a GitHub user) maintains it in Othmane-Khadri/YALC-the-GTM-operating-system, which has 318 GitHub stars. The repository holds 57 skills in this directory. The repository was last updated on August 20, 2026.
Source: Othmane-Khadri/YALC-the-GTM-operating-system on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.