mattrguerra/meridian-outreach-vaultmainPrivate on GitHub, published here in full

07-Automation/


type: automation-index scope: 07-Automation tags: [claude, outreach, automation] created: 2026-07-22 last_modified: 2026-09-21

07-Automation: Weekly Partner Client Pipeline

The live Cowork automation that turns live Plinth order data into a partner client classification each week, maintains the partner stubs and the XML census, and (on your approval) continues to enrichment and Bitrix. Everything here is plain files you can edit in VS Code and re-upload.

The files

  • [[partner-thresholds]]: config + the activation checklist. Tier bands are live program values; min_orders_to_qualify: TBD is the arming switch. Single source of truth; the driver parses the ```yaml block out of it.
  • [[Weekly-Partner-Pipeline-SOP]]: the full A0→H procedure and the guardrails, including "Why incremental", the Plinth truncation bug that dictates the whole architecture. Read that section before touching Stage A.
  • weekly_partner_pipeline.py: the deterministic engine, v2 incremental: seed-baseline → ingest (dedupe by order_ref) → classify (from local history) → verify (no-shrink) → guarded stub/XML/workbook writes, then the read-only Bitrix stages (reconcile-deals, route, outreach-pull-list, outreach-audit). Does not call Plinth or Bitrix; Claude feeds it the raw page dumps and deal pulls. The file is the command-line entry point; the code is in partner_pipeline/, one module per stage (stages.py holds the funnel, stage-name and send-token tables).
  • requirements.txt: pyyaml and openpyxl for the driver, freezegun for the replay.
  • data/order_history.json: the local order history (created at seed time). This, not the API, is the source of truth for lifetime value.
  • [[scheduled-task]]: the live weekly schedule + the exact prompt (ENABLED 2026-07-23).
  • [[pipeline-routing-rules]]: the entry rules route implements, and the four-funnel audit scope.
  • sample/: the generator and replay that produced every client file in this repo (see "The sample" below).

Current state (2026-07-24)

  1. LIVE. Seeded from Ines's roster workbook (baseline as_of 2026-07-20), armed (min_orders_to_qualify: 1), and scheduled: Mondays 06:00 America/Chicago, autonomy through_vault. First run completed 2026-07-23: verify OK, stubs + XML census refreshed for the whole partner base. (The shipped sample's first run, 2026-09-14: 128 partner clients, Palladium 85 · Platinum 28 · Iridium 8 · Rhodium 7.)
  2. refresh-xml maintains the XML map as a complete partner census (adds empty-pipeline pl- chunks for anyone missing; idempotent).
  3. Enrichment (Stage G) and Bitrix push (Stage H) remain manual and human-gated; nobody is routed into an outreach/watch pipeline until those entry rules are decided.

Activating it

Follow [[Activation-Runbook]]: the definitive step-by-step, with commands validated against the roster workbook (in this repo, the sample roster in sample/inputs/). Short version: two-pass seed (All Accounts then the Complete list), catch-up pull, set min_orders_to_qualify, classify + verify + reconcile, one manual E to H pass, enable the schedule.

The sample

Meridian Display Co. and every client in this vault are fictional. The client data is not hand-written: two scripts in sample/ invent a client book and then run the real driver over it.

  • sample/generate_sample.py invents the book: 381 Plinth accounts with a roster workbook (sample/inputs/Partner-Clients-Roster_2026-09-07.xlsx, tabs "2026 Partner Working List", "Complete Partner Clients List" and "All Accounts", totals through 2026-09-07), two weeks of Plinth orders.fetch page dumps, and two weekly Bitrix deal enumerations with their stage histories and contacts. It is seeded, so every run produces the same world. The world is built to exercise the parts of the driver that matter: a Plinth day (2026-09-17) that truncates silently at 50 rows with overlapping offsets; cancelled (27) and refunded (28) orders; clients crossing a tier band mid-week and a brand-new customer who qualifies on a first order (has_baseline=False); an approved merge; the directly-partnered Harlow Ridge Homes mailboxes (Rhodium by override, never cold-emailed) and a staff mailbox on our own domain; an identity twin where only one twin carries the Not Interested closure; a shared company mailbox; a role-named account and a messy shipping-label name; retired Bitrix stages, same-minute workflow re-entries, and a cart that moved into production funnels after converting.
  • sample/run_sample.py replays two Monday runs (2026-09-14 and 2026-09-21) of weekly_partner_pipeline.py against those inputs, doing what the scheduled session does with the MCP calls stood in for by files: seed the baseline in two passes and copy the roster as the first Drive-swap base; then per run, pull and ingest each day of the window until a page adds nothing, classify, verify, write-stubs, refresh-xml, refresh-workbook, reconcile-deals, route, outreach-pull-list (attaching stage histories and contacts only for the deals it asks for), and outreach-audit. The clock is pinned to each run date with freezegun, because the driver dates its outputs by today's date; that is the only thing it changes. Each run's console output is saved as 05-Outputs/partner-runs/run-log_<date>.txt.

Regenerating. The replay refuses to run if 07-Automation/data/order_history.json exists, so run it on a scratch copy with the generated state removed. The root [[README]] ("Replay the sample") has the exact commands and the diff that checks the result: only the ten hand-researched stubs differ, because the replay does not regenerate them. The replay copies each day's Plinth pages into 05-Outputs/partner-runs/raw/, which is not committed (the committed copy is sample/inputs/plinth/).

What came from the replay. Every stub in 03-Clients/client-stubs/, the census 03-Clients/partner-client-master-list.xml, everything in 05-Outputs/partner-runs/ (plus the two Bitrix_Pipeline_Outreach_Audit_*.xlsx workbooks in 05-Outputs/), and everything in 07-Automation/data/ (the merge registry and suppression list are copied in from sample/inputs/). State after the 2026-09-21 run: 381 Plinth accounts in history; 130 partner clients (Palladium 86, Platinum 29, Iridium 8, Rhodium 7), 130 stubs and 130 census chunks, 43 of them with pipeline placement; 54 deals across the four audited funnels, 28 with a verified touch; route: Winback 50, cat 24 first-touch 31, Dormant queue 4, excluded 45.

What was added by hand on top. Ten stubs carry invented research under ## Research (written the way client-enrich writes it, with sources on .example hosts), a few carry Human Notes and Outreach Log rows that match their Bitrix history, 05-Outputs/First Email/ and 05-Outputs/Second Email/ hold eleven sample drafts spread across the six stages, and 04-Working/dual-active-pipelines_2026-09-21.md is a review note built from the 09-21 census. A fresh replay does not recreate these.

Design decisions worth knowing

  • Incremental by necessity. Plinth orders.fetch truncates at roughly 70 orders per response with no signal and unreliable offsets (verified on one long-standing account, 2026-07-22; see [[DECISIONS]]). Lifetime totals therefore come from the local history store, never from a single API call. The verify no-shrink guardrail is the tripwire.
  • Two existing skills do the last two stages: client-enrich (enrichment) and partner-pipeline-push (Bitrix). This template chains them; it does not duplicate them.
  • Modification date is new. Neither stubs nor the XML map carried one. The driver adds last_modified (frontmatter) and <last_modified> (XML), deliberately named apart from the historical updated key the enrichment writeback avoids.
  • Human Notes and Research are never written by the classifier. Research is only ever touched by client-enrich. This is the [[CLAUDE-clients]] scraping-dump contract.
  • Bitrix is always human-gated. Even at full autonomy the SOP holds the push for a person.
  • Shared vault: every write stage appends one [[LEDGER]] entry.

Dependencies

pip install -r 07-Automation/requirements.txt (openpyxl optional for the driver, which falls back to CSV; freezegun is only for replaying the sample). Plinth MCP must be connected for the pull; Apollo/Apify for enrichment; Bitrix for the push.