scheduled-task.md
Language Markdown12.5 KB198 lines
---type: automation-schedulescope: 07-Automationtags: [claude, outreach, automation, schedule]created: 2026-07-22last_modified: 2026-08-10---# Scheduled Task: Weekly Partner Client Pipeline**ENABLED 2026-07-23: `through_vault` scope.** Live task `weekly-partner-client-pipeline`(Mondays 06:00 America/Chicago). It maintains the partner stubs and the XML map only: itdoes NOT enrich, push to Bitrix, or route anyone into an outreach/watch pipeline; thoseentry rules aren't decided yet, and the rest of the runbook resumes once they are. Theauthoritative prompt now lives in the task file(`~/Documents/Claude/Scheduled/weekly-partner-client-pipeline/SKILL.md`); the block belowis the in-vault reference copy, kept in sync.> [!NOTE] **Synced 2026-08-10: Matt pasted the current prompt into the external> SKILL.md** (`~/Documents/Claude/Scheduled/weekly-partner-client-pipeline/SKILL.md`),> clearing the drift that had stood since 08-06. The block below is the in-vault reference> copy and now matches: steps 5a-c (deal reconcile + route) and 5d-5e (the outreach audit,> the **second weekly spreadsheet**), four-funnel scope 24/26/36/16.>> **No session can verify this.** That path is outside the vault and outside what the> device bridge will grant, so "synced" rests on Matt's word, not a read. The tell if it> ever drifts again: Monday's summary reports only ONE spreadsheet, or the audit's Summary> sheet is stamped "INCOMPLETE RUN". Whenever this reference copy is edited, assume the> external file is stale until someone re-pastes it, and put the warning callout back.## Schedule- **Cadence:** weekly, Monday 06:00 America/Chicago.- **Cron:** `0 6 * * 1`- **Edit freely:** change the day/time here before enabling.## What the run doesRuns the [[Weekly-Partner-Pipeline-SOP]] at `autonomy: through_vault`: pulls thetrailing week's orders from Plinth (day-sliced, offset+dedupe until convergence), ingeststhem into the local order history, classifies from that history (baseline + deltas),verifies the no-shrink guardrail (exit 2 halts the run), exports the spreadsheet, then**creates/upgrades partner stubs, refreshes the XML map** (stamps existing partnerchunks and adds an empty-pipeline chunk for every partner client missing from the map, soit stays a complete census), and **regenerates the manual Google-Drive-swap workbook**(`Complete-Partner-Clients-List_updated-<date>.xlsx`) from the previous copy (Plinthcolumns refreshed, newly-qualified partner clients appended, All Accounts base rebuilt,human columns + the curated working tab left untouched), then reconciles the Bitrix dealsinto the census, computes the routing candidate lists, and **builds the secondspreadsheet, `Bitrix_Pipeline_Outreach_Audit_<date>.xlsx`** (one sheet per outreach funnel24/26/36/16: who is in it, how to find them in Plinth, when they were last actuallycontacted), then notifies you. It stops there: no enrichment, no Bitrix **writes**, andnobody routed into an outreach/watch pipeline. Baseline is seeded and`min_orders_to_qualify` is set, so the run is armed.**Two spreadsheets out, every week:** the Drive-swap partner roster (Stage F2) and theper-pipeline outreach audit (Stage F5). Matt still swaps the roster into Google Drive by hand.## The prompt (what the scheduled Claude session executes)```Run the Weekly Partner Client Pipeline for the Meridian Display Co. quarterly-outreach vault.Read 07-Automation/Weekly-Partner-Pipeline-SOP.md and 07-Automation/partner-thresholds.mdfirst. CRITICAL CONTEXT: Plinth orders.fetch silently truncates large result sets (~70orders/response, no "has more" flag) and offset pages overlap. Never treat one responseas complete; never query an all-time total. The SOP's "Why incremental" section governs.0. PRECONDITION. If 07-Automation/data/order_history.json does not exist, STOPimmediately and notify Matt that the pipeline is not seeded (see07-Automation/Activation-Runbook.md). Do not pull anything.1. DELTA PULL (Stage A). For each single day D in the trailing pull_window_days(from partner-thresholds.md, default 8), oldest first:a. orders.fetch since=D until=D limit=page_size (dates as yyyymmdd; hard cap 100).b. Save the raw response to 05-Outputs/partner-runs/raw/<run-date>/D_p0.jsonc. python3 07-Automation/weekly_partner_pipeline.py ingest --orders <that file>d. Read the INGEST line. If added > 0 OR the page had >= suspect_row_count rows,pull the same day again with offset += page_size, save as D_p1.json, ingest,and repeat until a page reports added=0. Offsets overlap by design; theingest dedupe by order_ref absorbs it.Plinth throttles at 10 req / 10 sec; just proceed, slowness is fine.2. CLASSIFY + EXPORT (Stages B-D):python3 07-Automation/weekly_partner_pipeline.py classify(No arguments: it computes from the local history store, never from the API.)3. VERIFY (mandatory):python3 07-Automation/weekly_partner_pipeline.py verifyExit 0: proceed. Exit 2: a lifetime total SHRANK. That is data loss, not reality. STOPthe run entirely: no stubs, no XML, no Bitrix, and say prominently in the summary whichcustomers were flagged and that nothing was changed.4. WRITE STUBS + REFRESH XML + REFRESH WORKBOOK (through_vault; only if verify exited 0):python3 07-Automation/weekly_partner_pipeline.py write-stubs --plan 05-Outputs/partner-runs/partner-classification_<date>.plan.jsonpython3 07-Automation/weekly_partner_pipeline.py refresh-xml --plan 05-Outputs/partner-runs/partner-classification_<date>.plan.jsonpython3 07-Automation/weekly_partner_pipeline.py refresh-workbook --plan 05-Outputs/partner-runs/partner-classification_<date>.plan.jsonHuman Notes are never touched; refresh-xml backs up first. refresh-xml keeps the XMLmap a COMPLETE census of the partner base: it stamps every partner client already inthe map AND adds a chunk (empty `<pipelines count="0" />`, id `pl-<user_id>`) for anypartner client missing from it. Idempotent: re-running never duplicates. refresh-workbookregenerates the Drive-swap workbook `Complete-Partner-Clients-List_updated-<date>.xlsx`from the latest existing copy (base): refreshes only the Plinth-derived columns, appendsnewly-qualified partner clients, rebuilds the All Accounts base, renames the datedPlinth LTV header, and leaves the curated working tab + every human column untouched.The vault copy is NOT wired to Google Drive: Matt uploads it by hand. If write-stubsprints a FLAGGED list (stubs from messy Plinth name fields), include it verbatim forreview. Do NOT delete or rename anything.5. DEAL RECONCILE + ROUTE + OUTREACH AUDIT (5a-c added 2026-08-06; 5d-e added 2026-08-10.Entry rules: 07-Automation/pipeline-routing-rules.md. Everything here READS Bitrix andwrites nothing back to it.):a. Enumerate EVERY deal in the FOUR in-scope outreach funnels: 24, 26, 36, 16.(Scope set by Matt 2026-08-10. 7 Days Delivery Up-Keep (28) and Cold Outreach (30)are deliberately OUT of scope; do not add them back without asking.)pipeline_summary per funnel for the stage list, then list_records per non-emptystage (add created_from="2020-01-01" on stages over 50 rows; split date windowsuntil counts match). Save ONE JSON to05-Outputs/partner-runs/bitrix-deals_<run-date>.json as{"deals":[{"deal_id","cat","stage","stage_date","title","owner","created","value"}]}.reconcile-deals ignores everything outside 24/26/36, so one file feeds both.KNOWN LIMIT: date-filtered list_records returns at most 200 rows per call andignores sub-day time bounds, so two parked stages cannot be fully enumerated:C26:UC_JUUNES ("Client") and C16:NEW ("New Abandoned Cart"). Recordthe counts you could not enumerate in the summary; never pretend they were covered.b. python3 07-Automation/weekly_partner_pipeline.py reconcile-deals --deals <that file>c. python3 07-Automation/weekly_partner_pipeline.py routed. python3 07-Automation/weekly_partner_pipeline.py outreach-pull-list --deals <that file>Read 05-Outputs/partner-runs/outreach-pull-list_<date>.json. For every id inneed_history call get_stage_history(entity_type=deal) and attach the result to thatdeal in the enumeration JSON as "history":[{"stage","at"}] (stage = the raw id like"C36:EXECUTING", at = "YYYY-MM-DD HH:MM"). For every id in need_contact callget_record and attach "contact":{"name","email","phone"}. Deals NOT in need_historymust be left WITHOUT a history key: that is how the cache gets used. Batch ~10concurrent; on QUERY_LIMIT_EXCEEDED reduce the batch and retry. No id may be skipped.e. python3 07-Automation/weekly_partner_pipeline.py outreach-audit --deals <that file>MUST run after step 4's refresh-workbook: it resolves Plinth ID, phone, tierand LTV out of that workbook. Writes the second spreadsheet,05-Outputs/Bitrix_Pipeline_Outreach_Audit_<date>.xlsx, and updates07-Automation/data/outreach_touch_cache.json.route is read-only and emits the weekly candidate lists (winback import CSV +cat24/dormant in the JSON). Do NOT create deals, do NOT move stages, do NOT "correct"a stage the audit flags: loading lists into Bitrix stays human-gated.6. STOP. Do NOT enrich (client-enrich) and do NOT push to Bitrix (partner-pipeline-push).Post a short summary to Matt: run date, verify result, tier counts, stubs created vsupgraded, XML chunks stamped vs newly added, reconcile stats (new/updated blocks,unmatched/ambiguous deals), route counts (winback / cat24 / dormant queue / excluded),any FLAGGED names, and any has_baseline=False customers worth a look (count only).For the audit: BOTH spreadsheet filenames, deals per funnel, how many clients have averified touch vs never contacted, how many histories were pulled fresh vs reused fromcache, the count with no Plinth match, and the parked stages you could not enumerate.Call out anything the audit DROPPED (no history and no cache entry): that is a bugsignal, not a normal result.7. LEDGER. Both spreadsheets + raw dumps + history + stubs + XML + the touch cache werewritten under the vault: append ONE batched entry to LEDGER.md (attribution by sessionemail; Matt = hello@meridiandisplay.example, Hazel = accounts@meridiandisplay.example),per the CLAUDE.md shared-vault rule.Do not expose customer PII in the chat summary. Counts and tiers only.```> Step 5a's KNOWN LIMIT predates the 2026-08-11 enumeration fix recorded in the SOP> (sub-day windows do work when `tz=Europe/Moscow` is passed; verify coverage against the> stage total). The prompt above is the 2026-08-10 sync and has not been re-pasted since.## After thresholds are set: the human-approved continuation**Update 2026-07-27:** Stage F2 (`refresh-workbook`) now also runs automatically each week:it regenerates the Drive-swap workbook from the previous copy (Plinth columns refreshed,newly-qualified partner clients appended, working tab + human columns untouched). Matt stillswaps the file into Google Drive by hand; the vault copy is not connected to Drive.**Update 2026-07-23:** Stages E and F (write-stubs + refresh-xml) now run automatically eachweek under `through_vault`. Stages G and H below (enrich, Bitrix push) stay manual andhuman-gated, and nobody is routed into a watch/outreach pipeline until those rules exist.Once [[partner-thresholds]] has real numbers, review the spreadsheet, then run thedownstream stages by hand (or ask me to):```# Stage E: create missing stubs / upgrade existing (adds last_modified)python3 07-Automation/weekly_partner_pipeline.py write-stubs \--plan 05-Outputs/partner-runs/partner-classification_<date>.plan.json# Stage F: refresh the XML map: stamp existing + add missing partner chunks (backs up first)python3 07-Automation/weekly_partner_pipeline.py refresh-xml \--plan 05-Outputs/partner-runs/partner-classification_<date>.plan.json# Stage G: enrich the new + changed stubs# invoke the `client-enrich` skill on that cohort# Stage H: push the approved cohort to Bitrix# invoke the `partner-pipeline-push` skill (one deal per client, tagged by User Group)```Each of E to H that changes the vault gets its own LEDGER entry.## To widen autonomy laterSet `autonomy` in [[partner-thresholds]] to `through_vault` (auto stubs + XML, holdBitrix) or `full` (everything). Bitrix push stays human-gated in the SOP regardless:change that only deliberately.