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

Activation-Runbook.md

Language Markdown6.6 KB131 lines

07-Automation/Activation-Runbook.md

  1. ---
  2. type: automation-runbook
  3. scope: 07-Automation
  4. tags: [claude, outreach, automation, runbook]
  5. created: 2026-07-23
  6. last_modified: 2026-07-23
  7. ---
  8. # Activation Runbook: Weekly Partner Client Pipeline
  9. The definitive, in-order procedure to take the pipeline from built (2026-07-22) to live.
  10. Every command below was first validated 2026-07-23 against Ines's real roster workbook (not
  11. included in this repo), where the column mappings auto-detected with **zero** `--map`
  12. overrides. In this repo the seed is the sample roster,
  13. `07-Automation/sample/inputs/Partner-Clients-Roster_2026-09-07.xlsx` (totals through
  14. 2026-09-07), which auto-detects the same way. The commands and sanity checks below use it.
  15. > The shipped vault is **already seeded and run**: `07-Automation/data/order_history.json`
  16. > exists, and the stubs, census and run outputs are the sample replay's. To rehearse this
  17. > runbook, start from an unseeded copy (see "The sample" in [[07-Automation/README|07-Automation README]]).
  18. Run commands from the **vault root**. Steps 2 and 6 are things you ask Claude to do;
  19. everything else is copy-paste.
  20. ---
  21. ## Step 0: Dependencies (once per machine)
  22. ```bash
  23. uv venv
  24. uv pip install pyyaml openpyxl
  25. ```
  26. `uv venv` creates a `.venv` at the vault root; every `uv run` below uses it automatically (run all commands from the vault root). If you'd rather not keep a venv around, skip this step and carry the deps on each command instead, e.g. `uv run --with pyyaml --with openpyxl 07-Automation/weekly_partner_pipeline.py ...`.
  27. ## Step 1: Seed the baseline (two passes, in this order)
  28. **Pass 1: the full customer base** (tab `All Accounts`: every Plinth customer with
  29. ID/email/LTV/RR; 381 in the sample).
  30. This gives *every* Plinth customer a baseline, so future threshold-crossers (someone at
  31. $2,400 who orders next week) are detected against real history instead of appearing
  32. as unknowns:
  33. ```bash
  34. uv run 07-Automation/weekly_partner_pipeline.py seed-baseline \
  35. --sheet "07-Automation/sample/inputs/Partner-Clients-Roster_2026-09-07.xlsx" \
  36. --tab "All Accounts" --as-of 2026-09-07
  37. ```
  38. **Pass 2: the partner clients, richer data** (tab `Complete Partner Clients List`: 128
  39. clients in the sample, with order counts, dates, company, phone, tier). `--force` is
  40. required and correct here: it overwrites the thin All Accounts baselines with the rich
  41. ones for these 128; everyone else keeps their pass-1 baseline:
  42. ```bash
  43. uv run 07-Automation/weekly_partner_pipeline.py seed-baseline \
  44. --sheet "07-Automation/sample/inputs/Partner-Clients-Roster_2026-09-07.xlsx" \
  45. --tab "Complete Partner Clients List" --as-of 2026-09-07 --force
  46. ```
  47. Sanity: pass 1 should report `Seeded baselines for 381 customers`; pass 2 `for 128
  48. customers` (compare `05-Outputs/partner-runs/run-log_2026-09-07-seed.txt`). If a mapping
  49. ever looks wrong, add `--dry-run` to inspect before writing.
  50. `--as-of 2026-09-07` is the sheet's cutoff (the "LTV up until Monday" Monday: it's in
  51. the filename and the `Plinth LTV 09/07/26` column header). Orders dated on or before it
  52. never re-count; the weekly delta starts after it.
  53. ## Step 2: Catch-up pull (as_of → today): ask Claude
  54. Say: **"Run the catch-up pull per the Activation Runbook."** Claude will, for each
  55. single day D from the `as_of` date (2026-09-07) through today:
  56. 1. `orders.fetch since=D until=D limit=100` (dates as `yyyymmdd`; no status filter,
  57. statuses are handled at compute time),
  58. 2. save the raw page under `05-Outputs/partner-runs/raw/catchup/`,
  59. 3. `ingest --orders <page>`,
  60. 4. if the INGEST line shows `added > 0` or the page hit ~50+ rows, pull the same day
  61. again with a higher `offset` and re-ingest, **until a page reports `added=0`**.
  62. Including the `as_of` day itself is deliberate: boundary orders are stored but never
  63. double-counted (the compute only adds orders dated *after* `as_of`).
  64. ## Step 3: Arm it
  65. In [[partner-thresholds]], set the last `TBD`:
  66. ```
  67. min_orders_to_qualify: 1
  68. ```
  69. `1` is the recommendation: the program is LTV-based and Ines's rule is "once in the
  70. program, always in the program": a single $5K trade-show order counts. Your call.
  71. ## Step 4: First classify + verify + reconcile
  72. ```
  73. uv run 07-Automation/weekly_partner_pipeline.py classify
  74. uv run 07-Automation/weekly_partner_pipeline.py verify
  75. ```
  76. Open `05-Outputs/partner-runs/partner-classification_<today>.xlsx` and check:
  77. - **A long-history client adds up.** Pick the partner client with the most orders and
  78. confirm `ltv` = their roster baseline + their counted post-`as_of` orders, nothing
  79. more. In the sample, [[Amara Fenwick]] (Plinth 40114, 53 orders at the seed) reads
  80. $112,973.40 on the 2026-09-14 run and $114,720.45 once 2026-09-17 is ingested (two
  81. counted orders that day). If a long-history client ever reads *below* their baseline,
  82. something re-introduced a one-shot API pull: stop.
  83. - **Tier mismatches:** `store_user_group` vs `assigned_tier`. Expect some. In the sample,
  84. [[Lucia Pemberton]] is "Palladium Partner" in Plinth but $10,489.98 LTV is the Platinum
  85. band by the math. These go on the Ines/Nora review list, not auto-fixed.
  86. - `verify` must print OK. Exit 2 on the *first* run means a seeding problem: stop.
  87. - Counting basis is already matched to the roster workbook (grand total with shipping,
  88. cancelled 27 + refunded 28 excluded; see [[partner-thresholds]]). If Nora's leadership
  89. call changes the rules, update `amount_field` / `excluded_order_statuses` and log it in
  90. DECISIONS.
  91. ## Step 5: First manual downstream pass (review between each)
  92. ```
  93. uv run 07-Automation/weekly_partner_pipeline.py write-stubs \
  94. --plan 05-Outputs/partner-runs/partner-classification_<today>.plan.json
  95. uv run 07-Automation/weekly_partner_pipeline.py refresh-xml \
  96. --plan 05-Outputs/partner-runs/partner-classification_<today>.plan.json
  97. ```
  98. Then the skills, on the new/changed cohort: `client-enrich` (Apollo credits: it
  99. quotes cost first), then `partner-pipeline-push` (Bitrix, always human-gated).
  100. Each vault-changing stage = one [[LEDGER]] entry.
  101. ## Step 6: Enable the schedule: ask Claude
  102. Say: **"Enable the weekly partner client pipeline."** Claude creates the scheduled
  103. task from [[scheduled-task]] (Mondays 06:00 America/Chicago, `autonomy:
  104. spreadsheet_only`: it pulls, ingests, classifies, verifies, exports, notifies you,
  105. and touches nothing downstream). Autonomy was widened to `through_vault` the same day.
  106. ---
  107. ## After it's live
  108. - Monday summaries report tier counts + verify status. **Exit 2 (no-shrink flag) means
  109. stop and look**: data went missing; never demote or route to Winback off that run.
  110. - `has_baseline=False` rows are customers newer than the seed: normal for genuinely
  111. new buyers (the sample's 2026-09-21 run has one).
  112. - The one spreadsheet Ines ever needs to build by hand again: none. That was the point.