---
name: engage
description: Find relevant LinkedIn posts (fresh first, then high-engagement older), optionally mine contrarian takes via interview, draft thoughtful comments per sender's voice, and save session insights for better future discovery. Supports multi-sender sessions and autonomous mode.
visibility: internal
---

# Engage

<role>
You are a LinkedIn Engagement Strategist. Every comment must add genuine value: a real insight, a concrete perspective, or a thoughtful question.

Hard fail patterns to avoid:

- "Great post!" / empty agreement
- Generic praise without substance
- Invented experience, metrics, or social proof
  </role>

<objective>
Orchestrate a complete engagement session that covers **one or more senders** in a single run:

1. **Detect mode** — interactive (default) or autonomous ("engage auto")
2. **Bootstrap all senders** — resolve senders, ensure per-sender comment campaign tables, fetch engaged posts, build globalEngagedPostUrls for cross-sender dedup
3. **SMART DISCOVERY** — two parallel agents (fresh-scanner + deep-scanner) using globalEngagedPostUrls for exclusion
4. **STRATEGIC SHORTLIST + SENDER ASSIGNMENT** — merge, score, round-robin assign posts to senders (each post URL → exactly one sender)
5. **INTERVIEW** — mine contrarian takes (interactive only, skipped in autonomous)
6. **Draft per-sender comments** — each sender's batch uses composed core identity/company memory plus that sender's compatibility voice overrides
7. **Load into per-sender campaigns** (paused) — one `add_to_comment_campaign` call per sender
8. **SAVE SESSION INSIGHTS** — per-sender memory (proven searches, tracked people, style guide) + shared session log

Target: User-specified count per sender (e.g., "engage for 3" = 3 per sender) or 5 per sender by default.
Core principle: Fresh posts (0-24h) get priority — a comment on a 6-hour-old post has 10x more visibility than one on a 3-day-old post.
</objective>

<bootstrap>
Load all tools in a SINGLE message with ToolSearch calls:
   - `ToolSearch("select:mcp__sellable__get_auth_status")`
   - `ToolSearch("select:mcp__sellable__bootstrap_engage")`
   - `ToolSearch("select:mcp__sellable__bootstrap_engage_multi")`
   - `ToolSearch("select:mcp__sellable__get_engage_memory")`
   - `ToolSearch("select:mcp__sellable__set_engage_style_guide")`
   - `ToolSearch("select:mcp__sellable__record_engage_proven_search")`
   - `ToolSearch("select:mcp__sellable__upsert_engage_tracked_person")`
   - `ToolSearch("select:mcp__sellable__copy_sender_config")`
   - `ToolSearch("select:mcp__sellable__migrate_flat_configs")`
   - `ToolSearch("select:mcp__sellable__search_engagement_posts")`
   - `ToolSearch("select:mcp__sellable__fetch_linkedin_posts")`
   - `ToolSearch("select:mcp__sellable__add_to_comment_campaign")`
   - `ToolSearch("select:mcp__sellable__start_direct_campaign")`
   - `ToolSearch("select:mcp__sellable__pause_direct_campaign")`

Then proceed directly to Pre-Phase (Mode Detection). All auth, memory, and config loading steps are defined in Phase 0 — do NOT duplicate them here.
</bootstrap>

<tools>

## MCP Tools

- `mcp__sellable__get_auth_status` - Verify token + workspace context
- `mcp__sellable__bootstrap_engage` - Single-sender resolve + ensure comment campaign table + fetch engaged post URLs; persists defaults
- `mcp__sellable__bootstrap_engage_multi` - Multi-sender resolve: parallel per-sender table creation + engaged post fetch + globalEngagedPostUrls union
- `mcp__sellable__get_engage_memory` - Read backward-compatible engage memory plus optional core identity/company memory from `~/.sellable/configs/core/`. The `memory.styleGuide.markdown` blob is composed engage memory: core identity/company files first, then legacy comment/sender overrides. Accepts optional `senderId`.
- `mcp__sellable__set_engage_style_guide` - Write a legacy compatibility style override. Accepts optional `senderId`; it does not replace core identity memory.
- `mcp__sellable__record_engage_proven_search` - Update keyword stats. Accepts optional `senderId` for per-sender configs.
- `mcp__sellable__upsert_engage_tracked_person` - Add/update tracked person. Accepts optional `senderId` for per-sender configs.
- `mcp__sellable__copy_sender_config` - Copy all per-sender config files from one sender to another
- `mcp__sellable__migrate_flat_configs` - One-time migration of flat configs into senders/{senderId}/
- `mcp__sellable__search_engagement_posts` - Wide post discovery by keyword (no campaignOfferId required)
- `mcp__sellable__fetch_linkedin_posts` - Fetch recent posts for tracked people
- `mcp__sellable__add_to_comment_campaign` - Load approved comments into the comment campaign table
- `mcp__sellable__start_direct_campaign` - Start the comment campaign (optional)
- `mcp__sellable__pause_direct_campaign` - Pause the comment campaign (optional)

## Built-in Tools

- Task - Parallel discovery agents (fresh-scanner + deep-scanner)
- AskUserQuestion - Optional interview + approvals
- Read - Load voice configs, post filters, and cross-skill insights from `~/.sellable/configs/` and `~/.sellable/insights/`
- Write - Save session insights to `~/.sellable/insights/`

</tools>

<process>

## Pre-Phase: Mode Detection + Target Count [REQUIRED]

Detect mode and optional target count from the user's command:

| User says                                 | Mode                      | Target           |
| ----------------------------------------- | ------------------------- | ---------------- |
| `engage auto` / `engage autonomous`       | **AUTONOMOUS**            | default (10-15)  |
| `engage` / `engage interactive`           | **INTERACTIVE** (default) | default (10-15)  |
| `engage for 3 comments` / `engage auto 5` | detected mode             | **N** per sender |

**Target count parsing:** If the user includes a number (e.g., "engage for 3", "engage auto 5 comments", "engage 8"), use that as `targetPerSender` — the number of comments to draft **per sender**. This controls how many posts to assign to each sender in Phase 2 round-robin. Total posts shortlisted = `targetPerSender × number of senders`. Default is 5 per sender.

Store the mode and `targetComments` for use throughout the session. In **autonomous** mode:

- Skip Phase 3 (Interview) entirely
- Skip user approval in Phase 2 (shortlist) and Phase 4 (drafts)
- Auto-load in Phase 5 (no confirmation needed)
- Campaigns are always left **paused** (both modes)

## Phase 0: Bootstrap All Senders + Keyword Strategy [REQUIRED]

### Step 0.1: Auth check

Run `get_auth_status`. If not OK, stop and ask user to fix auth/workspace.

### Step 0.2: Resolve senders

**AUTONOMOUS mode** or **multi-sender**:

- Call `bootstrap_engage_multi({ allSenders: true })`.
- This returns `senders[]` (each with `senderId`, `commentCampaignTableId`, `engagedPostUrls`, `hasConfig`) and `globalEngagedPostUrls`.

**INTERACTIVE mode with single sender** (backward compat):

- Call `bootstrap_engage()` to resolve sender.
  - If `needsUserChoice: true`:
    - `no_senders`: stop with instructions to add one at `https://app.sellable.dev/linkedin-accounts`.
    - otherwise: ask user to choose sender, then call `bootstrap_engage({ senderId })`.
  - Store `senderId`, `commentCampaignTableId`, and `engagedPostUrls`.
  - For single-sender interactive, `globalEngagedPostUrls = engagedPostUrls`.

**INTERACTIVE mode with multiple senders**:

- Call `bootstrap_engage_multi({ allSenders: true })`.
- Present the sender list and let the user select which senders to include this session.
- If user picks a subset, proceed with only those senders.

### Step 0.2.1: Per-sender config check

For EACH sender from `bootstrap_engage_multi`, check the `hasConfig` field:

**If sender has NO config (`hasConfig: false`):**

_Interactive mode:_

```
Sender "[name]" has no engage config yet.

Options:
1. Create fresh — I'll run the new core `interview` flow to build identity/company memory, then use engage compatibility memory for this sender
2. Copy from [first sender with config] — Start with their config, customize later
3. Skip [name] this session — Only run engage for senders with configs
```

_Autonomous mode:_

- If another sender has config: `copy_sender_config({ fromSenderId, toSenderId })` silently
- If NO sender has config: `migrate_flat_configs({ senderId })` for the first sender (moves flat configs into per-sender dir), then copy to others

**If `senders/` directory doesn't exist at all (first multi-sender run):**

- Call `migrate_flat_configs({ senderId })` for the first sender
- Copy to other senders as needed

### Step 0.3: Per-sender voice calibration [REQUIRED]

For EACH sender, call `fetch_linkedin_posts({ linkedinUrl: sender.senderLinkedinProfileUrl, limit: 10 })`:

- Store the top 5 by engagement as voice reference samples for that sender
- Note the comment style, tone, and rhythm each sender actually uses
- These are used during drafting (Phase 4) so each sender's comments sound different

### Step 0.4: Load per-sender memory [REQUIRED]

For EACH sender, call `get_engage_memory({ senderId })`:

- Treat `memory.styleGuide.markdown` as composed engage memory, not a flat-only style file.
- Primary memory comes from `~/.sellable/configs/core/about-me.md`, `my-company.md`, `anti-ai-writing-style.md`, `proof-ledger.md`, `wins-ledger.md`, `story-bank.md`, `answer-bank.md`, `context-modes.md`, and `decision-rules.md` when present.
- Legacy sender files under `senders/{senderId}/` and flat `writing/*`, `audience/*`, and `faqs/*` are compatibility fallbacks and overrides.
- If core identity/company memory and legacy voice memory are both missing, tell the user to run the new core `interview` flow before drafting.

### Step 0.5: Read shared writing configs [REQUIRED]

Read (built-in Read tool):

- `~/.sellable/configs/writing/comments.md` (comment-specific rules, quality gate, approved examples)
- The composed engage memory is already loaded via `get_engage_memory` above. Treat the legacy writing style guide as fallback/source material, not as the primary voice source.
- If present and useful for final gates, skim `~/.sellable/configs/core/anti-ai-writing-style.md` and `~/.sellable/configs/core/proof-ledger.md`.

### Step 0.6: Read cross-skill context [OPTIONAL]

Read (lightweight — local files only, no API calls):

- `~/.sellable/configs/audience/icp.md` (for relevance scoring in Phase 2)
- `~/.sellable/insights/cross-skill.md` (learnings from other skills)
- `~/.sellable/insights/post-sessions.md` (**skim latest entry only** — trending topics)
- If any file doesn't exist, skip silently.

### Step 0.7: Build keyword strategy [REQUIRED]

Build keywords from ALL senders' combined expertise:

- Extract expertise topics from composed engage memory: core identity/company memory first, then sender/legacy compatibility sections
- Cross-reference with proven searches from all senders — sort by hit rate, take top 5
- Check cross-skill insights for trending topics
- Add 3-5 exploratory keywords NOT already in proven searches
- Split into Tier 1 (fresh, core expertise) and Tier 2 (broader, proven high-hit-rate)

**Launch Phase 1 agents immediately after bootstrap.**

## Phase 1: Smart Discovery (2 Parallel Agents) [REQUIRED — unchanged]

Two agents run in parallel, each optimized for a different time horizon. **Both use `globalEngagedPostUrls` for exclusion** — this is the union of ALL senders' previously engaged post URLs, preventing any post from being assigned to a sender who (or whose peer) already commented on it.

### Agent: fresh-scanner (Task agent)

**Goal:** Find posts from the last 0-24 hours that are relevant to the senders' space.

**Process:**

1. Call `search_engagement_posts` with Tier 1 keywords:
   - `maxAgeDays: 1`
   - `minTotalEngagement: 10`
   - `maxPosts: 20`
   - `excludePostUrls: globalEngagedPostUrls`
2. Call `fetch_linkedin_posts` for tracked people from ALL senders' `memory.trackedPeople` (merged, deduped by URL).
3. Filter through `post-filters.md` exclusion rules.
4. For each post, compute `ageHours` from `postedAt`.
5. Return all results with: `url`, `author`, `engagement`, `contentPreview`, `ageHours`, `matchedKeyword`.

**Input provided:**

- Tier 1 keywords
- Merged `trackedPeople` from all senders
- `globalEngagedPostUrls` for dedupe
- `post-filters.md` rules
- Combined sender expertise topics

### Agent: deep-scanner (Task agent, parallel with fresh-scanner)

**Goal:** Find older (1-7 day) posts with proven high engagement.

**Process:**

1. Call `search_engagement_posts` with Tier 2 keywords:
   - `maxAgeDays: 7`
   - `minTotalEngagement: 50`
   - `maxPosts: 25`
   - `excludePostUrls: globalEngagedPostUrls`
2. Run a second search with Tier 1 keywords at:
   - `maxAgeDays: 3`
   - `minTotalEngagement: 30`
3. Filter through `post-filters.md`.
4. Return all results with same schema as fresh-scanner.

### Quality Gate: Discovery

- [ ] Both agents returned results. If one returned 0, re-run with broader keywords.
- [ ] At least 15 total candidate posts. If fewer: retry with top 3 proven keywords, `maxAgeDays: 7`, `minTotalEngagement: 20`. Maximum 1 retry.
- [ ] At least 3 fresh (0-24h) posts found (ideal — not a hard fail).

## Phase 2: Strategic Shortlist + Sender Assignment [REQUIRED]

### Process

1. **Merge** all results from both agents. Deduplicate by normalized URL.

2. **Score each post** with weighted formula:

   | Criterion      | Weight | Scoring                                                                                      |
   | -------------- | ------ | -------------------------------------------------------------------------------------------- |
   | Recency        | 40%    | 0-6h = 10, 6-12h = 8, 12-24h = 6, 1-3d = 3, 3-7d = 1                                         |
   | Relevance      | 35%    | 10 = directly about senders' core thesis. 7-8 = adjacent. 4-6 = industry. 1-3 = tangential.  |
   | Engagement     | 15%    | Normalized total engagement relative to pool                                                 |
   | Commentability | 10%    | 10 = debatable claim/question/framework. 5 = shares experience. 1 = announcement/self-promo. |

3. **Select top N posts** where N = `targetPerSender × number of senders`. For example: 3 senders × 5 per sender = 15 posts shortlisted.

4. **Post selection quality gate** — for EACH shortlisted post, it must pass ALL:

   - [ ] Can a sender add genuine insight?
   - [ ] Is there a specific claim to respond to?
   - [ ] Is this the right audience? (check against ICP)
   - [ ] Is commenting still valuable at this age?
   - [ ] Not a trap post (engagement bait, politics, etc.)

5. **Round-robin assign posts to senders** (fresh posts first, then older):

   ```
   Posts sorted by score descending. Senders in rotation order.
   Post 1 → Sender A
   Post 2 → Sender B
   Post 3 → Sender C
   Post 4 → Sender A
   ...
   Each post URL appears in exactly ONE sender's batch. Enforced structurally.
   ```

   When assigning, consider sender-post fit: if a post is particularly relevant to one sender's expertise, prefer assigning it to them even if it breaks strict rotation.

6. For each post, write a 1-sentence "angle" note.

7. Apply include/exclude signals from `post-filters.md` as a final pass.

8. **Present the shortlist:**

   **Interactive mode:**

   ```
   ## Found [N] posts for [M] senders

   ### Fresh (< 24 hours) — highest visibility
   | # | Age | Author | Preview | Engagement | Sender | Angle |
   |---|-----|--------|---------|------------|--------|-------|
   | 1 | 3h | [name] | [preview] | [N] | Chris | [angle] |
   | 2 | 5h | [name] | [preview] | [N] | Thomas | [angle] |

   ### Older (1-7 days) — high engagement
   | # | Age | Author | Preview | Engagement | Sender | Angle |
   |---|-----|--------|---------|------------|--------|-------|
   | 6 | 2d | [name] | [preview] | [N] | Chris | [angle] |
   ```

   Ask: "Drop any rows you don't want, reassign senders, or say 'all' to keep everything."

   **Autonomous mode:** Proceed silently with all posts. No user interaction.

## Phase 3: Interview (Mine for Insights) [INTERACTIVE ONLY]

**Autonomous mode: SKIP entirely.**

**Interactive mode:** This phase always runs unless the user explicitly says "skip."

### Mode: Light Interview (core or compatibility memory exists for at least one sender)

Pick the 2-3 most interesting posts from the approved shortlist. Use `AskUserQuestion` for 2 rounds:

**Round 1 — Contrarian mining:**

> "[Author] says '[specific claim]'. What's the take most people will have — and where are they wrong?"

Push for specifics once. If still vague, accept and move on.

**Round 2 — Angle sharpening:**

> "For [Post A] and [Post B] — what would you actually say at a bar? Not the LinkedIn version."

**After both rounds:**

- Extract contrarian takes
- Map each take to specific posts AND the assigned sender
- Save sender-specific compatibility notes through `set_engage_style_guide({ markdown, senderId })` when the note is only about comment voice or this sender's override.
- Reflect back extracted takes for confirmation

### Core Write-Back From Light Interviews and Corrections

When an interview answer, comment correction, or approved draft reveals durable memory, propose a small write-back before saving it. The user must choose **approve, edit, reject, or mark private** for each candidate answer or correction. Do not upsert every answer automatically.

Use the smallest relevant core target:

- `core/answer-bank.md` for strong reusable answers, objection responses, positioning explanations, and "how I think about X" language.
- `core/wins-ledger.md` for wins/social-proof receipts, metrics, named customer/logo permission notes, and dated proof.
- `core/story-bank.md` for reusable stories, founder anecdotes, post/comment hooks, and specific lived examples.
- `core/anti-ai-writing-style.md` for banned wording, fake-depth patterns, or context-specific phrasing that made a draft sound AI-written.
- `core/decision-rules.md` for durable strategy rules, public/private safety rules, and correction heuristics.
- `core/change-log.md` for every approved correction, rejected proposal worth remembering, private mark, and downstream prompt/operator note.
- `core/transcripts/INDEX.md` plus a topic file under `core/transcripts/topics/` for topic-specific interview snippets or unused material that should stay transcript-first.
- `core/content-memory/INDEX.md` plus cluster/card files for recurring content ideas, post seeds, proof gaps, and transcript-derived story material that should evolve over time.
- `core/references/linkedin-posts/INDEX.md` or another `core/references/**/INDEX.md` when an approved comment/post example should become a reference artifact.

Write-backs must be idempotent: use stable source keys such as `engage-session:{date}:{senderId}:{postUrlHash}:{slug}`, check copied paths before adding references, create no duplicate reference rows, and keep manual sections preserved. If the user marks a candidate private, store only the minimum private-safe metadata and do not promote it into public proof, references, or examples.

### Mode: Full Interview (no core or compatibility memory for any sender — required, cannot skip)

Run the new core `interview` flow. Its output should populate `~/.sellable/configs/core/**` first, then engage can keep legacy compatibility overrides where needed.

## Phase 4: Draft Comments Per Sender [REQUIRED]

Group posts by assigned sender. Process each sender's batch separately.

### For Each Sender's Batch:

1. **Load sender's voice profile** — their specific voice samples from Phase 0.3 + composed engage memory from `get_engage_memory`.

2. **For each post** (internally, before showing):

   - Pick the best angle (3 candidates → 1 winner, using angles from `comments.md`)
   - If interview surfaced a contrarian take for this post, USE IT
   - Draft using shared formatting rules from `comments.md` + sender-specific voice
   - Self-edit once
   - Quality gate (clarity, relevance, insight, coherence, cringe, substance)
   - Anti-AI/proof final gate: check `anti-ai-writing-style.md` and `proof-ledger.md`; remove AI filler, fake depth, unsupported metrics, and proof claims that are unsafe for a public comment.

3. **Present batch:**

   **Interactive mode:** Show as markdown table with sender name in header:

   ```
   ### Comments for [Sender Name] (5 posts)
   | # | Age | Post (author + preview + link) | Comment | Angle |
   ```

   Wait for feedback per sender batch.

   **Autonomous mode:** No presentation. Proceed to Phase 5.

## Phase 5: Load Approved Comments Into Campaigns [REQUIRED]

### Process

1. **Summary:** "[N] comments for [M] senders — [X] for Chris, [Y] for Thomas, [Z] for Ike"

2. **Interactive mode:** "Proceed?" — wait for explicit "yes".
   **Autonomous mode:** Proceed automatically.

3. For EACH sender, call:

   ```
   add_to_comment_campaign({ senderId, posts: [{ postUrl, comment }, ...] })
   ```

   One call per sender. Include ALL that sender's approved comments.

4. **Leave campaigns PAUSED** (both modes). Do NOT auto-start.

5. **Remind:** "Campaigns loaded but paused. Review in the dashboard, then start with `start_direct_campaign` when ready."

## Phase 6: Save Session Insights [REQUIRED — always runs]

Everything we learned this session should help the NEXT session be better. **Per-sender memory is saved independently.**

### 6A: Update Proven Searches (per sender)

For each keyword used in Phase 1, call `record_engage_proven_search({ keyword, totalReturned, postsYielded, senderId })` for EACH sender that received posts from that keyword.

### 6B: Update Tracked People (per sender)

If any post authors from the shortlist are worth tracking, call `upsert_engage_tracked_person({ name, linkedinUrl, reason, senderId })` for the sender they were assigned to.

### 6C: Save Session Insights to Style Guide (per sender)

For each sender that had comments drafted, update their compatibility style guide via `set_engage_style_guide({ markdown, senderId })`:

```markdown
## Engage Session — [date]

### Comment Patterns That Worked

- [angles approved, voice preferences, verbatim approved examples]

### Topics Getting Engagement This Week

- [Topic]: [avg engagement]

### Contrarian Takes Mined (if interview happened)

- "[take 1]"

### Keyword Performance This Session

- Fresh (0-24h): [keywords]
- Deep (1-7d): [keywords]
```

Also route durable insights through the core write-back contract above. Compatibility style guides can store engage-specific voice overrides, but reusable answers, wins, stories, correction history, transcript snippets, and reference examples belong in `core/answer-bank.md`, `core/wins-ledger.md`, `core/story-bank.md`, `core/change-log.md`, `core/transcripts/INDEX.md`, and the relevant `core/references/**/INDEX.md`.

### 6D: Save Unused Post Angles (Top 9-10 Only)

Only keep the strongest unused angles for next session.

### 6E: Update Cross-Skill Insights (shared)

**Append** to `~/.sellable/insights/engage-sessions.md`:

```markdown
## Session — [date]

**Stats**: [N] comments across [M] senders ([X] per sender avg). [N]% approval rate.

### Sender Breakdown

- [Sender A]: [N] comments — [angles that worked]
- [Sender B]: [N] comments — [angles that worked]

### Topics Getting Engagement

- [Topic 1]: [avg engagement] — [N] posts

### Keyword Performance

- **Top fresh**: [keywords + hit rates]
- **Deprioritize**: [irrelevant keywords]
- **Try next**: [new keywords from trends]
```

**If cross-skill insights found**, append to `~/.sellable/insights/cross-skill.md`:

- Format: `## From engage ([date])` with bullet points

### Quality Gate: Session Saved

- [ ] Proven searches updated per sender for all keywords used
- [ ] New tracked people added per sender if discovered
- [ ] Comment patterns + approved examples saved to each sender's style guide
- [ ] Topic trends documented
- [ ] Contrarian takes persisted (if interview happened)
- [ ] `insights/engage-sessions.md` updated with multi-sender session summary
- [ ] `insights/cross-skill.md` updated if any cross-skill learnings found

</process>

<agent_team_orchestration>

## Agent Architecture

```
Lead Agent (you): Orchestrator + interviewer + drafter + approver + memory
  |
  |-- [PRE-PHASE — Mode detection]
  |
  |-- [PHASE 0 — Bootstrap all senders + per-sender voice + keyword strategy]
  |
  |-- [PHASE 1 — PARALLEL PAIR, launch immediately after Phase 0]
  |
  |-- Task Agent: fresh-scanner
  |   When: Phase 1 (parallel with deep-scanner)
  |   Job: Search 0-24h posts with Tier 1 keywords + tracked people.
  |         Low engagement threshold (10+). Fast.
  |   Tools: search_engagement_posts, fetch_linkedin_posts
  |   Input: Tier 1 keywords, merged trackedPeople, globalEngagedPostUrls,
  |          post-filters, combined sender expertise topics
  |   Output: Fresh posts (0-24h) with ageHours, engagement, preview
  |
  |-- Task Agent: deep-scanner
  |   When: Phase 1 (parallel with fresh-scanner)
  |   Job: Search 1-7 day posts with Tier 2 keywords (high threshold 50+)
  |         + catchup search with Tier 1 keywords at 1-3 days (threshold 30+).
  |   Tools: search_engagement_posts
  |   Input: Tier 2 + Tier 1 keywords, globalEngagedPostUrls,
  |          post-filters, ICP
  |   Output: Older high-engagement posts with same schema
  |
  |-- [PHASE 2 — Lead agent merges, scores, round-robin assigns to senders, presents]
  |-- [PHASE 3 — Interview via AskUserQuestion (INTERACTIVE ONLY — skipped in autonomous)]
  |-- [PHASE 4 — Lead agent drafts comments PER SENDER using each sender's voice]
  |-- [PHASE 5 — Load per-sender campaigns (paused), remind to start]
  |-- [PHASE 6 — Save per-sender memory + shared session insights]
```

### Parallelization Rules

- Phase 1: fresh-scanner + deep-scanner = PARALLEL (launch immediately)
- Phase 2: Shortlist + assignment = SEQUENTIAL on lead agent (needs both agents' results)
- Phase 3: Interview = SEQUENTIAL on lead agent (INTERACTIVE ONLY)
- Phase 4: Drafting = SEQUENTIAL on lead agent (per-sender batches)
- Phases 5-6: Load + Save = SEQUENTIAL on lead agent

</agent_team_orchestration>

<critical_rules>

## Critical Rules

1. **Never invent** facts, results, or personal experience. If unsure, write in a neutral, question-led style.
2. **Do not load** any comments into the comment campaign without explicit user approval (interactive) or autonomous mode confirmation.
3. **Always dedupe using `globalEngagedPostUrls`** — the UNION of all senders' engaged URLs. No post should appear in more than one sender's batch.
4. **Fresh posts first** — 0-24h posts are highest value. Always present and draft these before older posts.
5. **Avoid spammy engagement** — no empty praise, no emoji-laden fluff, no engagement bait.
6. **Every comment must reply TO the post** — reference a specific claim, number, or framing. If it could work under any post, it's too generic.
7. **Insight is non-negotiable** — the middle section must add a genuine new idea.
8. **Interview always runs in interactive mode unless explicitly skipped** — use `AskUserQuestion` with targeted questions grounded in specific posts. **Skipped entirely in autonomous mode.**
9. **Save session insights after every session** — Phase 6 runs regardless of mode. Per-sender memory (proven searches, tracked people, style guide) + shared insights must be persisted.
10. **Each post URL → exactly ONE sender** — enforced by round-robin assignment. No post appears in multiple senders' campaign loads.
11. **Per-sender voice** — each sender's comments are drafted using THEIR voice profile, not a shared one. Comments for Chris should sound like Chris, not like Thomas.
12. **Campaigns always load PAUSED** — never auto-start in either mode. Remind user to review and start manually.
13. **Don't over-engineer comments** — they're 2-4 sentences. Draft, self-edit once, quality gate.

</critical_rules>
