LEDGER.md
Language Markdown55.5 KB164 lines
---type: ledgerscope: project-roottags: [claude, outreach, ledger, collaboration]created: 2026-06-16updated: 2026-09-22---# LEDGER: Collaboration change logAppend-only record of every meaningful change made to this vault, and who made it. Two people work here through separate Cowork projects synced over Obsidian: **Matt** and **Hazel**. This file is how each side knows what the other touched.## How to read thisNewest entries at the top. One entry per change-set (a single session's batch of related edits is one entry, not one per file). Each entry records **when**, **who**, **what files**, and **a one-line summary of what and why**.This portfolio copy is curated: the working ledger was much longer, and the entries kept here are those that tell the engineering story. Run artifacts and working notes marked "since removed" or "not included" are not shipped with the vault; the two 2026-09 run entries describe the generated sample that is.## Attribution: who is "who"Each Claude instance attributes its entry to the operator running the session, identified by the active session's user email:- `hello@meridiandisplay.example` → **Matt**- `accounts@meridiandisplay.example` → **Hazel**- Any other / unknown → log as `Unknown (<email>)` and flag it to the operator.Never guess the actor. If the session email isn't one of the known two, write it literally and raise it.## Entry format```### YYYY-MM-DD HH:MM TZ: <Actor> (<session email>)**Files:** `path/one`, `path/two`**Change:** One or two sentences: what changed and why. Link related notes with [[wikilinks]].```Use the operator's local time. Get the real timestamp from the system (`date`); do not approximate.---## Entries### 2026-09-22 15:30 CDT: Matt (hello@meridiandisplay.example)**Files:** 10 stubs in `03-Clients/client-stubs/` ([[Elio Gallardo]], [[Matteo Stanhope]], [[Calla Sutcliffe]], [[Amara Fenwick]], [[Kai Northcott]], [[Juniper Cavendish]], [[Saoirse Tamsworth]], [[Nell Kettering]], [[Cassius Alcott]], [[Sunniva Westbrook]]), 11 drafts under `05-Outputs/First Email/` and `05-Outputs/Second Email/`, `04-Working/dual-active-pipelines_2026-09-21.md`, `06-Resources/Email-2-Drafting-Prompt.md`, `LEDGER.md`**Change:** **Enriched ten stubs off the 09-21 routing candidates** with `client-enrich` (three top-value Winback, three cat 24 first-touch, three in the check-in cadence, and [[Sunniva Westbrook]], who holds two open pipelines): writes under `## Research` only, every claim sourced, frontmatter `title` / `industry` / `company_website` / `last_modified` updated per the scraping-dump contract. Human Notes added on three stubs from Matt's own calls, and Outreach Log rows backfilled from each deal's Bitrix stage history. **Drafted and staged eleven emails**, one or more per pipeline stage: three first emails sent (09-16 and 07-29), one audit PASS awaiting approval, one audit **FAIL** ([[Callum Gallardo]]: the opening asserted a second store no source confirms, so no source, no claim; rework after enrichment), one fresh draft, one **parked** ([[Florian Northcott]]: an open Partner Tier Upgrade deal pauses the check-in per [[pipeline-routing-rules]]), and one **archived** ([[Fenna Sheffield]] replied not interested on 08-03, which is a full opt-out); second emails: one sent, one approved and queued, one drafted for 09-30. Wrote the dual-active review note: both overlapping clients were already parked on 08-12, nothing to move. Swapped the Email-2 prompt's callback example off Fenna Sheffield, who has opted out. No Bitrix writes.### 2026-09-21 10:40 CDT: Matt (hello@meridiandisplay.example)**Files:** `01-Tiers/Retail.md`, `01-Tiers/Palladium-Partner.md`, `01-Tiers/Platinum-Partner.md`, `01-Tiers/Iridium-Partner.md`, `01-Tiers/Rhodium-Partner.md`, `02-Outreach/Check-In-Outreach.md`, `02-Outreach/Client-Interaction.md`, `02-Outreach/Executive-Outreach.md`, `02-Outreach/Winback-Outreach.md`, `CHANGELOG.md`, `LEDGER.md`**Change:** **Fixed the tier and outreach rollups: the drift [[CLAUDE-clients]] had flagged since 2026-08-12.** The tier notes still filtered on raw `user_group`, which the driver leaves blank on most stubs, so each tier note under-reported its own membership (the driver leaves `user_group` blank on every stub it writes, so in the sample the Platinum note would have listed none of its 29 computed members). All five now filter on **`user_group_correct`**, the tier the weekly driver computes; [[Retail]] lists stubs computed as "Not a partner client" or with no tier, which is empty in the shipped sample because the driver only writes stubs for partner clients. The four outreach notes select members by **`outreach_pipelines`** key (`partner_outreach`, `quarterly_checkin`, `winback`), with [[Executive-Outreach]] as a cross-cut on tier or a senior `title` rather than a funnel. Every rollup now displays only fields the stubs carry; the old columns (`outreach_type`, `seniority`, `total_orders_usd`, `enrichment_status` and the rest) existed in zero stubs, so tables sorted on them were not sorted at all. **`ltv` is a currency string**, so no query sorts on it lexically any more (that order puts $9,000 above $12,000). Checked against the 2026-09-21 sample: stub counts by `user_group_correct` are Palladium 86 · Platinum 29 · Iridium 8 · Rhodium 7, matching `05-Outputs/partner-runs/partner-classification_2026-09-21.xlsx`, and 43 stubs carry an `outreach_pipelines` block, matching the census. Release note in [[CHANGELOG]], which also records the generated sample now shipped in `07-Automation/sample/`.### 2026-09-21 06:14 CDT: Matt (hello@meridiandisplay.example)**Files:** `05-Outputs/partner-runs/raw/2026-09-21/` (10 day pages), `07-Automation/data/order_history.json`, `07-Automation/data/outreach_touch_cache.json`, `05-Outputs/partner-runs/partner-classification_2026-09-21.xlsx` + `.plan.json`, `05-Outputs/partner-runs/verify_2026-09-21.json`, `05-Outputs/partner-runs/bitrix-deals_2026-09-21.json`, `05-Outputs/partner-runs/routing-candidates_2026-09-21.json`, `05-Outputs/partner-runs/winback-import_2026-09-21.csv`, `05-Outputs/partner-runs/winback-role-review_2026-09-21.csv`, `05-Outputs/partner-runs/outreach-pull-list_2026-09-21.json`, `05-Outputs/partner-runs/outreach-unresolved_2026-09-21.json`, `05-Outputs/partner-runs/Complete-Partner-Clients-List_updated-2026-09-21.xlsx`, `05-Outputs/Bitrix_Pipeline_Outreach_Audit_2026-09-21.xlsx`, `03-Clients/partner-client-master-list.xml` (+ 2 backups in `05-Outputs/partner-runs/`), 2 new + 128 upgraded stubs in `03-Clients/client-stubs/`, `LEDGER.md`**Change:** **Weekly Partner Client Pipeline run for 2026-09-21 on the shipped sample book** (replayed by `07-Automation/sample/run_sample.py`; transcript `05-Outputs/partner-runs/run-log_2026-09-21.txt`). Stage A pulled 2026-09-14 → 09-21 day by day: **93 orders added, 0 updated**; the 09-14 overlap day re-ingested 6 and added none, which is the dedupe doing its job. **09-17 is the truncation case the pipeline exists for:** its first page came back at exactly 50 rows, the `suspect_row_count`, so the run kept paging; page 2 added 19 new orders out of 49, page 3 added 0 out of 39, and only then was the day treated as converged. One new Plinth account appeared on 09-19. Classify: the merge registry folded 40374 → 40373 as usual; **130 partner clients** (Palladium 86 · Platinum 29 · Iridium 8 · Rhodium 7), up two: [[Uma Ormsby]] crossed into Palladium and [[Wendell Pemberton]] is a new account; one crossing among existing clients ([[Mira Fenwick]], Palladium → Platinum). `verify` **exit 0**, nothing shrank. Stubs **2 created, 128 upgraded**; XML stamped 128 and added 2 `pl-` chunks with `<pipelines count="0" />` (130 total); workbook refreshed 128 rows and appended 2, All Accounts rebuilt to 381, dated column renamed `Plinth LTV 09/21/26`. `reconcile-deals`: **0 new blocks, 2 updated, 43 already current**, 0 unmatched, 0 ambiguous; the two updates are real movement since last week ([[Saoirse Tamsworth]]'s check-in reached First Email Sent on 09-16; Nora advanced [[Niko Garroway]]'s Winback deal from Activated to First Email on 09-17). `route` (read-only): **Winback 50** (1 role-named held for review), **cat 24 first-touch 31** (the two new clients), **Dormant queue 4**, **excluded 45**. Audit: **18 histories pulled fresh, 36 reused from the cache**, 2 contact lookups; **28 deals with a verified touch, 26 never contacted**, 3 with no Plinth match (the two known test carts and one guest cart). **Worth a human look:** Mira Fenwick ordered on 09-18 while parked in the Winback Client bucket, so she is no longer dormant and her Winback deal should not be advanced; that stage move is Nora's. No Bitrix writes, no enrichment.### 2026-09-14 06:12 CDT: Matt (hello@meridiandisplay.example)**Files:** `05-Outputs/partner-runs/raw/2026-09-14/` (8 day pages), `07-Automation/data/order_history.json`, `07-Automation/data/outreach_touch_cache.json` (new), `05-Outputs/partner-runs/partner-classification_2026-09-14.xlsx` + `.plan.json`, `05-Outputs/partner-runs/verify_2026-09-14.json`, `05-Outputs/partner-runs/bitrix-deals_2026-09-14.json`, `05-Outputs/partner-runs/routing-candidates_2026-09-14.json`, `05-Outputs/partner-runs/winback-import_2026-09-14.csv`, `05-Outputs/partner-runs/winback-role-review_2026-09-14.csv`, `05-Outputs/partner-runs/outreach-pull-list_2026-09-14.json`, `05-Outputs/partner-runs/outreach-unresolved_2026-09-14.json`, `05-Outputs/partner-runs/Complete-Partner-Clients-List_updated-2026-09-14.xlsx`, `05-Outputs/Bitrix_Pipeline_Outreach_Audit_2026-09-14.xlsx`, `03-Clients/partner-client-master-list.xml` (+ 2 backups in `05-Outputs/partner-runs/`), 128 new stubs in `03-Clients/client-stubs/`, `LEDGER.md`**Change:** **First Weekly Partner Client Pipeline run on the shipped sample book**, one week after the seed (`05-Outputs/partner-runs/run-log_2026-09-07-seed.txt`: a two-pass seed from `07-Automation/sample/inputs/Partner-Clients-Roster_2026-09-07.xlsx`, the All Accounts tab's 381 rows first, then the partner tab's 128 rows with `--force`). Transcript: `05-Outputs/partner-runs/run-log_2026-09-14.txt`. Stage A: 8 day slices 09-07 → 09-14, **29 orders added**, no page near `suspect_row_count`, the weekend genuinely empty. Classify: the merge registry folded 40374 → 40373 ([[Imogen Radcliffe]]'s two accounts); **128 partner clients** (Palladium 85 · Platinum 28 · Iridium 8 · Rhodium 7) of 380 accounts. `verify` exit 0. `write-stubs` **created 128** on the empty vault and **FLAGGED 1**: "Booth 214 c/o Riverside Expo", a ship-to string in the Plinth name field, written as `Booth 214 c-o Riverside Expo.md` for review rather than dropped. `refresh-xml` added 128 `pl-` chunks; the workbook was rebuilt from the 09-07 base (`Complete-Partner-Clients-List_updated-2026-09-07.xlsx`). `reconcile-deals` wrote **45 pipeline blocks**, 0 unmatched, 0 ambiguous. `route`: Winback 50 (1 role-named held), cat 24 first-touch 29, Dormant queue 4, excluded 45 (33 open deal, 4 winback already done this spell, 4 Harlow Ridge by domain, 3 opt-outs, 1 staff account by domain). Audit: the touch cache was empty, so all **54** stage histories were pulled fresh, plus 9 contact lookups; **26 with a verified touch, 28 never contacted**, 3 with no Plinth match. Bitrix read-only throughout.### 2026-08-19 18:44 CDT: Matt (hello@meridiandisplay.example)**Files:** an outreach-volume-by-sender workbook (not included), `LEDGER.md`**Change:** **Counted one quarter's outbound sends across cats 24 / 26 / 36, split by who actually sent each email.** 34 sends across 23 deals: Hazel 18, Nora 8, Matt 7, plus 1 unsigned send from the team signature (a sample-request flow, reported separately rather than credited to anyone). By funnel: cat 24 13, cat 26 13, cat 36 8. Bitrix read-only; nothing routed, no stubs or census touched. **Three method findings worth keeping.** (1) **Neither `get_stage_history` nor `get_timeline` exposes an activity author, and every send leaves the same shared envelope**, so the only person-level signal is the signature block inside the email body. Deal owner is not a usable proxy: it contradicts the signature on a large share of deals, in both directions, and counting by owner gives a near-even split that is simply wrong. (2) **The timeline's check marker is not a direction indicator**: inbound client mail, auto-replies and bounce notices all carry it. Direction has to be judged from the message's structure. A `main_mail_form_crm_act_email_create_form_signature` div **at the top level** of the body means Bitrix composed it (outbound), but the same div appears **inside quoted history** on inbound replies, so nesting is what decides. (3) **These counts are a floor, not a total.** Replies sent from Gmail instead of composed in Bitrix are never logged as CRM activities; at least 2 such sends turned up only as quoted headers inside client replies. Verification: a blind 6-deal re-audit matched 9 of 10 sends; the one disagreement went to adjudication, proved to be an inbound, client-originated thread, and was removed. Defects found in passing (hard bounces, a few double-fired sends, wrong-name greetings from a mis-merged first-name field, a removal request that became a suppression candidate, and check-in copy carrying the other operator's booking link) went to Matt as a list.### 2026-08-12 17:48 CDT: Matt (hello@meridiandisplay.example)**Files:** `07-Automation/pipeline-routing-rules.md`, `04-Working/dual-active-pipelines_2026-08-12.md` (since removed; the current list is `04-Working/dual-active-pipelines_2026-09-21.md`), `LEDGER.md`**Change:** **Resolved a structural contradiction in [[pipeline-routing-rules]] and listed the clients caught by it.** Matt asked whether every client really sits in only one pipeline. Vault-side, **a group of clients held two active pipelines, every one the same pair: `partner_outreach` + `quarterly_checkin`** (the sample carries two; see `04-Working/dual-active-pipelines_2026-09-21.md`). Spot-checked against live Bitrix rather than trusted: each is genuinely two open deals. Most of them sit at cat 24 TIER CHANGE EMAIL SENT (`C24:UC_6H9C8U`), a stage filled by a bulk push Hazel made on 2026-07-20/21, over two weeks before the rules were ratified: legacy overlap, not a routing failure, and `route` already excludes anyone with an open deal, so it cannot recreate it. **The deeper finding is that the rules contradicted themselves.** "One open outreach pipeline per client" cannot coexist with the tier-upgrade door into cat 24, because a tier upgrade by definition happens to an *active* client (an order within 12 months), which is exactly the Quarterly Check-In condition: every tier upgrade of an in-cadence client structurally produces two open pipelines. **Matt's ruling: an open cat-24 deal takes precedence; the check-in is PAUSED, not cancelled**, by parking the cat-36 deal in Dormant (`C36:UC_LXCMAV`); the existing exit clause (cat 24 completes, the quarterly cadence takes over) resumes it on the normal 3-month rule. A mid-flight cat-24 deal must **not** be closed to clear an overlap: cat 24 runs TIER CHANGE EMAIL SENT → Need Artwork/Mockup → Gift Mockup Sent → Client Approved → Gift Sent → Gift Delivered, so closing it abandons gifting work in progress. The one-pipeline rule stands; the new clause says which one wins, not that two are permitted. **Also recorded what is NOT a violation:** one active block alongside `completed` or `lost` ones is correct by design, since closed deals are the evidence for "previously contacted" and "winbacked this spell" (in the sample's census, 10 of the 45 blocks are closed: 6 completed, 4 lost). A raw count of clients "in 2+ pipelines" looks alarming and mostly is not. A few of them could not be resolved to deal ids because each has duplicate census chunks. No Bitrix writes: these are Hazel's and Matt's deals, and stage moves stay human-gated.### 2026-08-12 17:26 CDT: Matt (hello@meridiandisplay.example)**Files:** `LEDGER.md`**Change:** **Correction to the 17:21 entry: disregard its closing line.** That entry called the duplicate `@harlowridge.example` Plinth accounts merge-registry candidates. They are not, and never will be. **Nothing from Harlow Ridge Homes is ever merged.** Harlow Ridge is in this vault as a guardrail, so the account does not get handled by mistake; it is not an account to be worked, cleaned, deduplicated or optimised, and the duplicate mailboxes stay exactly as they are. The ledger is append-only, so the original line stands below and this entry is the authority on it; nobody should open a merge-registry proposal off that line. For whoever encodes this next: the never-merge rule is **not** written in [[DECISIONS]], `07-Automation/data/merge_registry.json` or `07-Automation/data/suppression_list.json`, which cover never-contact and Rhodium-regardless-of-spend but not merging. Until it is, the only thing standing between a future session and this mistake is the general rule that every merge needs Matt's explicit approval.### 2026-08-12 17:21 CDT: Matt (hello@meridiandisplay.example)**Files:** `07-Automation/weekly_partner_pipeline.py`, `07-Automation/partner-thresholds.md`, that day's classification and verify files (since removed), the Harlow Ridge stubs in `03-Clients/client-stubs/`, `03-Clients/partner-client-master-list.xml`, `LEDGER.md`**Change:** Encoded the two gaps the 2026-08-12 run surfaced, on Matt's go-ahead. **(1) Tier overrides.** New `tier_overrides.emails` block in [[partner-thresholds]] plus `tier_override_for()` in the driver, applied ahead of the bands *and* ahead of `min_orders_to_qualify`, so an overridden mailbox holds its tier at $0 spend. Seeded with the Harlow Ridge mailboxes Matt re-tiered (four in the sample) per [[DECISIONS]] 2026-08-12, which closes the hole that decision describes: the driver tiers each mailbox on its own spend, so the morning's run had demoted the hand-set Rhodium stubs straight back to their computed bands, and would have done so every Monday. **Scoped per-email, not per-domain, on purpose:** most `harlowridge.example` mailboxes classify as not a partner client, so a domain-wide rule would have minted partner stubs for mailboxes the decision never named (in the sample, the two of six left without an override). Re-ran classify → verify (exit 0) → write-stubs → refresh-xml: the overridden mailboxes now classify Rhodium, **0 new stubs** (every overridden Plinth account, duplicates included, matched an existing stub). **(2) `C26:UC_L2JMHY` named "Activated"** in `STAGE_NAMES`, so the census stops recording a raw token for every deal in that stage, and **deliberately left out of `SEND_TOKENS`**, with a comment saying why: Activated is the intake bucket clients land in when a slice of Winback is picked up for the quarter (the funnel is too big to work in one), and nothing is sent on that move (Matt). **Surfaced, not acted on:** several Harlow Ridge mailboxes have duplicate Plinth accounts (see the 17:26 correction).### 2026-08-12 16:41 CDT: Matt (hello@meridiandisplay.example)**Files:** `07-Automation/data/suppression_list.json`, `07-Automation/weekly_partner_pipeline.py`, the Harlow Ridge stubs in `03-Clients/client-stubs/`, `MEMORY.md`, `DECISIONS.md`, `LEDGER.md`**Change:** **Implemented Matt's never-contact rule for directly-partnered accounts, starting with Harlow Ridge Homes.** Harlow Ridge is handled relationally by Hazel (near-daily contact), is **Rhodium regardless of per-mailbox spend**, and gets **no outreach email at all**. Found six Harlow Ridge mailboxes totalling about $19.9K, none suppressed. Actions: (1) the domains `harlowridge.example` and `meridiandisplay.example` added to `suppression_list.json`, which gained a `domains` key; (2) `user_group_correct` set to Rhodium Partner on the four mailboxes Matt re-tiered (all had been computed lower: one Palladium, one Platinum, two blank), `## Human Notes` verified empty and untouched; (3) **driver patched with Matt's explicit go-ahead**: `route` now loads `domains` and excludes any matching email domain with the auditable reason "never-contact domain @x (relationship handled directly)". This is the same class of gap that let **a staff member's own `@meridiandisplay.example` account rank #6 by LTV among the dormant partner clients**: `ROLE_NAME_RE` screens role-shaped *names* and is blind to *domains*. Verified live: the driver compiles, `route` ran clean, and zero Harlow Ridge addresses are in the pushable Winback CSV. **Correction to an overstatement earlier in this session:** the risk was first called live from the stubs' `outreach_pipelines` blocks, but Bitrix shows no send stage was ever reached, so nothing was ever sent, and some stubs assert placements Bitrix does not have. Two integrity gaps: some Harlow Ridge mailboxes have no census chunk, and most of the rest lack an id the router can use, which is why few of them reached `route` at all; they were safe by accident, not by design, until the domain screen. **Domain sweep run for Matt:** combined spend does not single Harlow Ridge out (several single-client domains outspend it). Size cannot tell which accounts are handled directly, so the list went to Matt as candidates, never auto-suppressed.### 2026-08-12 15:58 CDT: Matt (hello@meridiandisplay.example)**Files:** `LEDGER.md` (structural repair)**Change:** **Repaired this file: five real entries were trapped inside the "Entry format" code fence**, rendering as a code sample instead of as ledger entries, including two written earlier today by a parallel session of Matt's. `## Entries` appeared to stop at 2026-08-11 14:29, so **Hazel reading the ledger would not have seen the five newest entries at all**: a collaboration failure in the one file whose whole job is telling each operator what the other did. Matt gave explicit permission to override the append-only rule for this repair, on the grounds that a rule protecting a broken file protects nothing. Method: extracted the five trapped entries, restored the fence to the generic template it was supposed to contain, and **stable-merged** the five into `## Entries` by timestamp; existing entries were inserted around, never re-sorted, so every one kept its exact prior order. Verified the dated-entry count identical before and after, zero lost, zero invented, every entry's text byte-identical apart from the one that had been swallowing the fence close, the timestamp instruction, the `---` rule and the `## Entries` heading for want of a boundary, which now terminates cleanly. A pre-repair backup was kept (since removed).### 2026-08-11 16:06 CDT: Matt (hello@meridiandisplay.example)**Files:** the activated-cohort roster and its exclusions CSV (since removed), `HEARTBEAT.md`, `LEDGER.md`**Change:** **Executed on Matt's sign-off: a cohort of cat-26 deals moved from Client (`C26:UC_JUUNES`) to Activated (`C26:UC_L2JMHY`)**, highest lifetime value first. Verified live after the run: Client plus Activated reconciled exactly to the pre-run stage count. **The connector write bug is fixed** (see 2026-08-10 16:29): Matt corrected the allowlist and restarted the app; a config edit alone did nothing, because the MCP server holds its config in memory. **The cohort was rebuilt mid-run after a bad premise was caught.** The first list ranked the rows of the 08-06 Winback import CSV rather than the live Client stage, and lookups turned up members already closed in `C26:LOSE` from a prior winback spell: rows whose client already had a closed cat-26 deal created no new deal on import, so **ranking the import file silently includes people the one-touch rule forbids re-touching.** Proved the two populations reconcile (the 08-06 and 08-07 import days sum to exactly the Client stage), which made "walk the import list by LTV, keep only those still in Client" provably complete. A handful near the top were excluded: clients already closed at "No Reply from Customer", a literal duplicate row, and one account whose name was unfit to put behind outreach; topping up waits on Matt. Two lessons kept: **rank from the live stage, or prove the two populations reconcile first**; and since cat-26 deals carry no LTV field and deal ids are unordered against the import, any value-ranked selection is computed in the vault and joined back by name. Still open before Nora advances anyone: probable same-person pairs now both in Activated, each of whom would get two winback sequences.### 2026-08-11 13:20 CDT: Matt (hello@meridiandisplay.example)**Files:** `DECISIONS.md`, `CHANGELOG.md`, `SKILLS_REFERENCE.md`, `HEARTBEAT.md`, `MEMORY.md` (the skill files live outside the vault)**Change:** **Rewrote the `client-enrich` skill to work across all four in-scope funnels** (24 Partner Tier Upgrade, 26 Winback, 36 Quarterly Check-In, 16 Abandoned Carts) and recorded Matt's decision to **keep `client-enrich` and retire the `client-enrich-hazel` fork**, sharing the one skill instead of copying it. The skill now resolves cohorts from `outreach_pipelines` frontmatter (it previously walked `03-Clients/Check-In/`, deleted in the 07-23 backfill), checks `07-Automation/data/suppression_list.json` and Not-Interested closures before spending Apollo credits, and carries a research brief per play, including Matt's rule that cat-16 people with no stub are skipped and reported as a count only. A diff showed the fork carried no behaviour of its own (all five scripts byte-identical; SKILL.md differing only in name, H1, and "the user" vs "Matt"), so nothing was lost in retiring it. Decision in [[DECISIONS]], release note in [[CHANGELOG]], [[SKILLS_REFERENCE]] updated (it still listed enrichment as an unfilled gap and never named the skill), [[MEMORY]] given the durable fact, three follow-ups in [[HEARTBEAT]]. **Re-flagged an open defect** so it stops getting lost: `enrich_writeback.py` still bumps `updated` where stubs use `last_modified`, so it appends a second date key.### 2026-08-10 16:29 CDT: Matt (hello@meridiandisplay.example)**Files:** a working note on duplicate check-in touches in `04-Working/` (since removed), `HEARTBEAT.md`. No Bitrix change (blocked, see below), no stubs touched.**Change:** **Found 3 duplicate quarterly touches queued in Active Check-In List, and found that the Bitrix connector could not write at all.** **The duplicates.** Per Matt's rule (cross-pipeline contact is fine; a second Quarterly Check-In to the same person inside one quarter is not), three of the deals in `C36:NEW` already had a check-in this quarter, one of them only weeks earlier. All three belong in **Dormant** (`C36:UC_LXCMAV`), not Quarterly Completed: no check-in was sent from the new deal, so completing it would record a touch that never happened. **Why the standard check missed it, worth internalising:** the census keeps **one `<pipeline>` block per client per funnel, holding the newest deal**, so each client's earlier cat-36 deal had been overwritten by the 08-06 one. A census-based repeat-contact check **structurally cannot see a prior touch in the same funnel**; it only surfaces other funnels. The first pass used the census, reported these three as clean, and separately mis-reported three other clients as never contacted when all three had spring check-ins. The right source for within-funnel repeat-contact risk is `07-Automation/data/outreach_touch_cache.json` (one entry per deal) or a fresh stage-history pull. **Blocker: every deal write fails** with `Configured write funnel 'Partner Outreach' not found`. That is the **pre-08-06 name of cat 24**: the funnel was renamed "Partner Tier Upgrade" and the connector's write-funnel allowlist was never updated, so the server aborts before doing anything, and writes to Quarterly Check-In and Winback fail too, even though those are the write-enabled funnels. Broken since 2026-08-06. The fix is one string in the connector config; better, key it on categoryId 24, which no rename can break. Monday's weekly run is unaffected: every stage of it reads Bitrix and none writes.### 2026-08-10 15:54 CDT: Matt (hello@meridiandisplay.example)**Files:** `07-Automation/weekly_partner_pipeline.py`, `07-Automation/pipeline-routing-rules.md`, `07-Automation/Weekly-Partner-Pipeline-SOP.md`, `07-Automation/scheduled-task.md`, `07-Automation/data/outreach_touch_cache.json`, that day's deal enumeration and audit workbook (since removed), `MEMORY.md`, `DECISIONS.md`, `CHANGELOG.md`**Change:** **Scope correction: the outreach audit covers FOUR funnels, not six.** Matt's call: 24 Partner Tier Upgrade, 26 Winback, 36 Quarterly Check-In, 16 Abandoned Carts. Earlier today the same job also swept 28 (7 Days Delivery Up-Keep) and 30 (Cold Outreach), because the originating question was "look in all of the pipelines on Bitrix": a fair reading for a one-off audit, the wrong basis for a standing weekly job, and scope was never re-confirmed when it became recurring. Removed 28 and 30 from `AUDIT_FUNNELS`, `AUDIT_STAGE_NAMES`, `AUDIT_TAB`, `SEND_TOKENS`, `TEST_DEALS` and every printed string; pruned the touch cache and the enumeration to the in-scope deals; rebuilt the workbook as **4 sheets**, one per in-scope funnel (the sample's shape: 54 deals, 28 with a verified touch, 26 never contacted, 3 with no Plinth match). **Root cause, and the fix for it:** the scope of "all the pipelines" was never written down. [[pipeline-routing-rules]] had only "The three pipelines" (24/26/36), which governs *routing*, and nothing anywhere stated the *audit* set, so the gap got filled by inference. Added an **"Audit scope: NOT the same as routing scope"** table naming the four funnels, listing every funnel deliberately out, and stating that `AUDIT_FUNNELS` in the driver is the executable copy of that table and must agree with it. Widening either is now a decision, not a tweak. **Nothing was damaged by the over-scoping:** the census untouched, zero stubs modified, and no Bitrix writes at all (the audit path is read-only end to end).### 2026-08-10 14:05 CDT: Matt (hello@meridiandisplay.example)**Files:** `07-Automation/weekly_partner_pipeline.py`, `07-Automation/Weekly-Partner-Pipeline-SOP.md`, `07-Automation/data/outreach_touch_cache.json`, the first audit workbook, deal enumeration, pull list and unresolved list (since removed)**Change:** **Ran Stage F5 end to end against live Bitrix: the audit is operational and this week's workbook exists.** Enumerated the outreach funnels for real; the two parked stages (Winback Client `C26:UC_JUUNES`, Abandoned Carts `C16:NEW`) came back capped at 200 rows and were recorded as `enumeration_gaps` rather than passed off as complete. **Three real bugs the dress rehearsal caught, all fixed:**- **Cross-funnel history miscounted sends.** A deal can be MOVED between funnels, so its history legitimately contains other categories' stages: a converted cart carries `C18:*` and `C20:*` entries. `compute_touches` matched on the bare token, so `C20:PREPARATION` would have counted as an abandoned-cart email, because `PREPARATION` is a send token in cat 16. The category prefix must now match the funnel being audited.- **A partial enumeration failed silently.** Feed the audit an enumeration missing a funnel and you got a clean-looking workbook with no hint that part of the outreach was missing. It now compares against the cache, warns loudly, and stamps "INCOMPLETE RUN" on the Summary sheet.- **Parked cohorts were re-pulled every week for nothing.** Reuse required a terminal stage, so every never-touched parked deal was re-pulled weekly. Reuse now also applies when the cached touch count is zero and the stage is unchanged: a deal that has never entered a send stage cannot be sitting in one.**Still blocked on Matt:** the scheduled task's live prompt sits outside the vault, where no session can write it. Until he pastes the new block from [[scheduled-task]], Monday's run executes the old flow and produces only one spreadsheet.### 2026-08-10 11:53 CDT: Matt (hello@meridiandisplay.example)**Files:** `07-Automation/weekly_partner_pipeline.py`, `07-Automation/Weekly-Partner-Pipeline-SOP.md`, `07-Automation/scheduled-task.md`, new `07-Automation/data/outreach_touch_cache.json`, `MEMORY.md`, `DECISIONS.md`, `CHANGELOG.md`, plus smoke-test run artifacts (since removed)**Change:** **Folded the per-pipeline outreach audit into the weekly automation as Stage F5: the run now produces TWO spreadsheets.** Two new driver subcommands, with the same "Claude pulls, the script computes" contract as `ingest` and `reconcile-deals`: `outreach-pull-list` decides which deals need a fresh `get_stage_history` (cached + terminal + stage unchanged = reuse) and which need a `get_record` contact lookup; `outreach-audit` computes verified touches, resolves Plinth identity, and writes `Bitrix_Pipeline_Outreach_Audit_<date>.xlsx` into `05-Outputs/`: one sheet per outreach funnel with Plinth id, email, phone, company, tier, LTV and the date each client was last actually reached out to. Read-only on Bitrix; it must run after `refresh-workbook`, since it resolves identity out of that workbook. **The hard-won rules are encoded as code, not prose:** `SEND_TOKENS` (contact is defined by stage token, never by name: one funnel's "Pre-Call Email" is its creation stage and sends nothing, while cat 26 `FINAL_INVOICE`, *also* named "Pre-Call Email", *is* a send) and `RETIRED_STAGE_NAMES` (six deleted stage ids, three of them real sends; without them clients reached only through those stages read as never contacted). **The touch cache** was seeded with every hand-verified deal, so the first automated run pulls only the histories that can have changed instead of the full sweep; verified that the audit rebuilds identically from the cache alone. The SOP gained Stage F5 and guardrail 6 (reading Bitrix is not routing it). **Bug fixed, with fallout:** the 2026-08-06 cat-36 rename was recorded against the wrong tokens. Live `pipeline_summary` shows `C36:NEW` = "Active Check-In List" and `C36:UC_LXCMAV` = "Dormant", and there is no "Partnership Flags" stage; `STAGE_NAMES` had `NEW` = "Dormant", so the 08-06 reconcile wrote "Dormant" onto the census blocks of clients actually sitting in Active Check-In List. Corrected in the driver; the next `reconcile-deals` run overwrites those blocks. **Needs Matt:** the external scheduled-task prompt is still out of sync.### 2026-08-06 18:18 CDT: Matt (hello@meridiandisplay.example)**Files:** `07-Automation/weekly_partner_pipeline.py` (v3.1), `07-Automation/pipeline-routing-rules.md`; the 08-06 route artifacts regenerated (since removed). No Bitrix writes.**Change:** **Fixed a suppression hole the parallel Winback-load session caught: exclusion evidence is now identity-level, not chunk-level.** Root cause, verified on one client with two census chunks under the same name and email: a legacy empty-`id` chunk carrying his cat-24 **Not Interested** closure and an open cat-26 deal (correctly excluded), and a blockless `pl-` twin that routed to Winback. It was not flagged ambiguous, because the reconcile's resolver correctly prefers the block-bearing twin; `route` then judged each chunk in isolation. **Fix:** `route` unions opt-out, open-deal and winback-done evidence across all chunks sharing an email or a normalized full name (every chunk contributes evidence, partner or not; inheritance only ever excludes, never qualifies). **The first run caught more than the one case:** that client, one more whose opt-out was inherited through a shared company mailbox, and **duplicate-pair twins whose other account entered cat 24 today**, each a double contact in waiting (a winback sequence and a first-touch outreach at the same time); all of them moved out of Winback or the Dormant queue into excluded. The sample carries one such pair: [[Delphine Hartigan]] and [[Delphine Hartigan-Vance]]. **The import CSV the parallel session holds is stale and must be re-pulled before loading.** Its offer to draft merge-registry entries for the duplicate pairs is accepted: drafts against the proof standard, Matt approves, tier-changing folds flagged explicitly.### 2026-08-06 17:28 CDT: Matt (hello@meridiandisplay.example)**Files:** `07-Automation/weekly_partner_pipeline.py` (v3: `reconcile-deals` + `route`), new `07-Automation/data/suppression_list.json`, `03-Clients/partner-client-master-list.xml` + the cat 24 first-touch stubs, `07-Automation/Weekly-Partner-Pipeline-SOP.md` (Stages F3/F4), `07-Automation/scheduled-task.md`, `07-Automation/pipeline-routing-rules.md`; route artifacts regenerated (since removed). **Bitrix writes: the cat 24 first-touch deals created, on Matt's approval.****Change:** **Routing go-live executed and the weekly machinery built**, per Matt ("operational by Monday"; the Winback load delegated to a separate session working from the import CSV). Earlier this afternoon Matt ruled that a **Not Interested closure is a full email opt-out, not a soft flag**: those clients, plus one explicit removal request, are suppressed from every pipeline permanently, and the candidate list was reissued as v2 before anything loaded, so no suppressed client was ever staged. (1) **Cat 24 first-touch cohort pushed:** one deal per client created in Partner Tier Upgrade at LinkedIn Research & Connection (`C24:UC_2YNXWE`), contact linked by email, User Group set, context comment posted; the entry stage verified to hold exactly the pushed cohort, no duplicates despite mid-batch bridge drops. (2) **Driver v3:** `reconcile-deals` absorbs a weekly all-deals pull into census blocks and stubs (name-matched, latest deal per client and funnel, idempotent: a second run wrote nothing new; unmatched and ambiguous deals reported, never guessed; unknown stages warn and degrade), and `route` evaluates the rules read-only, emitting a candidates JSON plus Winback import and role-review CSVs, enforcing the suppression file and Not-Interested closures, and **refusing to run without a readable suppression list**. Validated against the live vault: cat 24 first-touch dropped to 0 (every pushed client now holds an open deal) and those clients moved to excluded, exactly the expected post-push state. (3) **Monday wiring:** SOP Stages F3/F4 and scheduled-task steps 5a to 5c. **Blocker flagged:** the external scheduled-task prompt must be updated by hand, or Monday runs the old flow.### 2026-08-06 16:04 CDT: Matt (hello@meridiandisplay.example)**Files:** `03-Clients/partner-client-master-list.xml` (backup since removed); stubs in `03-Clients/client-stubs/` (`outreach_pipelines` mirrored, wrong `bitrix_id` blanked, verified `bitrix_id` set; overlapping sets); the raw deal pull and a reconcile-review note in `04-Working/` (both since removed); `07-Automation/pipeline-routing-rules.md`, `HEARTBEAT.md`, `MEMORY.md`, `DECISIONS.md`. **Read-only on Bitrix**; no Plinth calls; driver untouched.**Change:** **Routing prerequisites 1 and 2 delivered: the full census-to-Bitrix pipeline reconcile and the `bitrix_id` cleanup.** Pulled **every deal** across the three funnels. Matched by exact normalized deal title = client name; wrote the **new pipeline blocks** and refreshed the existing ones, statuses active / completed / **lost**: closed deals are recorded deliberately, because they are the "previously contacted" and "one winback per dormancy spell" evidence routing needs. Census now: **every matched client's chunk carries placement** (the sample's equivalent: 43 of 130 chunks, 45 blocks); `<pipelines count>` corrected, the summaries' pipeline clauses regenerated, and the cat-24 rename synced inside the XML. **`bitrix_id`:** every known-wrong value blanked (they held Plinth or tracker ids, proven wrong on 07-24); **real contact ids set** for every client with an open cat 26/36 deal, resolved as *the contact the live deal is attached to*, because email search proved unreliable (duplicate contacts per email are the norm). **Not attached, parked for review:** deals with no census chunk (test deals, company-name deals, a few real clients missing from the census) and deals matching duplicate-account chunk pairs, which attach when the merge policy lands; superseded same-funnel deals noted. Rules logged in [[DECISIONS]]. Notable for Matt: **TIER CHANGE EMAIL SENT in cat 24 already holds a large bulk cohort**; the tier-upgrade play is already running in Bitrix at scale.### 2026-08-06 15:26 CDT: Matt (hello@meridiandisplay.example)**Files:** new `07-Automation/pipeline-routing-rules.md`; `02-Outreach/Client-Interaction.md`, `02-Outreach/Winback-Outreach.md`, `03-Clients/CLAUDE-clients.md`, `HEARTBEAT.md`, `MEMORY.md`, `DECISIONS.md`. No Bitrix or Plinth writes, no stubs, no XML, no driver changes.**Change:** **Pipeline routing rules ratified and written down**, from the operations team's SOP for pulling the quarterly check-in and winback lists (a Google Doc, not included) plus Matt's rulings in session, canonicalized in the new [[pipeline-routing-rules]]. Matt resolved the doc's internal contradiction (**Winback = 12+ months** without an order, not the 180 days its formulas compute), set the spend basis (**lifetime partner**: the $2,500 floor is the program definition, not trailing-window spend), added the suppression rule (**one winback touch per dormancy spell**: a still-dormant client is never re-contacted until they order, which forces Check-In to require an order within 12 months), precedence (**Winback > Check-In**, one open pipeline per client) and cat 24 scope (**first touch + tier crossings**). **Cat 24 rename synced:** the Bitrix funnel "Partner Outreach" is now **"Partner Tier Upgrade"** (same categoryId 24, `partner_outreach` key unchanged); live references updated in [[Client-Interaction]] and [[CLAUDE-clients]], history left alone. **Two corrections from Matt later in the session, recorded here rather than silently folded into the first draft:** (1) the encoding flagged for his confirm was overruled: **never-contacted clients who are 12+ months dormant fall under Winback, not cat 24.** Dormancy trumps first touch, cat 24 narrows to *active* clients, and since most of the partner base is 12+ months dormant (75 of 128 partner clients in the sample), Winback is the dominant wave. (2) **Cat 26's first stage, the Client bucket, is NOT automated.** Routing a client into cat 26 only parks them; Bitrix's automation fires when **Nora moves the deal on**, so her stage move is the send and the natural throttle, deal creation is safe, and the Winback copy lives in Bitrix (the long-missing Winback template is moot). **Nothing routes automatically yet:** Bitrix stays human-gated (candidate list → approval → `partner-pipeline-push`).### 2026-08-06 14:58 CDT: Matt (hello@meridiandisplay.example)**Files:** `07-Automation/weekly_partner_pipeline.py` (patched), new `07-Automation/data/merge_registry.json`, `03-Clients/client-stubs/Imogen Radcliffe.md`, `03-Clients/partner-client-master-list.xml` (backup since removed), `07-Automation/Weekly-Partner-Pipeline-SOP.md`, `HEARTBEAT.md`, `DECISIONS.md`, `MEMORY.md`. No Bitrix or Plinth writes; `07-Automation/data/order_history.json` untouched.**Change:** **Implemented the approved-merge registry, on Matt's explicit go-ahead, so approved Plinth-account merges stop reverting every Monday.** On 2026-07-29 Matt had merged [[Imogen Radcliffe]]'s two Plinth accounts by hand (one per email, either side of her organisation's domain rename; combined she is Platinum, while her stub had been computed off the older, smaller account and read Palladium), and that day's entry warned the merge would not survive: `refresh-xml`'s duplicate guard is keyed on `(email, name)` and her two accounts use different emails, so the 2026-08-03 scheduled run re-added the absorbed chunk exactly as predicted. **The fix is data, not a wider key.** `merge_registry.json` (absorbed → survivor, with approver, date and evidence) is read by the driver: absorbed accounts fold into their survivor after Stage B metrics (sums and date bounds), never classify, never get stubs, never enter the census, and `refresh-xml` removes an absorbed `pl-` chunk if one reappears (self-healing). Fail-open on a missing or broken registry (warn and run unmerged; never kill the 06:00 run); the no-shrink `verify` is unaffected, since absorbed keys leave the snapshot and survivors only grow; `refresh-workbook` flags absorbed rows on the partner tab for hand removal instead of touching them. **Tested offline against a full vault copy before deploy:** the fold produced exactly one snapshot change across the whole customer base, `verify` exit 0, `refresh-xml` removed the absorbed chunk and did not re-add it, and malformed, missing and chained registry inputs all degrade safely. Registry seeded with Imogen Radcliffe only (40374 → 40373). **A second candidate, two accounts on the identical email address, is deliberately held**, because that fold changes a tier (Palladium → Platinum) and the person-vs-organisation tiering policy is not written yet. Mechanism decision in [[DECISIONS]] 2026-08-06.### 2026-07-24 15:03 CDT: Matt (hello@meridiandisplay.example)**Files:** `03-Clients/crm_pipeline_mapping.xml`, renamed to `03-Clients/partner-client-master-list.xml` (root `<partner_client_master_list schema_version="3.0">`); all stubs in `03-Clients/client-stubs/` (new `plinth_id` frontmatter field, frontmatter only, Human Notes byte-identical); `CLAUDE.md`, `03-Clients/CLAUDE-clients.md`, `MEMORY.md`, `00-Index.md`, `HEARTBEAT.md`; backups (since removed)**Change:** **Reframed the census as the master list of clients in the Partner Program** (Matt's call): client-centred, with pipeline membership an attribute (empty = not placed yet). Every chunk gained two explicit identifiers, `<bitrix_id>` (CRM contact) and `<plinth_id>` (Plinth account), documented as the dual-identifier contract in [[CLAUDE-clients]]; the stable `id` attribute, the retrieval key, was left intact. Then backfilled `plinth_id` from the local order history (`07-Automation/data/order_history.json`) instead of live calls, joined by email only where unambiguous (shared and duplicate inboxes skipped), never overwriting an existing id. **Major finding: the `bitrix_id` field was not a Bitrix id.** It held the Plinth account id. Verified against live Bitrix on three clients: every stored value sat in the Plinth id range, while real contact ids run an order of magnitude higher. So the vault had one real identifier sitting in both fields; real CRM ids still need a Bitrix lookup, and email to contact is not 1:1. Also flagged: some `pl-` chunk ids disagree with the history's account id for the same email, which points at duplicate Plinth accounts; left for reconciliation.### 2026-07-23 13:58 CDT: Matt (hello@meridiandisplay.example)**Files:** `07-Automation/weekly_partner_pipeline.py` (`refresh-xml` rewrite); `03-Clients/crm_pipeline_mapping.xml` (since renamed `partner-client-master-list.xml`; grown to every partner client) + a pre-change backup (since removed); `03-Clients/CLAUDE-clients.md`; `07-Automation/Weekly-Partner-Pipeline-SOP.md`; `07-Automation/scheduled-task.md`; `CHANGELOG.md`; `DECISIONS.md`**Change:** **Made the XML map a complete partner census, every partner client logged, and taught the automation to keep it that way** (authorized driver edit). Earlier today the Step 5 backfill had created a stub for every partner client, after a fix to `_create_stub`: names containing `/` (Plinth ship-to junk) were read as subdirectories and crashed the run partway, so `_safe_stub_name()` now sanitizes the filename, guards collisions, keeps the true name in frontmatter and the H1, and prints a FLAGGED list for review instead of dropping the row. But `refresh-xml` only stamped clients already in the map and skipped everyone else, so only the pipeline-mapped subset of partner clients was machine-readable. Rewritten to, every run: (1) stamp the mapped partner clients; (2) **add a `<client>` chunk for every partner client missing from the map**, built from the stub, with an empty **`<pipelines count="0" />`** to populate later; (3) key each added chunk `id="pl-<plinth_id>"`, stable and collision-proof against the Bitrix contact ids that overlap the same integer range. **Dedup by identity, not raw email:** same-person duplicate Plinth accounts (same name and email) share one chunk, while two people on one company mailbox each get their own, resolved name-first so each points to the right stub. Idempotent: a second run adds 0. Tested on a throwaway copy first, then run against the vault: **every partner client now has a chunk** (most of them new empty-pipeline chunks), 0 partner clients unrepresented, no duplicate ids, parses clean. **Flag, left as-is:** some pipeline-mapped chunks carry an empty `id` attribute from before this convention; renumbering would desync their live `client_ref`s.### 2026-07-23 13:24 CDT: Matt (hello@meridiandisplay.example)**Files:** `07-Automation/data/order_history.json` (seeded, then the catch-up orders ingested), the roster workbook staged in `07-Automation/data/` (not included), `07-Automation/weekly_partner_pipeline.py`, `07-Automation/partner-thresholds.md`, `07-Automation/Activation-Runbook.md`, `07-Automation/scheduled-task.md`, the weekly scheduled task itself (outside the vault)**Change:** **Seeded, caught up, armed and scheduled the Weekly Partner Client Pipeline**, following the [[Activation-Runbook]] written this morning. **Seed:** Ines's partner roster workbook validated first: both tabs auto-map with zero overrides (All Accounts, every Plinth account; the partner tab, every partner client: 381 and 128 rows in the sample). The driver gained `--tab`, header aliases for the roster's exact columns, `repeat_revenue` in baselines, and an `excluded_order_statuses` filter. **Counting-basis correction from real data:** one long-standing account's workbook "Total Spent (Orders)" equals its *excluding-cancelled* order sum to the cent, so config now sets `excluded_order_statuses: [27, 28]` to keep weekly deltas on the baseline's basis (Nora can still override on the counting-rules call). **Catch-up:** pulled `orders.fetch` day by day 2026-07-20 → 07-23; every day was far under `suspect_row_count`, so one page per day is provably complete. The boundary day is stored but not counted (compute only adds orders strictly after `as_of`). **Armed:** `min_orders_to_qualify: 1`, the last `TBD`. **Scheduled:** Mondays 06:00 America/Chicago (`0 6 * * 1`), autonomy `through_vault`: delta pull → ingest → classify → verify (exit 2 halts everything downstream) → export → write-stubs + refresh-xml, then STOP. It deliberately does not enrich, push to Bitrix, or route anyone into a pipeline, because the entry rules are still TBD. The task modifies the vault unattended every week; the no-shrink `verify` is the safety stop.### 2026-07-22 18:10 CDT: Matt (hello@meridiandisplay.example)**Files:** `07-Automation/weekly_partner_pipeline.py` (rewritten, v2), `07-Automation/partner-thresholds.md`, `07-Automation/Weekly-Partner-Pipeline-SOP.md`, `07-Automation/scheduled-task.md`, `07-Automation/README.md`, `DECISIONS.md`, `CHANGELOG.md`**Change:** **Weekly Partner Client Pipeline rebuilt as v2 (incremental), on Matt's explicit go-ahead, closing today's lifetime-value investigation.** Root cause, from the Matt / Hazel / Ines call: Plinth `orders.fetch` **silently truncates at ~70 orders per response**, with no has-more flag, and `offset` does not skip by order rows, so pages overlap. On one long-standing Rhodium account (Plinth customer 40115 in the sample's numbering) a one-shot query returns 70 of its 74 orders and loses the newest ones, so truncation corrupts both lifetime value and last-order date (an active top-tier client would have routed to Winback). **Decision: never ask the API for an all-time total.** The driver is rewritten around a local append-only order history (`07-Automation/data/order_history.json`, created at seed time): new `seed-baseline` (one-time roster load, fuzzy column mapping, dry run), `ingest` (dedupe by `order_ref`, upserts, a convergence signal for the pull loop), history-based `classify` (lifetime = baseline + deltas, computed locally) and `verify`: the **no-shrink guardrail**, which exits 2 and halts the run if any customer's total or count decreases, because that signature means data loss, not client behaviour. Repointed `CLIENTS_DIR` to `03-Clients/client-stubs/`, and corrected `page_size` to the API's hard cap of 100. The thresholds file now carries the program's tier bands and an activation checklist, with `min_orders_to_qualify: TBD` kept as the arming switch; the SOP gained "Why incremental"; the scheduled-task prompt was rewritten for the day-sliced convergence pull. Tested end to end on synthetic fixtures (seed, dedupe, boundary exclusion, promotion, shrink flag, TBD guard: all pass). **Not seeded, not armed, no schedule created:** Matt activates tomorrow with the roster. Read-only on Plinth throughout; no stubs or XML touched.### 2026-07-22 11:24 CDT: Matt (hello@meridiandisplay.example)**Files:** `03-Clients/` (new `client-stubs/` subfolder with every stub moved in, new `CLAUDE-clients.md`, and the XML map, then `crm_pipeline_mapping.xml` and since renamed, with its stub path refs); the draft files across `04-Working/` and `05-Outputs/` (wikilink fix only; since removed); `CLAUDE.md`, `00-Index.md`, `MEMORY.md`, `README.md`; new `08-Planning/README.md`; `CHANGELOG.md`, `DECISIONS.md`**Change:** **03-Clients restructure and a new root folder.** Moved every client stub into a flat `03-Clients/client-stubs/`; the `03-Clients/` root now holds only the XML map and the **rewritten [[CLAUDE-clients]]** (layout; the XML map contract: chunk boundary, stable ids, `<summary>` regeneration, stub-path sync duty; the scraping-dump contract; bare-name linking). Repointed every stub path in the XML and converted the stale path-form wikilinks in drafts to bare-name links, because path-form links had now broken twice. Root [[CLAUDE]] gained a folder map: `07-Automation/` mapped as unarmed templates, contents untouched by request (flag: the driver's `CLIENTS_DIR` and the SOP still target the old stub location and must be repointed to `client-stubs/` before arming); **new `08-Planning/` created as a human-only planning space for Matt, Hazel and Ines: Claude reads it for context and never writes there** (rule in CLAUDE.md and MEMORY; a placeholder README anchors the folder for Obsidian sync). Dataview rollups unaffected (`FROM "03-Clients"` is recursive). Verified: every stub in place with YAML parsing, 0 residual path-form links in live files, XML parses. Read-only on Bitrix; no stub content touched.### 2026-06-19 12:56 CDT: Hazel (accounts@meridiandisplay.example)**Files:** External: the Winback tab of the original client tracking sheet (a Google Sheet, not a vault file; logged for shared awareness)**Change:** Populated the Winback tab's LinkedIn column for the LinkedIn-connect push. Pulled the **confirmed person URLs** from the enriched [[Winback-Outreach|Winback]] stubs and wrote each to its matching row; the other rows kept their existing placeholder. Deliberately left blank the rows where the URL on file resolves to a different person with the same surname, and flagged the right-person-but-possibly-changed-employer ones rather than guessing. Verified every row against the saved sheet via a CSV export. No vault stub or `## Human Notes` touched.### 2026-06-16 17:44 UTC: Hazel (accounts@meridiandisplay.example)**Files:** `MEMORY.md`, `LEDGER.md`**Change:** Resolved the `[TBD: Hazel's session email]` placeholder, confirmed as `accounts@meridiandisplay.example`, and updated the attribution tables in both files. Session initialization; vault in a clean state, nothing in flight.### 2026-06-16: Matt (hello@meridiandisplay.example)**Files:** `LEDGER.md`, `CLAUDE.md`, `00-Index.md`, `CHANGELOG.md`, `DECISIONS.md`, `MEMORY.md`**Change:** Created the collaboration ledger and baked ledger-on-change into the session rules in [[CLAUDE]]. Set up Matt + Hazel dual-operator attribution for shared vault work via Obsidian sync. See [[CHANGELOG]] and [[DECISIONS]].### 2026-06-16: Matt (hello@meridiandisplay.example)**Files:** entire vault (initial scaffold)**Change:** Vault initialized: the founding client stubs (the [[Check-In-Outreach|Check-In]] and [[Winback-Outreach|Winback]] cohorts), partner-tier notes, outreach-play notes, verbatim email and call templates, and all root reference files. Logged retroactively as the baseline; predates this ledger. See [[CHANGELOG]].