mattrguerra/meridian-skillsmainPrivate on GitHub, published here in full

SKILL.md

Language Markdown10.6 KB186 lines

skills/partner-pipeline-push/SKILL.md

  1. ---
  2. name: partner-pipeline-push
  3. description: >-
  4. Push a cohort of Meridian Display Co. clients from the quarterly-outreach vault
  5. (03-Clients/client-stubs) into a Bitrix deal funnel and post each client's enrichment profile as a
  6. comment on their deal. Use when Matt says "push these clients into the pipeline", "load this
  7. cohort into Partner Tier Upgrade" (or its old name, "Partner Outreach"), "create deals for these
  8. graduates", "push the [tier/segment] clients into Bitrix", "add them to the partner pipeline",
  9. "compile the CRM mapping and push them", or "post the enrichment comments on those deals", or
  10. hands over a cohort (e.g. Retail -> Palladium Partner graduates) and wants them created as deals,
  11. linked to their contact, tagged with User Group, and annotated with their vault research. One deal
  12. per client; deal name = first_name + " " + last_name. Do NOT use for drafting outreach emails
  13. (matt-guerra-email) or for enriching the stubs themselves (client-enrich).
  14. ---
  15. # partner-pipeline-push
  16. Turns a selected cohort of client stubs into Bitrix deals, wires up the client +
  17. tier fields, and drops each client's research into the deal as one clean comment.
  18. End to end: **cohort -> cohort mapping -> create deals -> link contact + User Group
  19. -> (locate stubs by stage) -> post enrichment comment.**
  20. This is a **live CRM write** workflow. Confirm scope and cost-of-mistake before
  21. mass writes, and always verify with a single test write before batching.
  22. > Verified against live Bitrix 2026-08-12. Names and enum values below were wrong
  23. > in earlier versions of this skill and made it fail outright. If anything here
  24. > disagrees with `list_pipelines` / `find_field`, trust the live call and fix this file.
  25. ## Prerequisites
  26. 1. Read `03-Clients/CLAUDE-clients.md` and root `MEMORY.md`.
  27. 2. **The target Bitrix funnel must be write-enabled** in the "Bitrix24 CRM - Meridian
  28. Display" connector. Key the allowlist on **categoryId 24**, not the display
  29. name. The funnel was renamed from "Partner Outreach" to
  30. **"Partner Tier Upgrade"** on 2026-08-06, and the stale name sitting in
  31. the allowlist silently broke **every deal write in every funnel** for five days.
  32. If a write returns `Configured write funnel '...' not found`, that is this bug.
  33. **Editing the config is not enough: the app must be fully restarted**, because
  34. the MCP server holds the allowlist in memory.
  35. 3. The cohort's stubs should already be enriched (their `## Research` populated by
  36. `client-enrich`). This skill only *reads* the stub research; it doesn't create it.
  37. 4. Bitrix tools are usually deferred, so load them first with ToolSearch, e.g.
  38. `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`
  39. (the server-id prefix may differ; match by the `..._create_deal` etc. suffix).
  40. ## Reference values (Partner Tier Upgrade)
  41. - Funnel: **Partner Tier Upgrade**, categoryId **24**. Renamed 2026-08-06;
  42. the vault/XML pipeline key `partner_outreach` is unchanged.
  43. - Entry stage: **LinkedIn Research & Connection** = `C24:UC_7KD3PA`.
  44. - Other live stages include `Update Info Plinth`, `TIER CHANGE EMAIL SENT`,
  45. `Need Artwork/Mockup`, `Gift Mockup Sent`, `Client Approved`, `Client Replied`,
  46. `Qtly Touch-Point Scheduled`, `Deal won` = `C24:WON`, `Not Interested` = `C24:LOSE`,
  47. plus several `... - HAZEL ONLY` Potential-Partner stages.
  48. - **Re-read stages with `list_pipelines` or `pipeline_summary` before relying on any
  49. code.** Stages get retired: `1st Reachout (Email/LinkedIn)` (`C24:PREPARATION`) no
  50. longer exists but appears in older notes and returns a bare token.
  51. - **User Group** field = `UF_CRM_5F2C19A7B3D41` (enumeration). Options:
  52. `Retail | Palladium Partner | Platinum Partner | Iridium Partner | Rhodium Partner | Potential Partner | Reseller`.
  53. The values are the **full labels** ("Palladium Partner"), not the bare tier ("Palladium").
  54. An earlier version of this skill passed the bare tier and `set_deal_fields` rejects it.
  55. They match stub `user_group_correct` exactly.
  56. Set by label: `set_deal_fields(id, {"User Group": "Palladium Partner"})`.
  57. - The deal card's **"Client"** = the standard contact binding. Set it with
  58. `link_deal_contact` (there is no custom field literally named "Client").
  59. ## The pipeline
  60. ### 1. Select the cohort
  61. Filter `03-Clients/client-stubs/*.md` frontmatter. All stubs live flat in that
  62. folder, one `.md` per client. Segmentation lives in `user_group_correct` (the
  63. computed, true tier: **this is the one to filter on**) and `user_group` (the raw
  64. Plinth store group, which is blank on most stubs; do not filter on it).
  65. Example (the graduates case): `user_group == Retail` AND
  66. `user_group_correct == "Palladium Partner"`.
  67. Run `scripts/select_cohort.py` to list matches and emit the cohort mapping.
  68. ### 2. Compile the cohort mapping
  69. `scripts/select_cohort.py` writes `cohort-mapping.xml`: one `<client>` per person
  70. with `bitrix_id`, `first_name`, `last_name`, `outreach_pipeline`, `stub_file_name`,
  71. `relative_path`. Keep the tags valid (`<first_name>...</first_name>`).
  72. > **This file is NOT the vault census.** The vault's master list is
  73. > `03-Clients/partner-client-master-list.xml` (schema 3.0), a complete census of the
  74. > partner client base maintained by the weekly pipeline. `cohort-mapping.xml` is a
  75. > throwaway working file for one push. Never write one over the other. (An older
  76. > version of this skill emitted `crm_pipeline_mapping.xml`, which was the census's
  77. > former filename, hence the confusion.)
  78. ### 3. Create the deals (one per client)
  79. For each client: `create_deal(pipeline="Partner Tier Upgrade",
  80. title="<first> <last>", stage="LinkedIn Research & Connection")`.
  81. - **Test one first**, confirm it returns `Created deal <id>...`, then batch the rest.
  82. - For scale, fan the batch out to parallel sub-agents; the cost-of-mistake is real
  83. so tell them the cohort is pre-approved and to record every returned deal id.
  84. - Record `deal_id -> title` for the next steps.
  85. - "One deal per client": if some clients may already have a deal in this funnel,
  86. ask Matt whether to skip existing (true one-per-client) or create regardless.
  87. - **Deal ids are not ordered against your input list.** Row 2 of a CSV can become a
  88. higher id than row 100. There is no arithmetic shortcut from list position to id;
  89. capture each returned id as you go.
  90. ### 4. Link the contact + set User Group
  91. For each deal:
  92. - `link_deal_contact(deal_id, <client email from stub>)` links by **email**
  93. (reliable key; it refuses if no contact matches, so record those). This fills "Client".
  94. - `set_deal_fields(deal_id, {"User Group": "<their user_group_correct>"})`.
  95. ### 5. (Optional) Locate stubs by stage
  96. When asked "who's in stage X, find their stubs": `list_records(entity_type=deal,
  97. pipeline=..., stage=<stage id or name>, created_from=..., created_to=...)`.
  98. Match deal title -> `client-stubs` filename (normalized); fall back to last-name
  99. fuzzy for name variants (e.g. deal "Jo-Anne Pritchard" -> `Margaret Pritchard.md`,
  100. "Katherine Lowell" -> `Kathy Lowell.md`).
  101. **Enumerating a large stage:** a whole-day `created_on` filter truncates at 200 rows.
  102. Narrow sub-day windows do work, but the bounds resolve against Bitrix's own server
  103. offset: pass `tz=Europe/Moscow` with your local wall-clock window and walk the
  104. windows. Always verify coverage (union of windows == the stage's reported total)
  105. rather than trusting one call.
  106. ### 6. Post the enrichment comment (one per deal)
  107. For each deal, take the client stub's `## Research` block and convert it to Bitrix
  108. BBCode with `scripts/research_to_bbcode.py --stub PATH --title "<first last>"`, then
  109. `add_deal_comment(deal_id, <bbcode>)`.
  110. - Bitrix comments render **BBCode, not markdown**: `[B]HEADER[/B]`, `[LIST]`/`[*]`,
  111. bare URLs auto-link. `[TBD: ...]` renders as literal text (fine).
  112. - **Verify no truncation:** `add_deal_comment` echoes the posted char count, and it must
  113. match the generated comment length. Comments here run ~650-2400 chars, well under
  114. limits, so one comment holds the full profile.
  115. - Batch via sub-agents for scale; pass each row's comment string verbatim.
  116. ## Gotchas (the expensive lessons)
  117. - **Write-enabled funnel gate.** Creation/updates refuse non-allowlisted funnels, and
  118. a stale funnel *name* in the allowlist breaks writes everywhere, not just there.
  119. Allowlist by categoryId 24. Restart the app after any config change.
  120. - **User Group values are full labels ("Palladium Partner"), not bare tiers ("Palladium").**
  121. `set_deal_fields` rejects the wrong label.
  122. - **Link contacts by email, never by the stub `bitrix_id`.** Where populated,
  123. `bitrix_id` now holds a verified deal-linked Bitrix contact id (backfilled
  124. 2026-08-06, wrong values blanked), but coverage is partial and email→contact is not
  125. 1:1: duplicate contacts per email are normal. Resolve via the client's live deal
  126. when it matters.
  127. - **Connector list/summary index lags live edits.** `pipeline_summary` / `list_records`
  128. can miss a just-added stage or just-moved deals for a bit, while `get_record`
  129. (single deal) is live. If counts look stale, re-sync/reconnect the connector or read
  130. individual deals with `get_record`.
  131. - **Shared/ops mailboxes** (marketing@, info@, careers@, supplychain@, or a colleague's
  132. address on the stub) will link whatever contact owns that email, which may not be the
  133. named person. Flag, don't assume. A deal-name / contact-email mismatch is **not**
  134. automatically a defect: the named client is often the department head while the
  135. email belongs to whoever approves the proof.
  136. - **Screen out internal and role accounts.** Exclude any `@meridiandisplay.example` address
  137. outright, plus role-shaped names (Marketing Vendors, Testing Testing). Name-only
  138. screening has missed internal staff before.
  139. - **Stub frontmatter is maintained by the weekly driver** (`07-Automation/weekly_partner_pipeline.py`).
  140. Don't overwrite it from CRM.
  141. - **Attribution/logging:** CRM-only actions don't change the vault, so no `LEDGER`
  142. entry is required. Only log to `LEDGER` when you edit vault files.
  143. ## Bundled scripts
  144. - `scripts/select_cohort.py`: filter `client-stubs` by user_group /
  145. user_group_correct; print the cohort and write `cohort-mapping.xml`.
  146. - `scripts/research_to_bbcode.py`: read a stub's `## Research` and emit the BBCode
  147. comment body (bold headers + bulleted lists) for `add_deal_comment`.
  148. Both scripts must be the 2026-08-12 revision or later. Check `select_cohort.py`: its
  149. `--vault` help must read `03-Clients/client-stubs` and its `--out` default must be
  150. `cohort-mapping.xml`. If it still says `tracker-people` or `crm_pipeline_mapping.xml`,
  151. it is the stale copy and will write a wrong `<relative_path>` into every record.