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

partner-pipeline-push/

partner-pipeline-push

Turns 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 before mass 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.

Prerequisites

  1. Read 03-Clients/CLAUDE-clients.md and root MEMORY.md.
  2. The target Bitrix funnel must be write-enabled in the "Bitrix24 CRM - Meridian Display" connector. Key the allowlist on categoryId 24, not the display name. The funnel was renamed from "Partner Outreach" to "Partner Tier Upgrade" on 2026-08-06, and the stale name sitting in the 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, because the 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 any code. Stages get retired: 1st Reachout (Email/LinkedIn) (C24:PREPARATION) no longer 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 cohort

Filter 03-Clients/client-stubs/*.md frontmatter. All stubs live flat in that folder, one .md per client. Segmentation lives in user_group_correct (the computed, true tier: this is the one to filter on) and user_group (the raw Plinth 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 person with 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 real so 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 a higher 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 Group

For 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 stage

When 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-name fuzzy 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 server offset: pass tz=Europe/Moscow with your local wall-clock window and walk the windows. 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 Bitrix BBCode 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 must match the generated comment length. Comments here run ~650-2400 chars, well under limits, 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, and a 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 (backfilled 2026-08-06, wrong values blanked), but coverage is partial and email→contact is not 1:1: duplicate contacts per email are normal. Resolve via the client's live deal when 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 read individual deals with get_record.
  • Shared/ops mailboxes (marketing@, info@, careers@, supplychain@, or a colleague's address on the stub) will link whatever contact owns that email, which may not be the named 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 the email belongs to whoever approves the proof.
  • Screen out internal and role accounts. Exclude any @meridiandisplay.example address outright, plus role-shaped names (Marketing Vendors, Testing Testing). Name-only screening 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 BBCode comment 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.