SKILL.md
Language Markdown10.6 KB186 lines
---name: partner-pipeline-pushdescription: >-Push a cohort of Meridian Display Co. clients from the quarterly-outreach vault(03-Clients/client-stubs) into a Bitrix deal funnel and post each client's enrichment profile as acomment on their deal. Use when Matt says "push these clients into the pipeline", "load thiscohort into Partner Tier Upgrade" (or its old name, "Partner Outreach"), "create deals for thesegraduates", "push the [tier/segment] clients into Bitrix", "add them to the partner pipeline","compile the CRM mapping and push them", or "post the enrichment comments on those deals", orhands over a cohort (e.g. Retail -> Palladium Partner graduates) and wants them created as deals,linked to their contact, tagged with User Group, and annotated with their vault research. One dealper client; deal name = first_name + " " + last_name. Do NOT use for drafting outreach emails(matt-guerra-email) or for enriching the stubs themselves (client-enrich).---# partner-pipeline-pushTurns a selected cohort of client stubs into Bitrix deals, wires up the client +tier fields, and drops each client's research into the deal as one clean comment.End to end: **cohort -> cohort mapping -> create deals -> link contact + User Group-> (locate stubs by stage) -> post enrichment comment.**This is a **live CRM write** workflow. Confirm scope and cost-of-mistake beforemass writes, and always verify with a single test write before batching.> Verified against live Bitrix 2026-08-12. Names and enum values below were wrong> in earlier versions of this skill and made it fail outright. If anything here> disagrees with `list_pipelines` / `find_field`, trust the live call and fix this file.## Prerequisites1. Read `03-Clients/CLAUDE-clients.md` and root `MEMORY.md`.2. **The target Bitrix funnel must be write-enabled** in the "Bitrix24 CRM - MeridianDisplay" connector. Key the allowlist on **categoryId 24**, not the displayname. The funnel was renamed from "Partner Outreach" to**"Partner Tier Upgrade"** on 2026-08-06, and the stale name sitting inthe allowlist silently broke **every deal write in every funnel** for five days.If a write returns `Configured write funnel '...' not found`, that is this bug.**Editing the config is not enough: the app must be fully restarted**, becausethe MCP server holds the allowlist in memory.3. The cohort's stubs should already be enriched (their `## Research` populated by`client-enrich`). This skill only *reads* the stub research; it doesn't create it.4. Bitrix tools are usually deferred, so load them first with ToolSearch, e.g.`select:mcp__Bitrix24_CRM__Meridian_Display___create_deal,...link_deal_contact,...set_deal_fields,...add_deal_comment,...list_records,...list_pipelines,...pipeline_summary,...get_record,...find_field`(the server-id prefix may differ; match by the `..._create_deal` etc. suffix).## Reference values (Partner Tier Upgrade)- Funnel: **Partner Tier Upgrade**, categoryId **24**. Renamed 2026-08-06;the vault/XML pipeline key `partner_outreach` is unchanged.- Entry stage: **LinkedIn Research & Connection** = `C24:UC_7KD3PA`.- Other live stages include `Update Info Plinth`, `TIER CHANGE EMAIL SENT`,`Need Artwork/Mockup`, `Gift Mockup Sent`, `Client Approved`, `Client Replied`,`Qtly Touch-Point Scheduled`, `Deal won` = `C24:WON`, `Not Interested` = `C24:LOSE`,plus several `... - HAZEL ONLY` Potential-Partner stages.- **Re-read stages with `list_pipelines` or `pipeline_summary` before relying on anycode.** Stages get retired: `1st Reachout (Email/LinkedIn)` (`C24:PREPARATION`) nolonger exists but appears in older notes and returns a bare token.- **User Group** field = `UF_CRM_5F2C19A7B3D41` (enumeration). Options:`Retail | Palladium Partner | Platinum Partner | Iridium Partner | Rhodium Partner | Potential Partner | Reseller`.The values are the **full labels** ("Palladium Partner"), not the bare tier ("Palladium").An earlier version of this skill passed the bare tier and `set_deal_fields` rejects it.They match stub `user_group_correct` exactly.Set by label: `set_deal_fields(id, {"User Group": "Palladium Partner"})`.- The deal card's **"Client"** = the standard contact binding. Set it with`link_deal_contact` (there is no custom field literally named "Client").## The pipeline### 1. Select the cohortFilter `03-Clients/client-stubs/*.md` frontmatter. All stubs live flat in thatfolder, one `.md` per client. Segmentation lives in `user_group_correct` (thecomputed, true tier: **this is the one to filter on**) and `user_group` (the rawPlinth store group, which is blank on most stubs; do not filter on it).Example (the graduates case): `user_group == Retail` AND`user_group_correct == "Palladium Partner"`.Run `scripts/select_cohort.py` to list matches and emit the cohort mapping.### 2. Compile the cohort mapping`scripts/select_cohort.py` writes `cohort-mapping.xml`: one `<client>` per personwith `bitrix_id`, `first_name`, `last_name`, `outreach_pipeline`, `stub_file_name`,`relative_path`. Keep the tags valid (`<first_name>...</first_name>`).> **This file is NOT the vault census.** The vault's master list is> `03-Clients/partner-client-master-list.xml` (schema 3.0), a complete census of the> partner client base maintained by the weekly pipeline. `cohort-mapping.xml` is a> throwaway working file for one push. Never write one over the other. (An older> version of this skill emitted `crm_pipeline_mapping.xml`, which was the census's> former filename, hence the confusion.)### 3. Create the deals (one per client)For each client: `create_deal(pipeline="Partner Tier Upgrade",title="<first> <last>", stage="LinkedIn Research & Connection")`.- **Test one first**, confirm it returns `Created deal <id>...`, then batch the rest.- For scale, fan the batch out to parallel sub-agents; the cost-of-mistake is realso tell them the cohort is pre-approved and to record every returned deal id.- Record `deal_id -> title` for the next steps.- "One deal per client": if some clients may already have a deal in this funnel,ask Matt whether to skip existing (true one-per-client) or create regardless.- **Deal ids are not ordered against your input list.** Row 2 of a CSV can become ahigher id than row 100. There is no arithmetic shortcut from list position to id;capture each returned id as you go.### 4. Link the contact + set User GroupFor each deal:- `link_deal_contact(deal_id, <client email from stub>)` links by **email**(reliable key; it refuses if no contact matches, so record those). This fills "Client".- `set_deal_fields(deal_id, {"User Group": "<their user_group_correct>"})`.### 5. (Optional) Locate stubs by stageWhen asked "who's in stage X, find their stubs": `list_records(entity_type=deal,pipeline=..., stage=<stage id or name>, created_from=..., created_to=...)`.Match deal title -> `client-stubs` filename (normalized); fall back to last-namefuzzy for name variants (e.g. deal "Jo-Anne Pritchard" -> `Margaret Pritchard.md`,"Katherine Lowell" -> `Kathy Lowell.md`).**Enumerating a large stage:** a whole-day `created_on` filter truncates at 200 rows.Narrow sub-day windows do work, but the bounds resolve against Bitrix's own serveroffset: pass `tz=Europe/Moscow` with your local wall-clock window and walk thewindows. Always verify coverage (union of windows == the stage's reported total)rather than trusting one call.### 6. Post the enrichment comment (one per deal)For each deal, take the client stub's `## Research` block and convert it to BitrixBBCode with `scripts/research_to_bbcode.py --stub PATH --title "<first last>"`, then`add_deal_comment(deal_id, <bbcode>)`.- Bitrix comments render **BBCode, not markdown**: `[B]HEADER[/B]`, `[LIST]`/`[*]`,bare URLs auto-link. `[TBD: ...]` renders as literal text (fine).- **Verify no truncation:** `add_deal_comment` echoes the posted char count, and it mustmatch the generated comment length. Comments here run ~650-2400 chars, well underlimits, so one comment holds the full profile.- Batch via sub-agents for scale; pass each row's comment string verbatim.## Gotchas (the expensive lessons)- **Write-enabled funnel gate.** Creation/updates refuse non-allowlisted funnels, anda stale funnel *name* in the allowlist breaks writes everywhere, not just there.Allowlist by categoryId 24. Restart the app after any config change.- **User Group values are full labels ("Palladium Partner"), not bare tiers ("Palladium").**`set_deal_fields` rejects the wrong label.- **Link contacts by email, never by the stub `bitrix_id`.** Where populated,`bitrix_id` now holds a verified deal-linked Bitrix contact id (backfilled2026-08-06, wrong values blanked), but coverage is partial and email→contact is not1:1: duplicate contacts per email are normal. Resolve via the client's live dealwhen it matters.- **Connector list/summary index lags live edits.** `pipeline_summary` / `list_records`can miss a just-added stage or just-moved deals for a bit, while `get_record`(single deal) is live. If counts look stale, re-sync/reconnect the connector or readindividual deals with `get_record`.- **Shared/ops mailboxes** (marketing@, info@, careers@, supplychain@, or a colleague'saddress on the stub) will link whatever contact owns that email, which may not be thenamed person. Flag, don't assume. A deal-name / contact-email mismatch is **not**automatically a defect: the named client is often the department head while theemail belongs to whoever approves the proof.- **Screen out internal and role accounts.** Exclude any `@meridiandisplay.example` addressoutright, plus role-shaped names (Marketing Vendors, Testing Testing). Name-onlyscreening has missed internal staff before.- **Stub frontmatter is maintained by the weekly driver** (`07-Automation/weekly_partner_pipeline.py`).Don't overwrite it from CRM.- **Attribution/logging:** CRM-only actions don't change the vault, so no `LEDGER`entry is required. Only log to `LEDGER` when you edit vault files.## Bundled scripts- `scripts/select_cohort.py`: filter `client-stubs` by user_group /user_group_correct; print the cohort and write `cohort-mapping.xml`.- `scripts/research_to_bbcode.py`: read a stub's `## Research` and emit the BBCodecomment body (bold headers + bulleted lists) for `add_deal_comment`.Both scripts must be the 2026-08-12 revision or later. Check `select_cohort.py`: its`--vault` help must read `03-Clients/client-stubs` and its `--out` default must be`cohort-mapping.xml`. If it still says `tracker-people` or `crm_pipeline_mapping.xml`,it is the stale copy and will write a wrong `<relative_path>` into every record.