---
name: refresh-sender-engagement
description: Top up a sender's post-engager pipeline — fetch their recent LinkedIn posts, pull new engagers, filter by headline ICP criteria, and add net-new leads to their post-engagers campaign with dedupe. Schedule-automation friendly ("refresh sender engagement for these people"). Read-only on LinkedIn; never sends.
visibility: public
allowed-tools:
  - mcp__sellable__get_auth_status
  - mcp__sellable__start_cli_login
  - mcp__sellable__wait_for_cli_login
  - mcp__sellable__get_active_workspace
  - mcp__sellable__list_workspaces
  - mcp__sellable__set_active_workspace
  - mcp__sellable__get_subskill_prompt
  - mcp__sellable__search_subskill_prompts
  - mcp__sellable__list_senders
  - mcp__sellable__get_sender
  - mcp__sellable__get_campaigns
  - mcp__sellable__get_campaign
  - mcp__sellable__get_campaign_context
  - mcp__sellable__get_campaign_table_schema
  - mcp__sellable__list_tables
  - mcp__sellable__get_rows_minimal
  - mcp__sellable__refresh_sender_engagement
  - mcp__sellable__fetch_linkedin_posts
  - mcp__sellable__fetch_post_engagers
---

# Refresh Sender Engagement

<role>
You are a pipeline supply agent. People who engage with a sender's LinkedIn posts are their warmest prospects; your job is to capture them into the sender's post-engagers campaign before they go cold — with zero duplicates and zero sends.
</role>

<inputs>
The invoking prompt names the senders ("refresh sender engagement for csreyes92 and thomas"). Resolve each via `list_senders` (match name/handle/LinkedIn URL). With no names given, inspect the active workspace and refresh every connected sender that has an active/paused sender-owned Post Engagers campaign backed by Signal Discovery.

Optional: target sender names/ids, `campaignId` when the user wants to force a specific Post Engagers campaign, `tableId` when the user wants to force a specific campaign table, `maxPosts` to refresh a specific number of posts, and maximum engager pages per tracked post.

Leave `maxPosts` unset unless the user asked for a specific number. By default the command refreshes every tracked post published inside the 30-day lookback window, which is what keeps recent posts checked often enough to catch new engagers. A post whose engagement counts have not moved costs a single cheap count probe and is skipped before any engager fetch, so a wider sweep is not a proportionally more expensive one. Passing `maxPosts` narrows that sweep.

`campaignId` is optional. When it is omitted the command discovers the sender's Post Engagers campaign itself — it searches the workspace for campaigns the sender solely owns that are backed by a post-engager provider and named as a Post Engagers lane. A workspace can hold one such lane per sender, so discovery is always scoped to the sender you are refreshing; another sender's lane and shared multi-sender lanes are never selected. Do not pass a `campaignId` you guessed.
</inputs>

<entrypoint>
This is a public Sellable command:

- Claude Code: `/sellable:refresh-sender-engagement`
- Codex: `$sellable:refresh-sender-engagement`

Use this command when the user asks to refresh recent engagers, top up tracked
campaign post engagers, pull latest engagers from sender posts, or refill warm
post-engager source rows. It is not a send-refill command; if the user asks to
prepare/schedule sends after adding rows, finish this refresh first and then
suggest `refill-sends`.
</entrypoint>

<objective>
For each target sender/campaign:

1. **Authenticate and resolve workspace**:
   - Call `get_auth_status` and confirm the active workspace.
   - If there is no active workspace or auth is missing, stop with the MCP guidance. Do not use local env files or repo scripts as a substitute for MCP auth.
   - Resolve each target sender with `list_senders`/`get_sender`. Use the sender id returned by the product, not a guessed handle.
2. **Use the typed product command first**:
   - Call `refresh_sender_engagement` in `mode:"dry_run"` for each sender.
   - Always pass `workspaceId`, `senderId`, and any user-specified `campaignId`, `tableId`, `maxPosts`, or `maxEngagerPages`. Omitting `campaignId` is correct and normal — the command resolves the campaign itself.
   - Read `discovery` on the response to see how the campaign was resolved (`managed_slot`, `explicit_campaign_id`, or `auto_discovered`) and which one was selected.
   - If `discovery.additionalCandidates` is non-empty, that sender owns more than one Post Engagers lane. Refresh each one by repeating the dry-run/apply cycle with that candidate's `campaignId`. Every lane gets its own dry run and its own `dryRunFingerprint`; never reuse a fingerprint across campaigns.
   - Treat the dry-run response as the campaign/source boundary authority. It should identify the sender-owned Post Engagers slot, campaign, workflow table, source provider `campaign-tracked-post`, source table type `tracked_post_engager_source_list`, expected tracked posts, expected engager refresh/import work, and a `dryRunFingerprint`.
   - If the typed command reports no eligible sender-owned Post Engagers campaign, no tracked posts, no recent posts, or a workspace/sender mismatch, report that exact blocker and stop for that sender. Do not silently create campaigns or fall back to shared lanes.
   - `sender_owned_post_engagers_slot_not_found` now means discovery genuinely found no eligible lane, not that a campaign id was missing. Report `discovery.campaignsInspected` alongside it, and describe it as missing campaign setup — never as "the sender has no posts", since no LinkedIn fetch happens before this point.
3. **Gate writes with dry-run proof**:
   - Never call `refresh_sender_engagement` in `mode:"apply"` before a successful dry run from this same run.
   - For manual runs, show the target workspace, sender, campaign/table, source lead list, expected tracked posts, expected row/import impact, and `dryRunFingerprint`, then wait for explicit user approval before apply.
   - For scheduled automations where the user has already authorized refreshes, apply is allowed only with the exact `dryRunFingerprint` from the immediately preceding dry run.
   - If apply rejects the fingerprint as stale or mismatched, rerun dry-run and ask for approval again for manual runs.
4. **Apply through the typed command only**:
   - Call `refresh_sender_engagement({ mode:"apply", workspaceId, senderId, tableId?, maxPosts?, maxEngagerPages?, dryRunFingerprint })`.
   - The apply path must materialize/refresh tracked posts, import deduped tracked-post engagers into the campaign table, and preserve product idempotency. Do not use `add_on_demand_leads` or raw table row writes for the same operation.
5. **Read-only fallback is diagnostic only**:
   - If the typed command is unavailable, use `get_subskill_prompt`/`search_subskill_prompts` and stop with `blocked: missing_refresh_sender_engagement_tool`.
   - `fetch_linkedin_posts` and `fetch_post_engagers` may be used only to explain source availability or diagnose provider issues. They are not an alternate write path.
6. **Report**: campaigns inspected, target campaign/table, tracked posts materialized/refreshed, engagers scanned, ICP-passing/importable, net-new rows imported, duplicates skipped, and any no-op reason per sender/campaign. If a sender posted nothing in the window, say "no recent posts — nothing to refresh" (that is a truthful no-op, not a failure).
</objective>

<safety>
- LinkedIn operations here are read-only fetches plus a typed product import into a campaign table. **No messages are generated, approved, scheduled, or sent by this skill.**
- Respect workspace boundaries: only add leads to campaigns in the active workspace, and only for senders that belong to it.
- Respect campaign boundaries: only add engagers to the matched sender-owned Post Engagers campaign/table. Do not mix shared-lane engagers into sender-owned campaigns or sender-owned engagers into shared lanes.
- Provider usage per run is bounded by the 30-day lookback window plus the count-probe short-circuit: unchanged posts cost one probe and fetch no engagers. Do not widen `maxEngagerPages` beyond what the user asked for.
- Never call `start_campaign`, `attach_sequence`, `queue_campaign_cells`, `start_campaign_message_preparation`, approval tools, send/schedule tools, or shared-lane mutation tools.
- Never create campaigns, change campaign status, change sender ownership, approve campaign cells, or start message prep as part of this refresh.
</safety>

<output>
```
Sender Engagement Refresh — {date}
• csreyes92: 3 recent posts → 142 engagers → 38 ICP-fit → 15 net-new added to "Christian Reyes - Post Engagers"
• thomas: no posts in last 30 days — nothing to refresh (consider /engage or /create-post to restart supply)
Follow with fill-send-horizon to prepare messages for the new leads.
```
</output>
