| Same screens, same 28 days | 🍎 iOS | 🤖 Android | What we saw before (blended) |
|---|---|---|---|
| First workout started | 99.7% | 39.7% | 74% |
| Trial started (of pricing views) | 18.8% | 7.5% | 13.8% |
Blended, this reads "26% never start a workout" — a product problem. Split, iOS loses 0.3% and Android loses 59% on identical screens. iOS is already performing at the level we would have set as a target. The blend was pointing every recommendation at the wrong half of the estate.
Also corrected: the iOS "52.7% delete rate" is not a delete rate — it is 29 delete events ÷ 55 install events over six days, and a delete on 28 Jul can belong to a May install. Normal shape for a paywalled fitness app.
We can finally answer this — and the answer is: the branded lines are cheap and worth keeping, the non-branded experiment already ran and already failed.
1 · What actually runs is three Branded campaigns at ~$1.19 an install. US $89.14/70 · EU $73.12/66 · UK $14.61/12 — $176.87 for 148 of the 163 installs. That is cheap, and it is worth keeping. But branded is arithmetically capped: you can only buy as many searches for "the movement athlete" as people actually make, and those people already know us — our own listing is the top organic result for our own name. There is no volume to scale into.
2 · The "buy new demand" experiment already ran, and it already failed. The five paused campaigns are exactly that test — non-branded, competitive and demand-gen — and they burned $230.45 for 15 installs, about $15 each, one returning literally zero. Against branded at $1.19 that is a 12× gap between buying people who know us and trying to buy people who don't. They are already off. There is nothing to pause.
3 · Cheap is not the same as incremental — and we still cannot claim profit. The estate has been carrying "$18.98/install"; that is revenue per install, not cost, and its denominator (120 AppsFlyer installs) does not match Apple's 163. Putting $18.98 next to $2.50 would be a false profit claim, so CAC and ROAS stay unpublished until the revenue join is denominator-matched. Proving branded ASA is incremental needs a holdout, not a lower CPI.
| 1 | NIC-101 PARKED · 90 min🔴 MAKE THE IN-APP CANCEL DOWNSELL PLAN-AWARE — annual leavers are shown a WORSE deal than our own standing offer. Measured 11 Aug: 84 of the 115 in the cancel queue are ANNUAL, and every can… The cancel downsell offers ANNUAL members a WORSE deal than our own annual price. Every annual canceller who takes it costs us money — and 73% of cancellers are annual. |
| 2 | NIC-100 ASSIGNED-DEV · 60 min🤖 ANDROID: 32% OF USERS NEVER SUCCESSFULLY SIGN IN — and it is NOT a crash. RE-DIAGNOSED 11 Aug with Play vitals + a geo-controlled GA4 split. 🔴 THE CRASH THEORY IS REFUTED: Play Developer R… Android loses 59% of users between finishing setup and workout 1; iOS loses 0.3% on the SAME screens. Largest single defect on the app rail, and it is a bug hunt, not a redesign. |
| 3 | AGA-104 Aga · 20 min💸 PAID COMPLIANCE SWEEP — 5 checks in the ad UIs, ~20 min, stops live rule breaches. (a) Google Ads → Campaigns: verify Competitive Non-Branded + Competitors Branded + PMax are PAUSED per th… 20-minute paid compliance sweep. Competitive Non-Branded took £83.67 for zero sales, MOF - Lead Form is spending into a sync dead since Dec 2025, and Google's budget stepped +43% with the measurement gate uncleared. |
| 4 | NIC-98 DONE · 45 min🔴 FIX /start-training/ — RE-MEASURED LIVE 11 Aug 13:10 MDT, and this row's numbers have moved: the DEAD $297 lifetime price is GONE (zero occurrences of '297' on the page) and 'checkouts-v3'… /start-training/ is live RIGHT NOW showing the dead $297 lifetime price and the retired 4.9★ rating. It is a page we send buyers to. |
| 5 | FLEET-33 Fleet · 90 min📧 MOVE THE ASK IN 973 Nurture Sequence 1 NEW COPY FROM CLAUDE — from step 18 (day 57) to ~step 7, where 751 Nurture Sequence 1 proved it works. Measured 5 Aug via offer_reach_check.py: the o… 973 asks for the sale at step 18 — day 57. Move it to ~step 7 where the readers still are. 40,287 sends in 28 days sit behind that one decision. |
| 6 | FLEET-34 Fleet · 90 min📧 GIVE 758 Nurture Sequence 2 A PRICED OFFER BEFORE ANYONE ELSE MIGRATES IN — it holds 14,619 contacts and is TRIAL-DOOR ONLY: 11 emails, every CTA to /start-training/, no priced offer on an… 758 holds 14,619 contacts and carries NO priced offer at all. The biggest audience in the estate is being nurtured toward nothing. |
| 7 | AGA-129 Aga · 20 min📧 APPROVE: POINT ONE WEEKLY EMAIL AT THE QUIZ ROOT — one link decision in one already-scheduled send to the engaged list, CTA → https://quiz.themovementathlete.com/ (root, UTMs per the link … Point one already-scheduled weekly email at the quiz root. One link, one decision, zero build — and the quiz is the only rail currently selling. |
| 8 | NIC-94 DONE · 15 min📧 THE RESULTS EMAIL TELLS EVERY LEAD THEIR PASSWORD IS "[Chosen Password]" — quiz_email.html line 24 is literal text, not a merge field, so the reader sees the words '[Chosen Password]' wher… The results email tells every new lead their password is literally "[Chosen Password]". It is the first email a quiz lead receives. |
| 9 | AGA-147 Aga · 5 min🟢 GO/NO-GO: give '758 Nurture Sequence 2' (14,600 contacts) a quiz-root CTA slot — NP verdict lever 2, $0 ('fix the tap before the bucket'). Email currently sends the $97 quiz ~1 lead/8 days… Give 758 (14,600 contacts) a quiz-root CTA slot. $0, and it is the 'fix the tap' lever, not a new campaign. |
| 10 | AGA-119 Aga · 20 min💷 THE ANNUAL OFFER TO THE ENGAGED — 🔴 DO NOT APPROVE YET, THE AUDIENCE HALVED. Measured live 11 Aug via the AC API: tag 237 'Engaged' holds 2,912 people, not the ~6,367 this row claimed and … The annual offer to the engaged — but the audience HALVED on measurement. Read the new number before approving; 'the engaged list' is seven different tags, 1,666 to 19,826. |
Not on this list because they are already in
flight: AGA-153 (the Apple Search Ads key) and the still-has-access retention play. A list that
hands back work already underway costs more time than it saves.
| Lane | 28d | MTD | 7d | Read |
|---|---|---|---|---|
| Paid ads | 188 | 104 | 78 | Buying leads, not customers — Meta bought 0 purchases for £261.79 in the first 9 days of August. |
| ⚠ Attribution-dark (unmeasurable) | 110 | 33 | 23 | We cannot say where these came from. The biggest measurement hole we have. |
| TMA.com content CTAs | 62 | 52 | 29 | TMA.com content CTAs. The capture layer only switched on 5–6 Aug — too young to judge until ~19 Aug. |
| Organic search (untagged) | 44 | 8 | 8 | Untagged organic. Real, and the cheapest lead on the board. |
| Other referral | 17 | 6 | 2 | Other sites sending us people. |
| BWA article CTAs | 8 | 2 | 2 | BWA article CTAs — 318 pages went live 5–6 Aug; this should climb. |
| Organic social | 7 | 5 | 3 | Instagram/Facebook/TikTok. Saves-per-reach 0.39% vs a 1.00% target is the lever. |
| AI answer engines | 6 | 6 | 6 | AI answer engines (ChatGPT, Perplexity). Tiny but real, and free. |
| Other | 4 | 3 | 2 | Unclassified but attributed. |
| 🔧 Internal / QA traffic | 3 | 1 | 1 | Our own QA traffic — never a result. |
| YouTube descriptions | 3 | 3 | 3 | 875 videos, fully tagged since 9 Aug. Measure per LINK, never per video. |
| 3 | 2 | 2 | 🔴 Only a handful attributed — email revenue is still DARK, the single biggest hole. | |
| All lanes | 455 | 225 | 159 |
🔴 24% of leads are attribution-dark — we cannot say where they came from. That is the number that makes every other channel verdict softer than it looks, and closing it is worth more than any single channel's growth.
| 🌐 Web (the quiz) | 📱 App | |
|---|---|---|
| Sells today? | YES — the only rail selling | renewals from 240 existing subs |
| Since 1 Aug | 15 sales · $1,298 · AOV $86.56 | $3,413/mo MRR |
| The one leak that matters | S01→S02: keeps 46.8% (re-verified on clean August data) | Android sign-in + first workout (iOS is fine) |
| Where the work should go | One test on screen 1 | One question to Nic, then decide |
Everything downstream of quiz screen 1 is already healthy — from question 1 to the paywall the quiz barely leaks, and the assessment does not scare people off. That is why screen 1 is worth more than anything we could buy.
✅ Working — the new quiz is the only rail selling: 15 sales, $1,298, AOV $86.56 since 1 Aug (the old v2-2 rail: zero in August). iOS post-install behaviour is excellent. Deliverability is healthy — bounce 0.03%, unsub 0.29%.
❌ Not working — cold paid on either platform (Meta: £261.79 for zero purchases, 1–9 Aug). Android, top to bottom. Email revenue is still DARK — the single biggest measurement hole we have.
⏳ Too early to judge — the owned-content capture layer switched on 5–6 Aug: first honest read ~19 Aug, full read ~2 Sep. Any "content produces N leads" figure whose window starts before 6 Aug is an artefact, not a result.
| Step | Figure |
|---|---|
| Stripe recurring net, actually banked (day 12/31) | $2,585 |
| ÷ 12 × 31 → Stripe at full-month pace | $6,678 |
| + app net this month · iOS $1,288 × 1.0232 + Play $223 × 0.7311 MODELLED | $1,481 |
| = pace — THE NUMBER | $8,159 |
| − break-even target | $15,000 |
| = gap | $6,841 |
tools/finance/kpi_15k_sync.py → kpi_15k.json, rendered into this tab. Never hand-typed — if a figure here looks wrong, fix the script, not the page.| Per day → month | yesterday | last 7d | MTD |
|---|---|---|---|
| ✅ CUSTOMERS WON money committed | 2 | 35 | 53 |
| ① bought at a discountpays now · NO trial | 0 | 10 | 15 |
| ② trial converted → payingcounted when it converts, not when it starts | 0 | 9 | 16 |
| ③ app — new subscriptioniOS + Android | 2 | 15 | 20 |
| ④ app — resubscribedcame back | 0 | 1 | 2 |
| ↑ ① + ② + ③ + ④ = customers won. This is why "4 won / 0 trials started" is normal — discounted buyers skip the trial entirely. | |||
| 🎟️ Trials STARTED a commitment, not yet revenue — converts later | 3 | 11 | 26 |
| ↳ of those, died in trial | 0 | 16 | 19 |
| ❌ PAYING CHURN payers whose access ended (dead trials excluded — they never paid) | 7 | 57 | 70 |
| ↳ all cancellations incl. dead trials | 7 | 73 | 89 |
| ↳ cancel REQUESTS — decided to leave (leading) | 0 | 11 | 19 |
| NET PAYING CUSTOMERS = won − paying churn | -5 | -22 | -17 |
tools/seo/data_lead_lanes.json (pulled 2026-08-12).tools/aso/aso_daily.py.actives_movement chart cannot be segmented by store. Per store we CAN see paying
subs, trials, revenue and net movement (above). App free-account → paying conversion:
3.96%.| Source | yesterday | last 7d | MTD |
|---|---|---|---|
| Leads captured — these reach email | |||
| Quiz / assessment
✅ live
AC list 209 + GA4 quiz funnel — the measured channel | 16 | 154 | 262 |
| Lead magnets (guides, challenges)
✅ live
AC only — NOT wired to GA4, so no source/UTM attribution on these | 0 | 20 | 23 |
| App sign-ups — emailed, but no source | |||
| App free sign-ups — iOS | 9 | 69 | 121 |
| App free sign-ups — Android | 6 | 57 | 91 |
| CORRECTED 5 Aug 2026 — the old note read the WRONG LIST and was false. App sign-ups DO reach AC: Django's activteUserAndToList() writes them to list 204 (TMA ACCOUNT ACTIVATED) + tag 167 free_signed_up_mobile_&_web, which triggers 992 TMA | Welcome Sequence AFTER APPSIGNUP AB TEST Aug4th. Tag 167: 11,859 subs, +2,024/30d. List 199 is a different, genuinely empty list — email follow-up IS live. Counts below stay GA4-sourced (split iOS vs Android) because AC cannot split by store. 🔴 OPEN: 992's trigger fires on ANY activation — 2 of its first 4 entrants were the wrong audience (a quiz lead and a paying customer). | |||
| Where the quiz leads came from (GA4 attribution) | |||
| TMA.com content CTAs | 0 | 29 | 52 |
| BWA article CTAs | 0 | 2 | 2 |
| YouTube descriptions | 0 | 3 | 3 |
| AI answer engines | 0 | 6 | 6 |
| Organic search (untagged) | 0 | 8 | 8 |
| Organic social | 0 | 3 | 5 |
| 0 | 2 | 2 | |
| Other referral | 0 | 2 | 6 |
| Paid ads | 2 | 78 | 104 |
| Other | 0 | 2 | 3 |
| 🔧 Internal / QA traffic | 0 | 1 | 1 |
| ⚠ CAN'T SEE — attribution-dark | 10 | 23 | 33 |
FLEET_CHARTERS.json; every Now value on this page is read from its source file at render time, so this tab cannot quote a stale charter.| Fleet | Now | By 2026-10-29 | Est. cash/mo | Measurement | Open |
|---|---|---|---|---|---|
🎯 Funnel / CRO biggest rowtma-funnel-cro-lead | annual 48.4% · $92.4/sale | Annual share ≥40% of web new sales ⇒ cash/sale ≈$82 (0.4×$157 + 0.6×$32.97 blended non-annual). The new $97-annual paywall shipped 31 Jul to do exactly this; the kill-switch (14-day RPV < $6.04) protects the downside. | +$2,100–2,200 | LIVE | — |
🛟 Retention & CStma-retention-lead | 82 cancel-ends MTD · $1,184 MRR lost | Cancel-ends ≤85/month (from 110). Estimate is the day-90 run-rate of newly-retained MRR, gross basis, stated honestly: ~25 fewer cancels × ~$12.81 avg MRR, compounding, net of Stripe ~0.90 ratio and ~13%/mo re-churn of saved membe… | +$600–800 | LIVE | 8 |
🔍 Search (SEO · AEO · GEO)tma-seo-growth-owner | 70 owned-content leads/28d (artefact until ~12 Aug) | RE-BASELINE 26 Aug on the first clean 28d attribution window (7d pace already ≈40/28d — a 40 target would self-complete by window-healing alone). Then ≥80 owned-content leads/28d by 29 Oct. | +$200–350 | LIVE·healing | 53 |
💰 Paid Adstma-growth-cmo | $1,071 vs $3,000 cap · CPT UNCOMPUTABLE | WIRING GATED. Wiring by 22 Aug: CPT genuinely measurable (analytics brief tasks 5 AppsFlyer · 9 server-side purchase+dedup · 8 GA4→Ads import · 31 attribution in Stripe metadata). THEN: spend ≤$3,000/mo at CPT <$20. No scaling before the… | $0 · the gate | DARK on CPT | 14 |
📧 Email / Lifecycletma-email-lead | revenue DARK · 2,445 engaged-reachable · bounce 0.03% · unsub 0.29% (cumulative, plan sequences) | WIRING Wiring by 29 Aug: email→revenue attribution live (brief tasks 17 last-funnel-step export + 27 email UTM→Stripe persist), AND the unsub gate cleared (3,450 held sends released only after a passing test batch ≤0.5%). THEN: measurabl… | +$500–900 | DARK on revenue | 4 |
📱 Organic Social (TMA)tma-social-growth-owner | 486 organic web sessions/28d vs 1,200 target · 7 leads (real, tiny — a lead target is re-set after 4 clean weeks) · saves/reach 0.29% vs 1.00% ⚠️ the distribution lever · (128 PAID leads moved to P1 — they were never organic) | Win back DISTRIBUTION first — a lead goal on 446 sessions is arithmetic that cannot close. Primary: organic social WEB sessions ≥1,200/28d by 29 Oct (2.7× today) driven by saves-per-reach ≥1.0% (now 0.41%, was 0.83% in June — this… | $0 · the gate | REBASELINED | 3 |
🤝 Partnerships & Affiliates (both directions)tma-partnerships-growth-owner | ≈£275/mo net (bank-traced 25 Jul — STATIC, no live source) | Hold merchant side ≈£275/mo net with zero build hours; complete the publisher harvest (~4h: verify tmapicks-20 earnings, negotiate Kensui) and STOP per the standing verdict; outreach: ≥6 live placements from the listicle campaign. | +$100–150 | STATIC | — |
📊 Analytics / Data Truthtma-analytics-lead | dark share 24.2%/28d | WIRING Dark share ≤15% by 29 Oct. Route: email UTM audit daily (854 links fixed 31 Jul — judge ~28 Aug, attribution set at lead time) + magnet UTM wiring (P17/AGA-31) + the YouTube lane diagnosis + checkout UTM forwarding (NIC-16). Also:… | $0 · the gate | LIVE | — |
| Fleet | The gate it holds | Reaches Aga only when |
|---|---|---|
| 🏛️ Product Council | dual-axis composite + ranked backlog + exit-readiness verdict | any deal-blocking finding; composite falling |
| 🎨 Design / UX + Pixel Gate | TWO consecutive fresh-instance SHIPs, vision-only; DESIGN_LAW violations = BLOCKED | her eyeball gate before anything ships to 'ready' |
| 🧪 QA Fleet | P0/P1 finding counts; release-gate checklist | any P0 on a member-facing surface |
| 🏋️ Master Trainer Panel | training-science correctness sign-off on progression/engine changes | safety findings only |
| 🧘 Mobility | mobility doctrine compliance | build-vs-doctrine gaps needing Nic |
| 🖼️ Static Ads | AD_SCORING_RUBRIC + FINAL ship verdict | creative approval before spend |
| 🟡 Forge/Ath brand + YouTube | blind-judge SHIP gates (hook ≥58/70, editorial ≥75/100) | READY_FOR_AGA scripts only; channel is PRE-LAUNCH (3 subs) |
| 📤 Outreach | tma-outreach-gate PASS on EVERY send (zero bad sends); campaigns ≤50; sends as Jesse Head Coach | campaign approval before dispatch; any partner-list conflict |
| 📺 YouTube (TMA main channel) | VIEWS, not leads. Measured 3 Aug 2026: 15,032 views/28d (~537/day) → ~199 REAL site sessions/28d at a 1.3% view→click rate, which is NORMAL (0.5–2% typical). Nothing is broken — the channel is small. A ≥30 leads/28d target would need ~3× the channel views, which ~2 long-forms/mo cannot deliver, so it was arithmetically unreachable and is withdrawn. 🔴 Sessions MUST come from tools/youtube-analytics/yt_traffic_truth.py, never a raw GA4 count: bulk description edits trigger ~4 link-validator hits per video, which manufactured ~3,700 fake 'sessions' in late July and were briefed as real traffic. Levers, both unblocked and near-zero Aga attention: retention recuts (avg view 34.5%) and new-upload packaging. Packaging REFRESH is measured as a rounding error here — median CTR 3.44% is healthy and all 43 candidates are worth +5,584 views LIFETIME. 👁️ watched, not graded: 258 real sessions/28d → 6 leads · 2.3% (crawler-excluded; raw GA4 is mostly our own link-validators) | views/28d falling 2 consecutive weeks · YPP policy risk · 🔔 TRIPWIRE: views/28d ≥45,000 (~3× today) — at that scale the same normal click rate produces material leads, so the lane RE-ENTERS the revenue tier and a leads goal becomes reachable. Re-open this charter then. |
| 📋 Project Management | no row is called done until the LIVE artefact says so; every open row has exactly one valid owner, an honest estimate and a next action | a row stalled past its phase with no owner action; a done-claim the verifier reopened; a queue closed without a resolution |
docs/company/GOVERNANCE/FLEET_CHARTERS.json → rendered FLEET_CHARTERS.md. Generated by tools/ops/aga_console_sync.py · 2026-08-12 14:31 MDT. Never hand-edit inside the KPI90 markers — edit the charter and re-run.MESSAGE-TO-NIC-FUNNEL-CORRECTION-2026-07-16.
✅ R1 Wave A SENT 20 Jul — 24/24 delivered, 0 failures (3 variants: default · price-acknowledged · known-reason; plain-text 1:1 from Jesse via hello@, NOT AC). Replies to hello@ ARE the save mechanism. → Aga: answer hello@ replies <24h · 48h scorecard due 22 Jul (check_saves.py) → then Segments B/C (~54 of ~84 auto-skip, reason already known). Pending: T-30 watcher not yet scheduled · breakage alerting not wired (state: tools/retention/R1_SYSTEM_STATE.md).
T2 (RC→Amplitude) — live status: ✅ LANDED — auto-check 12 Aug: 8 rc_* events visible (rc_cancellation, rc_expiration, rc_initial_purchase, rc_product_change, rc_renewal…). Next: reconcile volume ±5% vs RC, then steward flips all docs green. The gap: some lifecycle toggles may still be off — reconcile the live list above against RC (~6 renewals/day expected). Most likely a few per-event toggles in RevenueCat's Amplitude integration aren't all enabled yet.→ Nic (5 min): RevenueCat → Integrations → Amplitude → enable the remaining lifecycle events (renewal, trial started/converted/cancelled, expiration, billing issue, product change, uncancellation). Then reconcile ±5% vs RC (194 subs, ~6 renewals/day). Then merge purchase + rc_initial_purchase in Amplitude — but exclude the broken af_purchase (2 fires vs 194 subs) from the merged view.
New-sub acquisition collapsed during the sale: June created 61 new subs (+$1,193 MRR); July is pacing 7 in 8 days. Some of that is the paid freeze — and some is the lifetime offer cannibalising would-be subscribers (cash now, no MRR later).→ When the cart closes ~12 Jul, watch the new-subs line recover on the toggle; if it doesn't by next weekly, that's the topic.
Recurring intake $880/wk vs $3,951/wk total paid spend (Meta + Google). The engine spent 4.5× its recurring intake last week; promo cash masked it.→ Hold the Mo.1 ramp ≈$40/day ($3K/mo is a ceiling, not a target — SoT amended 9 Jul). No scale-up before lead→trial reads.
Revenue cliff on ~12 Jul. 85% of last week's revenue was the lifetime cart — it closes in days. Next week's revenue line will drop; that is expected, not a new crisis.→ Pre-agree it now so nobody panics; watch the recurring line instead.
Google campaign state (9 Jul, corrected — do not quote past spend as present): the earlier over-cap UAC configuration was Nic's mistake and he corrected it same-day. Current 3d actuals ≈ $68/day total: July-4 remarketing ~$22/day (ends with the cart ~12 Jul) · UK Play ~$10 · US Play ~$5 · AP/SA/LATAM throttled to ~$5/day each (~$21 combined) · PMax ~$4 · the 5 new Quiz Funnel Search campaigns (Branded £3 · Non-Br £10 · Comp-Br £15 · Comp-Non £12 · PMax £7.50, ~375 kw) show ~$0 spend so far. Fleet recommendation pending Aga: move the AP/SA/LATAM ~$21/day to $0 (payment-incapable geos) and hold Search inside the Mo.1 ramp (≈$40/day) once remarketing ends; conversion import (GA4→Ads) before any Search scale.→ Weekly review: confirm Search scope vs SoT "Category Search only" + wire the conversion feed first (Nic message: MESSAGE-TO-NIC-GOOGLE-RETARGETING-2026-07-09.md).
Top-of-funnel is nearly off: spend collapsed to ~$30/day total (freeze real ✓) and leads followed — 6 on Jul 8, ~3/day from Meta.→ Expected during the freeze; the new quiz campaigns are now the main faucet. If leads don't recover to >15/day within a week of Google ramping, that's the weekly topic.
The web funnel is now readable PER SCREEN — and the leak is exactly one screen: S01→S02 loses 51% (700 users/month). Everything after is healthy: assessment keeps 92%, the email screen converts ~89%, 375/month reach the paywall. Leak #2: paywall→card entry ~15–20%.→ Screen-1 A/B (#17, goal-question-first) is now the top web lever with an exact baseline. Full per-screen funnel: Web tab.
Two session counts disagree ~12× (GA4 property-wide ~15.6k/wk vs quiz-scoped 3,969/30d).→ Weekly review: pin ONE denominator (quiz landing sessions) before any funnel % is quoted.
Meta retargeting is the one paid tactic earning its keep — ROAS 2.4–4.8× on 14 purchases (small n).→ Keep it capped & always-on; verify with a holdout before believing the multiple.
| Channel | 5 Aug | 6 | 7 | 8 | 9 | 10 | 11 | 12 (today, partial) | 7-day | Status |
|---|---|---|---|---|---|---|---|---|---|---|
| Meta (FB/IG) | $37 | $56 | $48 | $43 | $62 | $48 | $56 | $26 | $350 | LIVE |
| Google Ads (direct API) | $52 | $47 | $49 | $50 | $40 | $41 | $43 | $36 | $322 | LIVE |
| TikTok | not running | $0 | OFF | |||||||
| Apple Search Ads | not running — gated (Phase 4) | $0 | OFF | |||||||
| Measured total | $89 | $103 | $98 | $93 | $101 | $89 | $99 | $62 | $672 | |
| Source | 5 Aug | 6 | 7 | 8 | 9 | 10 | 11 | 12 (today, partial) | 8-day | Status |
|---|---|---|---|---|---|---|---|---|---|---|
| Stripe · account 1 (web — subscriptions + promo carts) | $320 | $279 | $147 | $491 | $376 | $456 | $100 | $0 | $2,168 | LIVE |
| Stripe · account 2 (Thrivecart recovery cart) | $0 | $0 | $0 | $0 | $0 | $0 | $0 | $0 | $0 | LIVE |
| App · iOS (App Store · RevenueCat) | $154 | $199 | $150 | $128 | $154 | $-9 | $50 | $194 | $1,021 | LIVE |
| App · Google Play (RevenueCat) | $0 | $0 | $29 | $0 | $0 | $9 | $124 | $0 | $162 | LIVE |
| Total, gross — ALL rails | $474 | $478 | $326 | $619 | $530 | $456 | $274 | $194 | $3,351 |
generate_lead
81.2% of capture56add_to_cart — GA4 only, NEVER revenue truth; the money table is Stripe
9.4% of paywall5Step % = share kept from the previous screen.
Values over 100% are branch re-entries, not growth. Assessment *_Q2 screens are conditional
routing and are excluded from the spine — they are not abandonment.
| Version | Landed | Q1 | Keep | Capture | Lead | Paywall | Paywall / lander |
|---|---|---|---|---|---|---|---|
| /2026-v2-2/ v2-2 Time Horizon (old primary) | 3,959 | 1,832 | 46.3% | 1,139 | 934 | 914 | 23.1% |
| /2026/ Control (Long Form) | 1,663 | 716 | 43.1% | 450 | 326 | 313 | 18.8% |
| /2026-v2-3/ v2-3 (legacy) | 1,289 | 538 | 41.7% | 352 | 287 | 274 | 21.3% |
| /2026-quiz-checkout-v1/ v1 $97 checkout (new primary) | 676 | 334 | 49.4% | 171 | 132 | 124 | 18.3% |
| Rail | Starts | Paid | Cash | AOV | Aug paid | Aug cash |
|---|---|---|---|---|---|---|
| quiz_checkout(v1) | 16 | 15 | $1,298 | $86.56 | 15 | $1,298 |
| register(v2-2 rail) | 143 | 90 | $2,348 | $26.09 | 0 | $0 |
| register(Control rail) | 57 | 50 | $4,163 | $83.27 | 0 | $0 |
| checkouts-v3 | 73 | 1 | $25 | $24.97 | 0 | $0 |
| other | 7 | 5 | $207 | $41.49 | 0 | $0 |
🔴 The new quiz
(quiz_checkout(v1)) is the ONLY rail selling in August. Quiz-conversion figures from
before 1 Aug describe a different quiz that never sold — never blend the eras.
| First-time downloads — the acquisition number | 27 | 4.5/day ≈ 135/mo |
| All install events (incl. app UPDATES, restores, redownloads) | 55 | 🔴 never quote this as acquisition |
| Deletes — fresh-install bounces | 14 | delete rows carrying a recent App Download Date |
| Deletes — older installs cleaned up | 15 | no download date ⇒ not a fresh bounce |
| Fresh-bounce rate (fresh deletes ÷ first-time downloads) | 51.9% | ours ran 45–70% by month in 2026 [ESTIMATE]; category-typical 40–60% [UNVERIFIED benchmark] |
Where installs come from: App Store search 96.3% · App referrer 3.7%
🔴 Apple's "App Store search" bucket contains BOTH organic
search and Apple Search Ads placements — it cannot separate paid from organic. Never quote it as
"organic".
74.3% install without ever opening the product page ⇒ the search-result mini-listing (icon · title · subtitle · rating · first screenshot) is the real conversion surface. The product page only touches the other 25.7%.
Store discovery — its OWN window
2026-07-29 → 2026-07-31 (3d), which is NOT the
installs window above:
1,996 impressions → 92 page views
(4.61%) · 665.3 impressions/day.
🔴 Install-conversion is deliberately NOT shown.
NOT COMPUTED — downloads cover 2026-07-31…2026-08-01 but impressions cover 2026-07-29…2026-07-31. A conversion rate across two different windows is not a rate.
This block previously carried no window at all and the cockpit published 333 impressions/day —
exactly half the real figure — because every consumer assumed it shared the 6-day installs window.
Found and fixed 11 Aug 2026.
| Month | Device installs | From countries that never bought |
|---|---|---|
| 2026-01 | 522 | 36.4% |
| 2026-02 | 457 | 33.9% |
| 2026-03 | 519 | 36.8% |
| 2026-04 | 473 | 48.6% |
| 2026-05 | 417 | 37.6% |
| 2026-06 | 4,109 | 69.0% |
| 2026-07 | 5,221 | 67.2% |
| 2026-08 | 48 | 29.2% |
| Step (unique users) | 🍎 iOS | 🤖 Android |
|---|---|---|
| Onboarding started | 364 | 268 |
| Onboarding completed | 317 (87.1%) | 239 (89.2%) |
| First workout started | 316 (99.7%) | 95 (39.7%) |
| Workout completed | 254 (80.4%) | 61 (64.2%) |
| Pricing page seen | 229 | 134 |
| Trial started | 43 (18.8%) | 10 (7.5%) |
🔴 app_removed is deliberately absent from
this table: Apple does not report deletions to Firebase, so iOS reads 0 by instrumentation, not by behaviour.
iOS deletion truth is App Store Connect only — the block at the top of this tab. Comparing the two platforms on
GA4 removals would be comparing a real number with a missing one.
🔴 GA4's app purchase event fires on
INITIATION — purchase-users (102) exceed trial-users (52), which is impossible as a funnel.
Never quote GA4 app purchases as revenue. Money truth is the stores + Stripe, below.
Also live: app_installed 1,366 · app_removed 372 ·
comeback_after_7_days 282 · assessment started only 65
(it is paywalled — that number is not a defect).
| iOS NET — last complete month (2026-07) | £2,365.68 |
| Play NET — last complete month (2026-07) | £730.65 |
| App rail MRR (RevenueCat, live) | $3,405 |
| App subscribers | 240 |
Both stores report NET. The iOS RevenueCat-vs-Apple ratio near 1.0 is CORRECT — two net figures agreeing. Play via RevenueCat is GROSS (ratio ~0.73). Never "fix" the iOS ratio to 0.85, and never pull a store month mid-month. No store revenue exists before May 2026.
| First-time downloads — the only acquisition number | 27 | 4.5/day ≈ 135/mo |
All Install events — incl. app UPDATES |
55 | 🔴 the number the old cockpit line quoted |
| Deletes — fresh bounces / older cleanup | 14 / 15 | split by whether the row carries an App Download Date |
| Fresh-bounce rate | 51.9% | ours ran 45–70% by month in 2026 [ESTIMATE] · category-typical 40–60% [UNVERIFIED benchmark] — flag only above that |
| Baseline first-time downloads | ≈119/mo | 11 non-sale months, mean ≈4/day, range 85–158 — 🔴 "130–160/mo" is RETIRED (it described only the best months) |
| Search share of first-time downloads | 96.3% | the store is a search engine we rank in |
What survives and matters: ~96.3% of first-time downloads originate in App Store search, and ~74% install without ever opening the product page — so the search card (icon · title · subtitle · rating · first 3 screenshots) is the conversion surface, not the description. 🔴 Apple's "search" bucket contains BOTH organic and Apple Search Ads placements and cannot separate them; the organic keyword split is NOT KNOWABLE from ASC at all.
| Thing | State | Detail | Owner | Rests on |
|---|---|---|---|---|
| Promotional text on the LIVE version | 🔴 NOT DONE — ships TODAY, zero review | live 1.70.71 promotionalText is EMPTY — and this is the ONE field that ships with NO App Review; the copy is already written on 1.70.72 | Aga · 5 min in ASC | CHECKED — data_asc_listing.json |
| Title + subtitle | 🔴 NOT FIXED BY 1.70.72 | both the live and staged appInfos still read Movement Athlete / Calisthenics & Gymnastics. Title/subtitle live in a SEPARATE record from the version — the 30 Jul fill guide never covered it. 🔑 App Information submits WITHOUT a build. | Aga GO on strings → submit | CHECKED — data_asc_listing.json appInfos |
| Keyword field | STAGED, not live | live: ab,butt,beachbody,tracker,gym,exercise,diet,planner,trainer,butt…staged 1.70.72: bodyweight,workout,plan,progression,home,beginner,strength,mobil… | blocked on the build | CHECKED — data_asc_listing.json |
| Support URL fix + description | STAGED on 1.70.72 | dead help.themovementathlete.com → intercom.help/the-movement-athlete/en/; a dead support URL is a known App Review rejection reason | blocked on the build | CHECKED — APP_STORE_1.70.72_FILL_GUIDE.md |
| 🔴 A BUILD ATTACHED TO 1.70.72 | THE SINGLE BLOCKING DEPENDENCY | 1.70.72 has sat in Prepare-for-Submission since 30 Jul with no build, so 'Add for Review' is greyed out. Every staged fix above waits on this one act — and so does any non-branded ASA test, because Search-Match targets off listing metadata. | app team (via Nic) | CHECKED — ASC state + fill guide |
| Screenshots + app preview video | PARKED — needs a designer (AGA-26) | the search card is ~74% of first-time downloads and screenshot 2 ships “STRENGHT” in 40pt type; 8 legacy iPhone-8-era frames, zero iPad, retired cyan captions, 0 of 3 App Previews used. Interim with existing assets: pull the typo frame (7 good beats 8 with a typo) and lead with the assessment frame. | designer brief written → Aga unparks | CHECKED — ASO_MASTER §2 + queue |
| In-app review prompt (ratings velocity) | Jira raised (AGA-39) · in-app ship UNVERIFIED | 297 iOS ratings vs Thenx 12,273 — ratings volume is the ranking ceiling and the only lever that COMPOUNDS. Modelled 8–20 new reviews/mo once live. Verify: is SKStoreReviewController actually called, on what event, and does it fire in a production build? | 3 questions for the app team | UNVERIFIED — ticket raised, ship not confirmed |
| Spend / impressions / taps | $133.30 · 7,084 · 245 | 3 campaigns RUNNING — all branded · 7 paused · 🔴 never divide this spend by the revenue below — different 30d windows; the ROAS file carries its own matched cost ($137.27) for exactly that reason |
| Tap-installs vs VIEW-installs | 111 vs 222 | 🔴 two-thirds are view-through — the self-attribution mechanism |
| ASA-attributed revenue / purchasers | $2,077 · 7 | AppsFlyer, store GROSS · n is tiny (±1 buyer ≈ ±14%) · 1 of those buyers sits on a paused non-branded campaign |
| Organic, same window | $5,946 · 24 purchasers | 447 installs |
| Channel | 5 Aug | 6 | 7 | 8 | 9 | 10 | 11 | 12 (today, partial) | 7-day | Status |
|---|---|---|---|---|---|---|---|---|---|---|
| Meta (FB/IG) | $37 | $56 | $48 | $43 | $62 | $48 | $56 | $26 | $350 | LIVE |
| Google Ads (direct API) | $52 | $47 | $49 | $50 | $40 | $41 | $43 | $36 | $322 | LIVE |
| TikTok | not running | $0 | OFF | |||||||
| Apple Search Ads | not running — gated (Phase 4) | $0 | OFF | |||||||
| Measured total | $89 | $103 | $98 | $93 | $101 | $89 | $99 | $62 | $672 | |
| Source | 5 Aug | 6 | 7 | 8 | 9 | 10 | 11 | 12 (today, partial) | 8-day | Status |
|---|---|---|---|---|---|---|---|---|---|---|
| Stripe · account 1 (web — subscriptions + promo carts) | $320 | $279 | $147 | $491 | $376 | $456 | $100 | $0 | $2,168 | LIVE |
| Stripe · account 2 (Thrivecart recovery cart) | $0 | $0 | $0 | $0 | $0 | $0 | $0 | $0 | $0 | LIVE |
| App · iOS (App Store · RevenueCat) | $154 | $199 | $150 | $128 | $154 | $-9 | $50 | $194 | $1,021 | LIVE |
| App · Google Play (RevenueCat) | $0 | $0 | $29 | $0 | $0 | $9 | $124 | $0 | $162 | LIVE |
| Total, gross — ALL rails | $474 | $478 | $326 | $619 | $530 | $456 | $274 | $194 | $3,351 |
| Source | Mar 26 | Apr 26 | May 26 | Jun 26 | Jul 26 | Aug 26 * | 6-mo |
|---|---|---|---|---|---|---|---|
| Stripe · account 1 (web) | $7,320 | $7,903 | $10,532 | $5,356 | $21,426 | $2,585 | $55,122 |
| Stripe · account 2 (recovery cart) | $81 | $0 | $0 | $556 | $1,345 | $0 | $1,982 |
| App · iOS (RevenueCat) | $3,123 | $3,936 | $3,586 | $3,146 | $3,161 | — | $16,952 |
| App · Google Play (RevenueCat) | — | $134 | $155 | $506 | $976 | — | $1,771 |
| Total, gross — ALL rails | $10,524 | $11,973 | $14,273 | $9,564 | $26,908 | $2,585 | $75,827 |
| Automation | Tier | Sends | CTR | Unsub | Health |
|---|---|---|---|---|---|
| 992 TMA | Welcome Sequence AFTER APPSIGNUP AB TEST A | C | 205 | 1.95% | 4.39% | BREACH |
| 988 TMA | Welcome Sequence AFTER APPSIGNUP | C | 2,311 | 1.34% | 1.69% | BREACH |
| 975 Onboarding | After The Purchase | C | 1,810 | 0.83% | 0.99% | OVER-HARDSTOP |
| 991 NEW Re-Engagement - For people who stopped opening | C | 915 | 0.44% | 0.66% | OVER-HARDSTOP |
| 967 TMA | Welcome Sequence After LEAD MAGNET | B | 4,144 | 0.36% | 0.51% | OVER-HARDSTOP |
| 973 Nurture Sequence 1 NEW COPY FROM CLAUDE | A | 40,287 | 0.37% | 0.33% | WATCH |
| 758 Nurture Sequence 2 | A | 30,991 | 0.21% | 0.19% | OK |
| 751 Nurture Sequence 1 | B | 7,398 | 0.16% | 0.22% | OK |
| 750 TMA | Welcome Sequence After Quiz | C | 112 | 0.0% | 0.0% | OK |
Six more automations sent under 350 in the window (986 · 760 · 976 · 977 · 978 · 990) and are DARK by volume — real, but not rankable.
| Rail (USD, NET) | Jul 26 — full | Aug 26 — to date |
|---|---|---|
| Stripe · web | $21,426 | $2,585 |
| Stripe · recovery cart | $1,345 | $0 |
| App · iOS | $3,161 | — |
| App · Google Play | $976 | — |
| Total | $26,908 | $2,585 |
The app rows are blank for the current month by design — a store month is never read before it closes. Jul 26's total includes the lifetime sale; strip it before calling it a baseline.
| Month | NET, all rails |
|---|---|
| Mar 26 | $10,524 |
| Apr 26 | $11,973 |
| May 26 | $14,273 |
| Jun 26 | $9,564 |
| Jul 26 | $26,908 |
| Aug 26 | $2,585 |
Current MRR $11,176 · web $7,771 / 538 subs · app $3,405 / 240 subs · cancel queue 106 worth $1,487/mo.
Each pane carries a machine-written as-of date taken from the
data file it renders from, and cockpit_freshness_check.py fails the daily run if any tab is
over its age budget or any "go deeper" link 404s. If a tab is on screen, it is inside budget — you
no longer have to wonder whether a number is current, which is the only reason this page is worth trusting.
Every channel now links to its deeper analysis from its own tab —
the "Go deeper" rail. For a question the docs do not answer, run /growth-fleet in Claude:
plan first, then execute. Decisions get recorded in the decision ledger so that in six weeks we can score
whether the call was right, not just whether it felt right.
| Campaign | Spend | Clicks | Conv | What the conversions actually are |
|---|---|---|---|---|
| Quiz Funnel | Competitors Branded | US | £103.03 | 58 | 20 | all lead forms — zero purchases |
| Quiz Funnel | Competitive Non-Branded | US | £83.67 | 16 | 0 | nothing at all |
| Quiz Funnel | Non-Branded | US | £83.45 | 22 | 2 | lead forms only |
| Quiz Funnel | PMax | US | £45.57 | — | 44 | 21 lead forms · 3 purchases (£0 value passed) |
| Quiz Funnel | Branded | US | £14.53 | — | 10 | 2 purchases £72.05 + 1 phantom £297 (see below) |
| Total | £330.25 | 87 | 6 PURCHASE · 73 lead form · 2 install · 6 session |
QLG | ADV+ ON | US | Variant 2 | ACTIVE | OUTCOME_LEADS |
MOF - Lead Form 🔴 | ACTIVE | OUTCOME_LEADS — dead sync since 1 Dec 2025 |
QLG | ADV+ OFF | MC | Control | ACTIVE | OUTCOME_LEADS |
🔴 NEVER SUM META ACTION TYPES. Meta reports one conversion under seven labels
(purchase / omni_purchase / offsite_conversion.fb_pixel_purchase / onsite_web_purchase / …) with identical counts. Summing them made every Meta number
exactly DOUBLE until it was caught on 11 Aug 2026. Take exactly one. lead is the aggregate; offsite_conversion.fb_pixel_lead is a subset of it.
docs/company/ANALYTICS/NATIVE-APP/NATIVE_APP_DEEP_ANALYSIS_2026-07-20.md.| Cohort era | D7 | D28 | n | read |
|---|---|---|---|---|
| BASELINE — Feb–Apr 2026 (pre-promo) | 20% | 6% | 1,378 | the truth |
| Long-run check — early 2025 | 19% | 6% | 1,259 | stable a full year |
| May 50%-off | 26% | 7% | 807 | HEALTHIER than baseline (attracts intent) |
| July lifetime (4–12) | 11% | 0% | 4,282 | TOXIC — 15× volume, ~0% D28, cheap-geo dump |
| status | question | how to settle | owner · effort |
|---|---|---|---|
| P0 | Jun–Jul adds trough: Google channel-shift or REAL acquisition collapse? (paid adds 61/56 vs ~100 baseline) | RevenueCat Charts → New Subscribers by Store, May–Jul 2026 (or Play Console financial report). June doc's '−8% blended' rests on unverified Google CSVs; RC's 217 total subs makes full absorption doubtful | Aga · 15 min |
| OPEN | Does RevenueCat ingest Stripe, or is it IAP-only? (decides if combined MRR ≈$13.5K gross is double-counting) | RC dashboard → project integrations page | Aga · 5 min |
| OPEN | PayPal rail — still live? (no PAYPAL_* key in .env.local; rail fully DARK) | If active: create read-only REST app in PayPal dashboard, add key | Aga/Nic · 10 min |
| OPTIONAL | Annual renewal ground truth (≈32% is an approximation via current_period_start) | Count 2nd paid subscription_cycle invoice ÷ cohort that paid a first annual (Claude, Stripe API) | Claude · 20 min |
| OPTIONAL | July lifetime '53% new customers' = upper bound (guest-checkout email blind spot) | Match the 62 charge emails vs app account email list — feeds the CFO/CXO lifetime-cadence decision | Nic+Claude · 30 min |
| OPTIONAL | Did the March bug wave hit IAP/free users? (unprovable on Stripe; RC pre-May contaminated) | RC Charts churn export Feb–Apr 2026 — treat with epoch caveat | Aga · 15 min |
| # | task | owner · size | status |
|---|---|---|---|
| P-990 | 990 Monthly→Annual Upgrade Engine (Jul 2026) went LIVE 3 Aug — READ THE RESULT at 14 days (by 17 Aug 2026). The one number 980 never captured: CONVERSIONS. 980 pulled 26.7–29.2% open and 3.44–4.93% click on 3,310 sends but zero conversion tags were ever written, so its payoff is still unknown. This run has the checkout link + UTMs (utm_campaign=990-monthly-annual-upgrade-engine-jul-2026) so clicks reach Stripe metadata. Decision at 14d: keep $157 as-is, or move the $129 Stage-3 rate earlier. Live counters in the ⚙️ band above. | Aga (read) · 10 min | OPEN |
| P0 | Settle the June question — RC 'New Subscribers by Store' May–Jul export. BLOCKS the CEO priority call (acquisition vs retention) | Aga · 15 min | OPEN |
| P1 | Annual pre-anniversary renewal sequence T-45/30/7 + year-in-review recap — via /email-fleet (renewal = known lifecycle coverage GAP). Honest size +$170–450/mo | Claude fleet → Aga approves · 2–3 h | OPEN |
| P2 | Stripe-portal cancel intercept: point in-app cancel at portal login (SAVE30 loaded). PRE-CHECK first: Nic confirms webhooks sync portal un-cancels to app entitlements | Nic · 1–2 h | OPEN |
| P3a | R1 voluntary saves: Seg A sent 20 Jul (30 emails, 0 saves yet — T-30 offers arm at expiry, too early to judge). Seg B/C question-send awaiting authorization | Aga authorizes · 30 min | WAITING-AGA |
| P3b | 976 Cancelled Win-Back — reason-matched copy through /email-fleet gates (6/7 current CTAs fail QA); reason tags already applied daily by machine | Claude fleet → Aga · 2–3 h | OPEN |
| P4 | August clean read: first unpromo'd funnel month. Tripwire = weekly PAID annual adds ≥20 (July true paid adds 56, not 64). No panic-reads of Jun–Jul before then | Claude monitors · — | SCHEDULED |
| P5 | D1 activation cliff (D0→D1 ~10%) — durable product fix in RN app; route via CPO → Nic | CPO/Nic · large | BACKLOG |
| GOV | Lifetime cadence decision: each event ≈$20–30K instant cash vs MRR-multiple hollowing ('53% new' = ceiling). Board-level dial | Aga + CFO/CXO · board item | OPEN |
| FIX | Cockpit lost its RETTODAY markers (today-line now renders inside this block instead). Restore markers only if the one-line Today strip is wanted back | cockpit owner · 5 min | NOTED |
data/customer-analytics/CHURN_BASELINE_PRE_MARCH_2026_ANALYSIS_2026-07-24.md (Parts 1-3) · baseline: Jan 2025-Feb 2026, promo windows excluded: ~100 adds/mo, ~130 ends/mo, 13.5%/mo blended churn, quick ratio 0.76tools/retention/R1_SYSTEM_STATE.mdquiz_abandon_sync.py · owner: tma-quiz-checkout-abandon-owner · register §2.6failed_purchase_lane.py · register §2.5 ↗All commands run from the repo root (/Volumes/T9/projects/the-movement-athlete). Emails go via the same Gmail pipe as your 7am cockpit email.
python3 tools/retention/send_r1.py --segment Apython3 tools/retention/send_r1.py --segment A --live --limit 1 --to hello@themovementathlete.compython3 tools/retention/send_r1.py --segment A --livepython3 tools/retention/check_saves.py — this is your no-discount save rate, the number that decides whether discounting is even worth it.--segment B --live then --segment C --livepython3 tools/retention/check_saves.pySAVE30 (30% off, duration once) to their existing subscription. Never send them a checkout link — it makes a 2nd subscription and double-bills them.The queue is mostly not urgent. 85 annual, 20 quarterly, 7 monthly — median 110 days of paid access left; only 31 end within 30 days. So B and C get no offer, just one question ("what made you turn renewal off?") — selling to someone paid through next spring reads as tone-deaf, and lapsed annual subs reactivate at only ~5% (RevenueCat). Their save offer fires automatically at T-30 via pre_expiry_watch.py.
Not 70% off — 30%. Your exit survey (3,704 price responses): 64.8% would pay nothing; of those who named a price, 78% said ~$9.97/mo ≈ a 24% discount. 70% gives away 3× what's needed and dilutes the MRR a buyer underwrites. Ladder: split the annual lump to monthly (no discount) → 30% next renewal only → 50% hardship on reply. Churnkey's own data: ~89% of discount-saved still churn within a year — so cap the depth.
Pause rejected, with data. Only 7 of 676 web subs are monthly (the queue has 7) — pause is meaningless to an annual subscriber. And Stripe removed self-serve pause from the Customer Portal (no subscription_pause feature exists). Handle pause manually on reply; revisit only if the reason data shows real demand. No app release needed for any of this.
Your Stripe portal is already live — the un-cancel link (billing.stripe.com/p/login/…) is in every R1 email, so members can turn renewal back on themselves. It does NOT touch your app backend — it writes to Stripe only; the app learns via webhooks (Nic's side). Safe for R1: renewing changes nobody's access today (they're all active + prepaid).
Your cancel button is custom, not the Stripe portal — which is why only 7 of 112 cancels carry a reason (those 7 used the portal). Ask Nic to either route the cancel button through the Stripe portal, or write the cancel reason into Stripe cancellation_details via the API. That's what makes reason-capture + the auto save-offer work for everyone, not just this backfill. Leave "plan switching" OFF in Stripe until Nic confirms he consumes webhooks (it changes live subs and would desync entitlements).
tools/retention/AGA_RUNBOOK.md · strategy: Retention Plan ↗tools/ops/aga_console_sync.py v2 · 2026-08-12 14:31 MDT · merged from every fleet queue + the analytics brief. Never hand-edit inside the CONSOLE markers — edit the fleet's own status file (or the brief) and re-run.AGA-66 · FLEET-20FLEET_CHARTERS.json; every "now" value below is read from its source file at render time — the scoreboard cannot quote a stale charter. DARK means the fleet's first goal is lighting the metric, per Aga's ruling.| Fleet | Now (live) | 90-day goal | By | Est. cash/mo | Tag | Open |
|---|---|---|---|---|---|---|
🎯 Funnel / CROtma-funnel-cro-lead | annual 48.4% · $92.4/sale | CASH — annual_share_pct >= 40 | 2026-10-29 | $2,100–2,200 | LIVE | — |
🛟 Retention & CStma-retention-lead | 82 cancel-ends MTD · $1,184 MRR lost | CASH — cancel_end_mtd <= 85 | 2026-10-29 | $600–800 | LIVE | 8 |
🔍 Search (SEO · AEO · GEO)tma-seo-growth-owner | 70 owned-content leads/28d (artefact until ~12 Aug) | LEAD — content lanes >= 80 per 28d (post-rebaseline) | 2026-10-29 | $200–350 | LIVE·healing | 53 |
💰 Paid Adstma-growth-cmo | $1,071 vs $3,000 cap · CPT UNCOMPUTABLE | WIRING — CPT measurable by 2026-08-22, then CPT < $20 within cap | 2026-08-22 | $0 (gate) | DARK on CPT | 14 |
📧 Email / Lifecycletma-email-lead | revenue DARK · 2,445 engaged-reachable · bounce 0.03% · unsub 0.29% (cumulative, plan sequences) | WIRING — email revenue attributable by 2026-08-29; growth target set 1 Oct | 2026-08-29 | $500–900 | DARK on revenue | 4 |
📱 Organic Social (TMA)tma-social-growth-owner | 486 organic web sessions/28d vs 1,200 target · 7 leads (real, tiny — a lead target is re-set after 4 clean weeks) · saves/reach 0.29% vs 1.00% ⚠️ the distribution lever · (128 PAID leads moved to P1 — they were never organic) | DISTRIBUTION — organic web sessions ≥1,200/28d AND saves-per-reach ≥1.0% | 2026-10-29 | $0 (gate) | REBASELINED | 3 |
🤝 Partnerships & Affiliates (both directions)tma-partnerships-growth-owner | ≈£275/mo net (bank-traced 25 Jul — STATIC, no live source) | CASH — net contribution ≥ £275/mo + 6 placements | 2026-10-29 | $100–150 | STATIC | — |
📊 Analytics / Data Truthtma-analytics-lead | dark share 24.2%/28d | WIRING — dark_share_28d <= 15 | 2026-10-29 | $0 (gate) | LIVE | — |
docs/company/GOVERNANCE/FLEET_CHARTERS.md.| # | Task | Owner | Fleet | Time | Impact | |
|---|---|---|---|---|---|---|
| 1 AGA-146 | 🟢 GO/NO-GO: mail a 500-contact test segment of the 6,813 app-side contacts who finished the survey but never did a workout (NP verdict lever 1, $0,…full detail →🟢 GO/NO-GO: mail a 500-contact test segment of the 6,813 app-side contacts who finished the survey but never did a workout (NP verdict lever 1, $0, ranked DO-FIRST). The fleet drafts the activation email through /email-fleet + segment_guard.py and shows you the rendered email + segment PASS before anything sends — this row is only your GO to draft-and-show. Rates behind it are MODELLED (skeptic-flagged): read the 500-test click rate before touching all 6,813. | AGA | P7 Search | 5 min | ●●●●● | |
| 2 AGA-147 | 🟢 GO/NO-GO: give '758 Nurture Sequence 2' (14,600 contacts) a quiz-root CTA slot — NP verdict lever 2, $0 ('fix the tap before the bucket'). Email…full detail →🟢 GO/NO-GO: give '758 Nurture Sequence 2' (14,600 contacts) a quiz-root CTA slot — NP verdict lever 2, $0 ('fix the tap before the bucket'). Email currently sends the $97 quiz ~1 lead/8 days while 14 sales came from everywhere else. SISTER ROW to AGA-129 (weekly broadcast repoint) — one GO can cover both. Evolution-Law compliant path: ONE slot change through /email-fleet, not an arc rewrite; fleet executes end-to-end on your GO. | AGA | P7 Search | 5 min | ●●●●● | |
| 3 AGA-168 | 🧲 DECIDE: THE 10th LEAD MAGNET HAS 232 SESSIONS/28d AND NO WAY TO CAPTURE AN EMAIL. Measured live 12 Aug on…full detail →🧲 DECIDE: THE 10th LEAD MAGNET HAS 232 SESSIONS/28d AND NO WAY TO CAPTURE AN EMAIL. Measured live 12 Aug on themovementathlete.com/get-started-with-calisthenics/: the opt-in form's container is switched OFF on desktop, tablet AND mobile (the block source says UAGHideDesktop/UAGHideMob/UAGHideTab all true — deliberate, not responsive), so the rendered page has ZERO visible email inputs; the only visible form is the site search. Traffic is real and EXACT-matched: 232 sessions/28d, 442/90d. The page still routes people out via three quiz-root CTAs, so it is not converting nothing — it simply cannot take an email. 🔴 IT CANNOT JUST BE SWITCHED BACK ON: the form has NO destination configured (no afterSubmitAction, no confirmationType, no webhook, no email target on its 36 attributes; no Spectra entry store; and no Spectra submission has ever reached hello@ — the only 4 'Spectra' emails there are WP Engine vulnerability notices). Unhiding it alone would show visitors a form that silently discards them, which is worse than today. YOUR CALL, three options: (a) WIRE IT — I build the destination (AC via API + UTM hidden fields from the start) and then unhide, so it captures with source attribution from day one; (b) MOVE IT to Leadpages, matching the other 9 magnets, one consistent rail; (c) LEAVE IT OFF and let the quiz-root CTAs do the work, and I retire the dead form so nobody re-discovers this in three months. My recommendation is (a) if you want the email list to grow from content, (c) if the quiz is the only funnel you want to maintain — the honest case for (c) is that the page already routes to the quiz and a second capture path is another thing to keep alive. ⚠️ SIZE HONESTLY: at a typical 20-40% magnet opt-in this is [MODELLED] ~45-90 leads/28d, but that rate is an industry assumption, NOT measured on this page — the page has never captured, so we have no rate of our own. | AGA | P7 Search | 5 min | ●●●●● | |
| 4 NIC-106 | 🔴 REPOINT 'QLG | ADV+ OFF | MC | Control' TO THE QUIZ ROOT — 5 min in Ads Manager. All 4 of its live ads still land on…full detail →🔴 REPOINT 'QLG | ADV+ OFF | MC | Control' TO THE QUIZ ROOT — 5 min in Ads Manager. All 4 of its live ads still land on https://quiz.themovementathlete.com/2026/ the old page that does NOT show the 97 dollar offer. Change the destination on all four to https://quiz.themovementathlete.com/ (the ROOT). Nothing else: leave url_tags, budgets, geo and the objective exactly as they are, and do NOT pause the campaign. EXACT LIST — adset 'General / T2 / FM 30-54 US/Canada' (GBP 9.62/day, geo US+CA): ad 120253084939360728 and ad 120253084939340728. Adset 'General / T2 / FM 30-54 AUS, FR, UK SING, NZ' (GBP 9.62/day, geo NZ GB IL AU FR): ad 120245527336080728 and ad 120245527336070728. WHY THIS AND NOT A PAUSE — measured live 11 Aug on the Meta API, 30 days, unified attribution: MC Control produced 129 leads at GBP 2.43 each, the biggest lead source on Meta (56% of the two QLG lines' 230 leads; 57 in the last 7 days). Pausing would cut Meta lead volume by more than half in the window we are banking leads for Black Friday. Repointing keeps all 129 leads/month and sends them to the page that shows the 97 AND enrols them into 993 SALES SEQUENCE, whose 10 Sep gate the whole paid plan waits on. Zero cash change either way — the account's daily budget is GBP 45.48 before and after. This is also exactly what Aga's 10 Aug ruling asked for ('RE-AIM into the ONE geo-locked lead line, quiz root'); NIC-97 was built as a clone, which the spec allowed, so MC Control itself was never touched. TWO OPTIONAL EXTRAS while you are in there: (a) drop IL from the second adset's geo — Israel is the only country in this campaign not on the buyer list (US UK CA AU DE SE NL FR); (b) pause the third adset 'General / T2 / FM 22-65 North America' — it holds a GBP 7.00/day budget but all SIX of its ads are paused, so it cannot deliver and spent GBP 0 in the last 7 days. Hygiene only, frees nothing. VALIDATION (2 min): after saving, open each of the 4 ads' preview and confirm the click goes to the quiz root and lands on the page showing the 97 offer, then confirm the campaign is still ACTIVE and its daily budget is unchanged at GBP 9.62 + 9.62. | NIC | P7 Search | 5 min | ●●●●● | |
| 5 AGA-139 | 🔴 TELL THE PHP DEV: calisthenicsacademy.co's Divi is 4.4.8 — the "only PBE" assessment is WRONG, and upgrading PHP first will break the site.…full detail →🔴 TELL THE PHP DEV: calisthenicsacademy.co's Divi is 4.4.8 — the "only PBE" assessment is WRONG, and upgrading PHP first will break the site. Measured live 10 Aug: the ACTIVE THEME on CA is Divi 4.4.8, and Elegant Themes only added PHP 8.0 support in Divi 4.8 — so the theme predates PHP 8 support by four minor versions. Page Builder Everywhere is real but is NOT the only blocker and is the smaller one (its entire footprint is ONE copyright line in one widget area — the widget is Divi's own, so it moves to Divi's native empty Footer Area #1 and PBE deactivates; no replacement plugin). Likely cause of the wrong assessment: BWA runs Divi 4.27.7 (per NIC-14), so a check done there would look clean — CA was never separately verified. ⚠️ We CANNOT read PHP version from outside WP Engine, so I cannot tell you what PHP CA is on today — ask the dev. THE ASK: before the dev touches PHP on CA, they update Divi first (Elegant Themes Updater is installed and active — needs the ET API key, so confirm the Divi licence is still live), and ideally the whole job moves onto Rocket staging as part of the migration instead of running on the live site. Full plan, plugin disposition table and verification commands: docs/company/HOSTING_MIGRATION_2026-08/CA_PHP8_DIVI_UPGRADE_ADDENDUM_2026-08-10.md | AGA | P7 Search | 10 min | ●●●●● | |
| 6 AGA-161 | 🏋️ ONE BUILDER VISIT, ~10 MIN — THE WORKOUT-1 EMAIL + 2 CONDITIONS. 🔴 CORRECTED 12 Aug: part 2 is ONE arc, not two. The highest-intent moment in the…full detail →🏋️ ONE BUILDER VISIT, ~10 MIN — THE WORKOUT-1 EMAIL + 2 CONDITIONS. 🔴 CORRECTED 12 Aug: part 2 is ONE arc, not two. The highest-intent moment in the funnel carries no email at all — the morning after someone's FIRST workout (~48 people/day). My side is built and seeded: a nightly job reads Intercom, finds everyone whose workout count went 0 to 1, excludes payers four ways, and stamps the tag "workout1-yesterday" by ~03:30. The tag is the bridge — ActiveCampaign never has to see the app. What it cannot do is create an automation or add a condition. YOUR SIDE, three things in one sitting: (1) NEW AUTOMATION: trigger 'Tag workout1-yesterday is added' (Runs: Once) -> Wait until 8:00am contact time -> Send an email -> End. Use a PLACEHOLDER for the email (any template, subject 'W1 morning after', save and close) — do NOT open the designer; send me the automation id from the URL and I write the real body in by API, same as every email this week. Leave it Inactive. (2) On 750 TMA | Welcome Sequence After Quiz ONLY, add a goal block — copy the existing 'If customer - END' one — with the condition 'has tag workout1-done', which ends the automation. Its whole job is getting them to workout 1, so once they train the job is done, and two of its slots turn false (W3 'how did your first workout feel?' and W4 'have you pressed start yet?' would ask someone who trained yesterday whether they've started; W7's trial ask would repeat the morning-after email's ask days later). 🔴 992 and 967 are deliberately LEFT ALONE — doing a workout is not the same as being measured, so their 'get measured free' argument survives activation and neither contains a 'have you started yet' nudge. (3) AGA-145's promo-opt-out exclusion on 993 SALES SEQUENCE — same condition class, 2 min, and it is live with 121 people in it. Tell me the automation id and I do the rest. | AGA | P7 Search | 12 min | ●●●●● | |
| 7 RN-01 | 📲 RN TEAM — IN-APP PAYWALL UI REBUILD. Sent to the RN devs 5 Aug; awaiting their estimate then build. 🔴 THE LINK TO SEND (standalone dev site, 6 Aug…full detail →📲 RN TEAM — IN-APP PAYWALL UI REBUILD. Sent to the RN devs 5 Aug; awaiting their estimate then build. 🔴 THE LINK TO SEND (standalone dev site, 6 Aug — the claude.ai artifact links died twice; Aga ruled the handoff gets its OWN site, never a page under the revenue plan): https://tma-paywall-brief.netlify.app/ — the brief; it links the design contract (/spec.html) and the package zip on the same domain. All three verified live 6 Aug. Scope is UI-ONLY across ALL THREE existing pricing pages (full price / 30% off / 50% off) — RevenueCat, purchase, restore, terms and analytics already wired; offers already configured. Estimate 5h max in 6 milestones. THE ASK ON THEM: all three pages on the ONE component via variant='full'|'off30'|'off50', not three rebuilds — three drifting copies is how the current bug survived, and separate rebuilds roughly double the cost. WHAT TO WATCH FOR ON RETURN: (1) no price literal anywhere in the UI — every figure derived from the store product at render time, retry state if offerings fail; (2) they confirm the estimate BEFORE starting rather than pushing past 5h; (3) the on-device pass includes a NON-USD currency, the actual defect being fixed. Also in the package: 7 broken strings below the fold. NOT in scope (Aga, 6 Aug): the RN-1 identity + push add-on — Nic is already working on those. | RN | P7 Search | 15 min | ●●●●● | |
| 8 AGA-119 | 💷 THE ANNUAL OFFER TO THE ENGAGED — 🔴 DO NOT APPROVE YET, THE AUDIENCE HALVED. Measured live 11 Aug via the AC API: tag 237 'Engaged' holds 2,912…full detail →💷 THE ANNUAL OFFER TO THE ENGAGED — 🔴 DO NOT APPROVE YET, THE AUDIENCE HALVED. Measured live 11 Aug via the AC API: tag 237 'Engaged' holds 2,912 people, not the ~6,367 this row claimed and not the 6,038 the 31 Jul trust audit recorded — it has more than halved in 11 days and NOBODY HAS DIAGNOSED WHY (FLEET-53, taken by the fleet). That number is the denominator of the whole prize, so the modelled +$898/mo roughly halves with it. Two other things must clear first: (1) the draft DOES NOT EXIST — FLEET-07 is still open, so there is currently nothing for you to approve; (2) the unsub gate is still marginal (967 at 0.51% vs the 0.5% hardstop on today's scoreboard). Your decision when it reaches you is ONE thing: which week it sends, sequenced against FLEET-38 (50%-off falsifier) and AGA-118 (warm pool) — never the same week as either. | AGA | P7 Search | 20 min | ●●●●● | |
| 9 AGA-129 | 📧 APPROVE: POINT ONE WEEKLY EMAIL AT THE QUIZ ROOT — one link decision in one already-scheduled send to the engaged list, CTA →…full detail →📧 APPROVE: POINT ONE WEEKLY EMAIL AT THE QUIZ ROOT — one link decision in one already-scheduled send to the engaged list, CTA → https://quiz.themovementathlete.com/ (root, UTMs per the link rule). Evidence: email produced exactly 1 quiz lead in 8 days and it landed on the DEAD 2026-v2-3 build (via 967 TMA | Welcome Sequence After LEAD MAGNET E2); ~14,600 sit in 758 Nurture Sequence 2 with no priced offer; 4 of the 8 provable $97 buyers came from owned surfaces — our highest-converting traffic, free. Flow on your yes: /email-fleet drafts it → lint gate → you see the RENDERED email → you approve the send. To-do #4 of the quiz-funnel CEO doc §19. | AGA | P7 Search | 20 min | ●●●●● | |
| 10 AGA-134 | 🚀 APPROVE THE WELCOME PERSONALISATION DESIGN (W-D1–W-D4) — the abandonment engine extrapolated to the email estate, welcome sequence first. Read the…full detail →🚀 APPROVE THE WELCOME PERSONALISATION DESIGN (W-D1–W-D4) — the abandonment engine extrapolated to the email estate, welcome sequence first. Read the artifact (~20 min) and reply GO/amend on four calls: (D1) the personalised welcome deploys as a NEW automation + atomic entry-trigger swap replacing 750 TMA | Welcome Sequence After Quiz under the EVOLUTION LAW — THIS ROW'S ID IS THE WRITTEN OVERRIDE the law requires; (D2) stage-branched start — non-checkout leads get E1 at ~T+2h, only NEW quiz-reached-checkout waits to day 3–4 (refines your 4 Aug ruling; only they are in the $97 conversation the abandonment estate owns); (D3) the welcome ask = soft free-trial at E5 per SPEC-WELCOME doctrine, the $97 conversation stays the abandonment estate's (offer-ladder ruling — yours); (D4) instrument = 50/50 concurrent split (the AGA-64 manual AC setup) over swap+confounded pre/post. Doc: docs/company/ANALYTICS/WELCOME_PERSONALISATION_ENGINE_EXPANSION_2026-08-07.md · artifact: https://claude.ai/code/artifact/9bd17095-a7ac-4cca-be28-c5a5340d690a | AGA | P7 Search | 20 min | ●●●●● | |
| 11 AGA-142 | 🔴🔴 ROTATE 3 PASSWORDS SITTING IN PLAINTEXT IN THE REPO — and remove a stale WordPress admin. Found 10 Aug in SUPPORT/Ca SUPPORT SOPs.txt (~20…full detail →🔴🔴 ROTATE 3 PASSWORDS SITTING IN PLAINTEXT IN THE REPO — and remove a stale WordPress admin. Found 10 Aug in SUPPORT/Ca SUPPORT SOPs.txt (~20 password lines, Calisthenics Academy era). ✅ CONTAINED: the file is tracked locally but I verified it is NOT on origin/main — it was never pushed, same shape as the July token.json find. I have added SUPPORT/ to .gitignore, which stops it reaching a remote but does NOT fix an already-committed secret — only rotation does, and history rewriting is hard-forbidden here. THE LIVE EXPOSURE, in priority order: (1) 🔴 jeffcowancc@gmail.com — its password is in the file AND that Gmail is currently an ADMINISTRATOR on calisthenicsacademy.co (WP user calisthenicsacademy-2). Anyone with that password can password-reset their way to full control of the site. Change the Gmail password, and tell me if that admin account should be REMOVED from WordPress — it is the old CA support person and I did not delete a user without your word. (2) The Drift and UserEcho logins for hello@calisthenicsacademy.co — both services we no longer use, so cancel rather than rotate. (3) An Intercom login on hello@themovementathlete.com — Intercom IS live and carries the app funnel data, so rotate this one properly. ALSO WORTH KNOWING: developer.mrski@gmail.com is a live administrator on that site — correct while the PHP work runs, worth removing when it finishes. | AGA | P7 Search | 20 min | ●●●●● | |
| 12 AGA-118 | 📨 EMAIL THE ~1,020 WARM POOL (90d: ~570 saw the paywall + ~450 clicked buy, never paid) — the free version of the best paid audience we own, and it…full detail →📨 EMAIL THE ~1,020 WARM POOL (90d: ~570 saw the paywall + ~450 clicked buy, never paid) — the free version of the best paid audience we own, and it has NEVER been sent. Re-verified 6 Aug: newest AC broadcast with real volume is 24 sends (4 Aug) — nothing at this scale exists in the campaign log. Copy through /email-fleet; MUST pass tools/retention/send_collision_check.py and the broadcast-cadence doctrine before send. | AGA | P7 Search | 45 min | ●●●●● | |
| 13 AGA-103 | 💷 THE CUT LIST — ~1h of clicking for ~$1,650-2,180/mo of certain recurring cash. Every one of the 17 fleets points at revenue; NONE points at cost.…full detail →💷 THE CUT LIST — ~1h of clicking for ~$1,650-2,180/mo of certain recurring cash. Every one of the 17 fleets points at revenue; NONE points at cost. The 5 Jul CFO Master Report found £2.9-4.3K/mo recoverable 'without cutting people or growth' — it has sat in a folder called RESCUED_TEMP_DOCS, is not in DOCUMENT_MAP, and has generated ZERO queue rows in a month. List is ranked and dependency-checked: TIER 1 = 6 clean kills, £533/mo, ZERO repo references (Kajabi 187 · Atlassian 130 · The One Percent 74 · Envato 71 · Paddle Retain 37 · Typeform 34). TIER 2 = one question each, £700-1,100/mo — 🔴 AWS £296/mo is the highest-value 5 minutes: the app runs on DigitalOcean and NOTHING in the repo references AWS. TIER 3 = Intercom £221, a real conflict needing your ruling (CFO says kill; canonical_strategy.settled says 'Don't drop Intercom' — but that ruling was made without the cost number in front of it). 🔴 I CHANGED 3 CFO VERDICTS: Tapfiliate £66 = KEEP, it returns ~£275/mo net (killing it would have destroyed 4x its cost); WP Engine already superseded by the Rocket.net decision (~£260/mo, own track); Intercom escalated not actioned. Confirm every cut on the NEXT STATEMENT, never the cancellation screen. | AGA | P7 Search | 60 min | ●●●●● | |
| 14 NIC-101 | 🔴 MAKE THE IN-APP CANCEL DOWNSELL PLAN-AWARE — annual leavers are shown a WORSE deal than our own standing offer. Measured 11 Aug: 84 of the 115 in…full detail →🔴 MAKE THE IN-APP CANCEL DOWNSELL PLAN-AWARE — annual leavers are shown a WORSE deal than our own standing offer. Measured 11 Aug: 84 of the 115 in the cancel queue are ANNUAL, and every canceller sees $9.99/mo. That is $119.88/yr, and MEASURED over 516 people who ever took it (493 cancelled): median life 2.0 months, $34/taker — against annual-at-50% = $78.50. It defers churn by two months at 40% of the price. (1) BRANCH THE CANCEL DOWNSELL ON PLAN: annual → the 50% annual save; monthly/quarterly → keep $9.99/mo (price nine-ninetynine). 🔴🔴 MECHANISM — CORRECTED 11 Aug, READ THIS: the save at cancel-time MUST be a COUPON APPLIED TO THEIR EXISTING SUBSCRIPTION (a 50%-off duration=once coupon such as tma_v3_50off_once, so their next renewal bills $78.50 and auto-renew stays on) — NEVER a checkout link. At the cancel moment they STILL HOLD AN ACTIVE SUBSCRIPTION (88 of 115 have access, median 106 days), so a fresh checkout opens a SECOND subscription and DOUBLE-BILLS them. This is the exact defect the 10 Jul R1 rebuild already caught with /50promo/. The checkouts-v3/50off/12month/ link is correct ONLY for the truly-lapsed (access ended), i.e. the 976 Cancelled Win-Back arc — not for this screen. (2) ANSWER WHAT WE CANNOT SEE: does subscription_cancelled fire at renew-off (access still paid) or at access-end? (3) 🔴 IF PAUSE IS EVER BUILT it needs ENTITLEMENT GATING: Stripe's pause_collection leaves status=active, so an app gating on status alone grants FREE ACCESS — and portal subscription_pause reads enabled=false and is deprecated, so the real path is native POST /subscriptions/{id}/pause. Note the 10 Jul finding that our web cancel button is CUSTOM, not the Stripe portal, so portal settings do not govern this screen. | NIC | P7 Search | 1.5h | ●●●●● | |
| 15 E11 | 756/757 Abandoned Cart Reminder — UPLOAD THE 4-EMAIL 2026-08 ARC INTO FRESH SHELLS. 🔴 CORRECTED 7 Aug 2026: this row previously described the RETIRED…full detail →756/757 Abandoned Cart Reminder — UPLOAD THE 4-EMAIL 2026-08 ARC INTO FRESH SHELLS. 🔴 CORRECTED 7 Aug 2026: this row previously described the RETIRED July build (3 emails, AC field 56, %ABANDONED_CHECKOUT_URL%, entry off the 'Subscription Cart Abandoned' tag 63598). That artefact is dead — its Stripe trigger (checkout.session.expired) can never fire on Payment Element, and tag 63598 is now orphaned. THE LIVE ARTEFACT is marketing/email-sequences/quiz-checkout-abandon-2026-08/ (E1-E4, lint PASS 4/4, %TMA_QUIZ_PERSONA% conditional content, API entry via quiz_abandon_sync.py). Both current shells are UNUSABLE: 756's email lacks an unsubscribe link + renders raw markdown, 757's four emails are EMPTY (subjects r/r/e/e) — Aga paused both 5 Aug. So this is: fleet hands over the personalised rebuild (FLEET-42), Aga builds 4 FRESH shells and un-pauses 756 only. E3/E4 wait on the $97-window enforcement decision (AGA-109). Plan: ABANDONMENT_STRATEGY_2026-08-07.md sections 5.2 + 7. | NIC | P2 Email | — | ●●●●● | |
| 16 SOC-1 | Nic S2 token (4 scopes) → IG + FB per-post flip LIVE on the board | NIC | P3 Social | — | ●●●●● | |
| 17 BRIEF-5 | T5 — Fix the AppsFlyer conversion events ★ highest leverage | NIC | Nic wiring (brief) | — | ●●●●● | |
| 18 BRIEF-8 | GA4 → Google Ads conversion import ("Task 3" in the 16-Jul message) | NIC | Nic wiring (brief) | — | ●●●●● | |
| 19 BRIEF-17 | T4 — One canonical user identity across all SDKs the keystone | NIC | Nic wiring (brief) | — | ●●●●● | |
| 20 AGA-157 | 🟡 HOLD UNTIL 13 AUG, THEN 2-MIN GO/NO-GO (fleet executes by API): set-level negatives on 'Quiz Funnel | Competitive Non-Branded | US'. 🔴 DO NOT RUN…full detail →🟡 HOLD UNTIL 13 AUG, THEN 2-MIN GO/NO-GO (fleet executes by API): set-level negatives on 'Quiz Funnel | Competitive Non-Branded | US'. 🔴 DO NOT RUN THIS BEFORE 13 AUG — Nic did the keyword surgery on 11 Aug (change log: 14:36:02 bid changes on 5 keywords in this campaign; 20:15:17-24 the 3 remaining spenders PAUSED — both halves of what was asked), and writing negatives on top of it would make his round unreadable. Two clean days (12-13 Aug) belong to him. THE READ ON 13 AUG: spend holding at ~GBP 6-9/day on hardware search terms → the set-level fix is needed, GO this row; a material sustained drop → his round worked, stand this row down and let the campaign have its clean fortnight to the 24 Aug gate. WHY THE ROW EXISTS REGARDLESS OF THE READ: 30-day totals are GBP 235.89 / 32 clicks / 0 conversions AND 0 all-conversions while both siblings converted (Competitors Branded GBP 311.57 → 24, Non-Branded GBP 241.33 → 6); all 60 still-enabled keywords are equipment phrases; the campaign carries ZERO negatives and ZERO shared negative lists; and the money has been buying 'backyard calisthenics setup' GBP 9.94, 'outdoor calisthenics gym' GBP 8.46, 'wooden parallettes calisthenics' GBP 7.91, 'parallettes' GBP 6.00, 'gornation pull up bar' GBP 4.77, 'dip bar for home' GBP 3.26 — hardware shoppers, and we sell a programme. The one round already readable is 7 Aug: it had four clean days after it and spend continued (GBP 7.61, 7.41, 6.22, 9.11). THE OPTIONS: (a) add hardware negatives at campaign level — bar, bars, parallettes, equipment, gear, machine, station, vest, kit, setup, buy, for sale, cheap; or (b) pause and rebuild on programme intent. Recommend (a) then read at the 24 Aug gate. ALSO CORRECTS OUR OWN STANDING ARGUMENT: 'never pause the campaign, 59 of 66 keywords are untested' is right in general but does not transfer here — the untested 60 are the same equipment-intent class that has already spent GBP 235.89 for nothing. | AGA | P7 Search | 2 min | ●●●●● | |
| 21 AGA-26 | 🍎 SCREENSHOTS + APP PREVIEW VIDEO (needs a designer, not a 5-min task — logging it so it isn't lost). (a) 🔴 Screenshot 2 ships 'STRENGHT' misspelled…full detail →🍎 SCREENSHOTS + APP PREVIEW VIDEO (needs a designer, not a 5-min task — logging it so it isn't lost). (a) 🔴 Screenshot 2 ships 'STRENGHT' misspelled in ~40pt type under the unsupportable claim 'THE MOST EFFECTIVE … ON EARTH', over a phone rotated so the text clips mid-sentence. (b) Apple uses only the FIRST 3 on the install sheet — the assessment (our actual differentiator) is at slot 3 shown on a LAPTOP in a phone listing, and the real proof (members + Red Bull/Forbes) is at slot 8 where nobody scrolls. Move the wedge to 2 and proof to 4. (c) 🔴 '0 of 3 App Previews' — no video at all. The preview autoplays at the top of the listing and is the strongest conversion element Apple offers; we have footage. Biggest unclaimed win on the page. | AGA | P7 Search | 5 min | ●●●●● | |
| 22 AGA-138 | DECIDE on the Remarketing Playbook — REVISED 11 Aug after a growth-fleet review BLOCKED the original. ~5 min ·…full detail →DECIDE on the Remarketing Playbook — REVISED 11 Aug after a growth-fleet review BLOCKED the original. ~5 min · https://tma-remarketing-playbook.netlify.app (1) ⛔ STRUCK 12 Aug — nothing left in it: the dead $297 lifetime price is GONE from /start-training/ (measured live 12 Aug, 0 occurrences in the served HTML), and the rating half is your 11 Aug "leave this alone, backlog this" ruling on NIC-98 — not to be re-raised in any form, including as a sub-item. (2) Meta un-pause is DEMOTED to a small experiment — the "10 purchases at £2.41" that justified it was double-counted AND window-cropped; the honest figure is 5 purchases at £67.48 full-life, all of a $297 offer that ended 12 Jul. (3) Google abandoner ladder £12/day — rung 1 OK, rung 2 must move to days 17-30 so paid does not undercut the welcome email's only full-price ask on day 10. | AGA | P7 Search | 5 min | ●●●●● | |
| 23 AGA-145 | 🛑 ARMED ARC, MISSING SAFETY VALVE: every email in 993 SALES SEQUENCE promises 'reply STOP to promo emails' via the promo-opt-out tag — the tag exists…full detail →🛑 ARMED ARC, MISSING SAFETY VALVE: every email in 993 SALES SEQUENCE promises 'reply STOP to promo emails' via the promo-opt-out tag — the tag exists (id 63977) but the EXCLUSION never got built. DEPLOY_SPEC §7 step 2: add condition 'Tag does-not-exist promo-opt-out' to the standing exclusion on every promo send path (993's entry, and any promo broadcast segment). Until it exists, a member who opts out KEEPS receiving the arc — and 993 is now ACTIVE with 115+ in flight, so the promise is live. ~5 min in the builder. | AGA | P7 Search | 5 min | ●●●●● | |
| 24 AGA-164 | 🔴 BY 18 AUG — CANCEL THE DUPLICATE TRIAL on achatnet43@laposte.net (Laurent Frejaville). He holds TWO trialing annual $157 subs on two customer…full detail →🔴 BY 18 AUG — CANCEL THE DUPLICATE TRIAL on achatnet43@laposte.net (Laurent Frejaville). He holds TWO trialing annual $157 subs on two customer records, both trial_end 18 Aug 2026: sub_1U3MTMI6s6pBAbnm9BmlPxJ1 (card attached, pm_1U3MXJ…) and sub_1U3ABWI6s6pBAbnmVQgr1ADU (NO card). On 18 Aug the carded one bills correctly; the uncarded one attempts, fails, and starts sending dunning mail to a live paying member. CANCEL sub_1U3ABWI6s6pBAbnmVQgr1ADU in the Stripe Dashboard (Subscriptions → search the sub id → Cancel immediately). Same class as the 15 customers / $1,662.86 of overlapping paid periods in CANCELLATION_AND_WINBACK_STRATEGY_2026-08-11.md §2c — and nothing in the estate watches for it forming. | AGA | P7 Search | 5 min | ●●●●● | |
| 25 AGA-96 | 🔴 REVOKE THE EXTERNAL DEV'S WP ENGINE SFTP — the open half of AGA-41, split out 4 Aug so it cannot close with its parent. He currently holds live…full detail →🔴 REVOKE THE EXTERNAL DEV'S WP ENGINE SFTP — the open half of AGA-41, split out 4 Aug so it cannot close with its parent. He currently holds live SFTP to THREE production sites (themovementath · movementath · calisthenicsac, port 2222) and is mid-PHP-upgrade, so access is CORRECT right now — this is not a problem, it is a diary entry. 🔴 Do it the day the PHP work and the Rocket.net migration are signed off: WP Engine Portal → Sites → each environment → Users and SFTP → delete the user. Three deletions, one per environment. Why it matters more here than usual: this estate has been compromised before (BWA), the same contractor also received the credentials referenced in AGA-42, and a contractor's standing access outliving the job is the single most common way a fixed breach comes back. Check-back: ask the dev for a completion date and set this row's date from his answer, rather than guessing. | AGA | P7 Search | 10 min | ●●●●● | |
| 26 AGA-126 | 🗄️ OLD ARC — 1 decision left, ~5 min. The 12 rebuilt emails have NO entry trigger and no day order yet — they are files, not an automation. Confirm…full detail →🗄️ OLD ARC — 1 decision left, ~5 min. The 12 rebuilt emails have NO entry trigger and no day order yet — they are files, not an automation. Confirm you want them wired, and to which list, before anything is built in AC. (The Ricky Warren ruling is DONE — moved to the shoulder lane as a presale email.) Review: Memorial Day viewer → 🗄️ OLD tab. | AGA | P7 Search | 10 min | ●●●●● | |
| 27 AGA-128 | 🧪 ADD THE SPLIT STEP IN 991 — 10 min in the AC builder, and it is the ONLY thing left before the re-engagement test runs. You approved the split; I…full detail →🧪 ADD THE SPLIT STEP IN 991 — 10 min in the AC builder, and it is the ONLY thing left before the re-engagement test runs. You approved the split; I cannot build it. Open 991 NEW Re-Engagement - For people who stopped opening Before Sunset in the automation builder, add a SPLIT action immediately after the start and BEFORE the first email, set it 50/50, leave path A on the existing R1 (campaign 4109, 'You've ignored my last 19 emails. Fair.') and point path B at the new challenger, then let both paths rejoin so R2/R3 and the suppression logic are unchanged. The challenger HTML is at marketing/email-sequences/991-r1-challenger-3choice-2026-08/R1B-three-choices.html — open it first, it is already rendered on your screen. Keep the SUBJECT IDENTICAL on both arms; that is deliberate, the subject earns 28.24% opens and varying it would confound the only thing being tested. Read on CLICKS, never opens, at T+7d. Everything else is done: 8,765 clean contacts ready, bounce circuit-breaker armed, all QA gates passed. | AGA | P7 Search | 10 min | ●●●●● | |
| 28 AGA-136 | 📧 THREE CLAIMS DEFECTS IN THE LIVE T+1h ABANDON EMAIL — one ruling from you and they all go to Nic together. The email is 'Try the app on us — your…full detail →📧 THREE CLAIMS DEFECTS IN THE LIVE T+1h ABANDON EMAIL — one ruling from you and they all go to Nic together. The email is 'Try the app on us — your results are ready' (subject), the ~1h post-checkout send doing ~30 in 4 days at 29.3% open. Its copy (tma-paywall-spec.netlify.app/email, fetched live 7 Aug) publishes: (1) 🔴 4.9★ — the published figure is 4.8, settled 4 Aug and re-ruled by you 6 Aug; 4.9 is retired on every surface, and FLEET-14 already swept the estate to 4.8, so this one was missed because it lives in Django, not in AC or WordPress. (2) 🔴 'Backed by Lead Sports Accelerator — led by Adidas and ex-Red Bull executives'. Canon is 'Backed by the LEAD Sports Tech Accelerator — run by the Adidas family and ex-Red Bull executives'. It drops 'Tech' and turns 'run by the Adidas family' into 'led by Adidas' — a corporate endorsement we cannot evidence. THIS IS THE THIRD DRIFT OF THAT ONE STRING IN TWO DAYS (also found in D3 of the new sales arc, fixed there). (3) The T+0 results email additionally promises 'goodbye to that annoying joint pain in just 30 days' to EVERY quiz finisher — a dated outcome claim. WHAT I NEED: your yes on all three wordings, then they ship to Nic as one small deploy alongside NIC-94 (which already covers the '[Chosen Password]' literal in the same estate). Copy is rendered for you in the review doc: marketing/email-sequences/evergreen-sales-sequence-2026-08/REVIEW.html — the SendGrid group at the top. | AGA | P7 Search | 10 min | ●●●●● | |
| 29 AGA-46 | 🧭 DNS CLEANUP — EXIT SITEGROUND COMPLETELY (sale-readiness). **Dev brief ready to send: **…full detail →🧭 DNS CLEANUP — EXIT SITEGROUND COMPLETELY (sale-readiness). **Dev brief ready to send: ** https://claude.ai/code/artifact/57248822-249c-4e54-bc48-67fb3822bf57 · analysis: `docs/company/HOSTING_MIGRATION_2026-08/DNS_EMAIL_ESTATE_ANALYSIS_2026-07-31.md`. **5–7 h, 3 phases.** ✅ **4 of 5 decisions now CLOSED** (31 Jul): @calisthenicsacademy.co email → **move to Google Workspace as a domain alias, do NOT delete** · Resend/SES → **KEEP** (new web app transactional) · SendGrid → **leave, costs nothing, its records don't touch the SPF budget** · Cloudflare proxying → **NO, DNS-only** (Rocket.net already includes Cloudflare Enterprise; double-CDN would hurt). Dev/test hosts → routed to Nic, not you. 🔷 DNS moves BEFORE the hosting cutover — SiteGround runs a **24-hour TTL** so a bad cutover there takes a day to reverse on the 1.9M-impression site. Fits inside Rocket.net's staging window, delays nothing. 🔴 **THE ONE THING LEFT FOR YOU — before SiteGround is cancelled, check SiteGround → Email → Accounts for stored mail on calisthenicsacademy.co. Cancelling deletes mailbox contents permanently.** 📊 SPF measured live: **8 of 10 lookups used** — dropping SiteGround's include frees 2. | AGA | P7 Search | 10 min | ●●●●● | |
| 30 NIC-102 | 🍎 SHIP THE STAGED iOS KEYWORD FIELD — the LIVE one wastes ~86 of its 100 characters, and the good one is ALREADY WRITTEN and sitting unsubmitted.…full detail →🍎 SHIP THE STAGED iOS KEYWORD FIELD — the LIVE one wastes ~86 of its 100 characters, and the good one is ALREADY WRITTEN and sitting unsubmitted. Read live from App Store Connect 11 Aug 2026. LIVE (v1.70.71, READY_FOR_SALE): 'ab,butt,beachbody,tracker,gym,exercise,diet,planner,trainer,buttock,calisthenics,ladder,routine,diet' — 'diet' appears TWICE (Apple ignores the duplicate), 'calisthenics' is ALREADY indexed free from the subtitle 'Calisthenics & Gymnastics' so it is 13 wasted characters, 'butt'+'buttock' are both off-brand, 'beachbody' is a competitor brand, and only ONE term describes what we sell. ALREADY STAGED on v1.70.72 (PREPARE_FOR_SUBMISSION): 'bodyweight,workout,plan,progression,home,beginner,strength,mobility,pull,push,handstand,planche,core' — no duplicates, nothing already-indexed, dramatically better. ✅ SO THE ASK IS SIMPLY: confirm v1.70.72 is actually going out, and tell us roughly when. If it is stuck, say what on. This matters because 87.3% of iOS installs come from App Store SEARCH and 74.3% install WITHOUT ever opening the product page — the keyword field and subtitle ARE the conversion surface, and this costs GBP 0. Optional further gain, our proposal, 92/100 chars, skill-intent over generics: 'bodyweight,pullup,pushup,handstand,muscleup,planche,lever,dips,mobility,strength,progression'. | NIC | P7 Search | 10 min | ●●●●● | |
| 31 NIC-110 | 💷 NIC — CUT 'Quiz Funnel | Competitors Branded | US' FROM £10/DAY TO £5/DAY, AND DO IT AFTER NIC-108 LANDS. It bids on rivals' brand names (mostly…full detail →💷 NIC — CUT 'Quiz Funnel | Competitors Branded | US' FROM £10/DAY TO £5/DAY, AND DO IT AFTER NIC-108 LANDS. It bids on rivals' brand names (mostly 'betterme'). It is the biggest line in the Google account — £314.77 over 30 days, 32%% of all Google spend, running at its £10/day cap — and it returns 24 conversions at £1.58/click. THE PROBLEM IS WHAT THAT COSTS PER OUTPUT: £13.12 per conversion, against PMax Search at £1.19, Branded at £1.74, and Meta's cheapest lead line at £2.43. It is 3rd of 5 Google lines by cost-per-conversion — better than Non-Branded (£40.07) and Competitive Non-Branded (no conversions at all), but 7.5x worse than the two that work. And 86%% of this account's conversions are not sales — 30d the account booked 105 leads + 4 add_to_cart + 4 Android installs against just 18 purchases (14 Purchase | Nic - Built on July 2026 + 4 Lifetime $297 Promo) — so those 24 are leads, and the 1-10 Aug Stripe audit found all 3 verified Google sales came from Branded, none from this line. 🔴 WHY CUT AND NOT PAUSE: competitor-brand search is genuinely in-market intent ('betterme alternative' is a person shopping), which a Meta cold lead-form fill is not, so the two lead types are not interchangeable at any price — and killing a line before knowing what its output feeds is the mistake the MC Control round already cost us. A halving keeps the intent flowing, halves the bleed and stays readable. 🔴 WHY AFTER NIC-108, NOT BEFORE: the freed £5/day has exactly one good home — PMax, the cheapest converter in the account and the only line actually at its budget cap (£150.32/30d against £5.00/day). Branded cannot absorb it: it spent £40.00 in 30 days against a £6/day cap, so it is volume-bound, not budget-bound, and money moved there will not spend. But PMax leaks 40%% of its budget into placements that return zero until NIC-108 fixes it — so moving money in first would just enlarge the leak. THE ORDER: (1) NIC-108 placements first; (2) then set this campaign's daily budget to £5.00 and raise PMax's to £10.00; (3) change nothing else — leave the keywords, the bidding, the geo and the objective alone. ALSO HOLD UNTIL AFTER 13 AUG: AGA-157's read on Competitive Non-Branded lands 13 Aug and two simultaneous Google changes make the account-level read unusable. VALIDATION (5 min): after saving, confirm the two daily budgets read £5.00 and £10.00 and the account's enabled total is unchanged at £36.00/day. THE READ, 10 SEP (the existing gate): if this line still shows zero Stripe-verified sales AND its cost-per-conversion is still above £10, pause it then — that is the kill condition, pre-registered now so the decision is not re-argued. If you think there is a reason to keep it at £10/day that these numbers cannot see, say so and it stands — you built it and you may hold information this analysis does not. | NIC | P7 Search | 10 min | ●●●●● | |
| 32 P-990 | 990 Monthly→Annual Upgrade Engine (Jul 2026) went LIVE 3 Aug — READ THE RESULT at 14 days (by 17 Aug 2026). The one number 980 never captured:…full detail →990 Monthly→Annual Upgrade Engine (Jul 2026) went LIVE 3 Aug — READ THE RESULT at 14 days (by 17 Aug 2026). The one number 980 never captured: CONVERSIONS. 980 pulled 26.7–29.2% open and 3.44–4.93% click on 3,310 sends but zero conversion tags were ever written, so its payoff is still unknown. This run has the checkout link + UTMs (utm_campaign=990-monthly-annual-upgrade-engine-jul-2026) so clicks reach Stripe metadata. Decision at 14d: keep $157 as-is, or move the $129 Stage-3 rate earlier. Live counters in the ⚙️ band above. | AGA | P6 Retention | 10 min | ●●●●● | |
| 33 AGA-159 | 💸 15 MEMBERS PAID TWICE — $1,662.86 — FOR YOUR AWARENESS, and any money decision is entirely yours. Measured 11 Aug across every Stripe subscription…full detail →💸 15 MEMBERS PAID TWICE — $1,662.86 — FOR YOUR AWARENESS, and any money decision is entirely yours. Measured 11 Aug across every Stripe subscription since 2024: of 3,478 customers, 370 hold more than one subscription and 15 of those PAID for two overlapping periods. 🔴 THIS ALSO CORRECTS A CLAIM I MADE TO YOU: I said a live subscription plus a checkout always double-bills; you said our system recognises the email and lets them upgrade/rebuy — you were right, 355 of 370 resolve cleanly. But these 15 are real, and nothing in the estate was watching for them. The worst: andreszeledon@gmail.com $157 over 314 overlapping days · mattlahana@gmail.com $141.30 over 290d · moniquetavian@gmail.com $235.50 over 90d · bmwparts@outlook.com $78.50 over 176d · stoica.ciprian@gmail.com $235.50 over 44d · tomifaludi@gmail.com $199.88 over 43d. Nine more run 4-39 days; the full list is in the Win-back tab §2c. 🔴 NO REFUND ACTION FROM ME, EVER — your 11 Aug ruling, now a standing rule across the estate: the fleet surfaces the finding and stops. What the fleet IS doing without you: diagnosing HOW these happened, i.e. whether one specific path leaves the old subscription running, from Stripe alone. https://tma-customer-journey-2026.netlify.app | AGA | P7 Search | 15 min | ●●●●● | |
| 34 NIC-105 | 🔴 REMOVE THE THIRD-PARTY GTM CONTAINER FROM THE CHECKOUT — GTM-PPGJXHH is hardcoded in app/apps/templates/base.html and is NOT ours. Aga checked all…full detail →🔴 REMOVE THE THIRD-PARTY GTM CONTAINER FROM THE CHECKOUT — GTM-PPGJXHH is hardcoded in app/apps/templates/base.html and is NOT ours. Aga checked all 7 GTM accounts / 12 containers on 11 Aug: no match. It loads on every legacy /register/ checkout — the page that holds a live Stripe key — alongside our own GTM-TKGDZR9, and we cannot see what it fires. DELETE two blocks in that one file: the <script> at lines ~12-18 (head) and the <noscript> iframe at ~52-55 (body). Leave GTM-TKGDZR9 untouched. While in there, also delete the dead Universal Analytics tag UA-104067894-1 (line ~20) — UA was sunset by Google in Jul 2023 and sends nothing. Nothing else in the file changes. AFTER DEPLOY: load /register/yearly3/ and confirm only GTM-TKGDZR9 loads. | NIC | P7 Search | 15 min | ●●●●● | |
| 35 NIC-108 | 🔴 NIC — STOP PMax BUYING PLACEMENTS THAT HAVE NEVER CONVERTED. 'Quiz Funnel | PMax | US | Sales OBJ' is the only LIVE campaign breaching the settled…full detail →🔴 NIC — STOP PMax BUYING PLACEMENTS THAT HAVE NEVER CONVERTED. 'Quiz Funnel | PMax | US | Sales OBJ' is the only LIVE campaign breaching the settled 'Category Search ONLY — no Demand Gen, no YouTube' ruling, and the breach is measurable: over the last 30 days its non-Search arms took £60.85 across 280 clicks and returned ZERO conversions — CONTENT/Display £28.37 (39,067 impressions, 210 clicks, 0 conv), SEARCH_PARTNERS £28.17 (48 clicks, 0 conv), YOUTUBE £3.28 (17 clicks, 0 conv), DISCOVER £1.03 (5 clicks, 0 conv). That is 40%% of the campaign's £150.32 spend buying nothing. 🔴 DO NOT PAUSE THE CAMPAIGN — its SEARCH arm took £93.07 and returned 78 conversions at £0.53/click, the lowest cost-per-conversion in the account. THE ASK: keep the Search delivery, stop the rest. PMax gives no clean channel toggle, so pick whichever you judge best and tell us which: (1) Account-level Excluded Placements + Content Suitability to cut Display/YouTube inventory; (2) campaign-level negative keywords + turn OFF Final URL expansion; or (3) the clean structural answer — retire PMax and move its £5/day into a standard Search campaign, which is what the ruling actually asks for. TWO CAVEATS TO CARRY, not decorations: (a) 86%% of this account's 'conversions' are email addresses, not sales — 30d the account booked 59 Leads-GTM + 46 generate_lead + 4 add_to_cart + 4 Android installs against only 14 Purchase + 4 Lifetime-promo — so PMax's 78 are leads; (b) PMax self-attributes generously (78 conversions from 174 clicks is a 45%% rate), so treat its Search figure as directional, not proven. VALIDATION (5 min): after the change, re-open the campaign's 'Where ads showed'/placement report 3 days later and confirm Display/YouTube impressions have gone to ~0 while Search impressions and conversions hold. | NIC | P7 Search | 15 min | ●●●●● | |
| 36 P0 | Settle the June question — RC 'New Subscribers by Store' May–Jul export. BLOCKS the CEO priority call (acquisition vs retention) | AGA | P6 Retention | 15 min | ●●●●● | |
| 37 AGA-53 | 🔧 WP ENGINE ESTATE (moved from NIC-17, Aga ruling 2 Aug) — own the external-WP-dev workstream: (1) 69 outdated plugins across 3 environments + 11…full detail →🔧 WP ENGINE ESTATE (moved from NIC-17, Aga ruling 2 Aug) — own the external-WP-dev workstream: (1) 69 outdated plugins across 3 environments + 11 outdated themes — ask the dev for triage first (security releases · live envs only · Divi/Yoast/Redirection/WP Rocket handled carefully), update security ones, schedule the rest; (2) mobile Lighthouse 48 on calisthenicsac + themovementath — connect to the WP Rocket Delay-JS task, do together; (3) rule out the live WP Engine 404/wp-admin incident before diagnosing any 404 on our sites; (4) eyeball: 'Deleted install themovementdev' 30 Jul + bandwidth 401/550 GB (73%) with avg visits/day +63% — falsifiable hypothesis: the fixed 51-hop BWA redirect loop was inflating requests; next cycle should show requests fall with no traffic drop. PHP upgrade = separate dev, target 8.2 (Divi 4.27.7 tested only to 8.2), brief marketing/seo/DEV_BRIEF_bwa_php_upgrade_2026-07-29.md | AGA | P7 Search | 20 min | ●●●●● | |
| 38 AGA-101 | 🔴 BWTA EMAIL AUTH — bodyweighttrainingarena.com sends mail with NO SPF, NO DKIM and NO DMARC (all three verified live 5 Aug; a real received header…full detail →🔴 BWTA EMAIL AUTH — bodyweighttrainingarena.com sends mail with NO SPF, NO DKIM and NO DMARC (all three verified live 5 Aug; a real received header shows spf=none and DKIM signed by mail1.wpengine.com, i.e. unaligned). 100% unauthenticated on our biggest property and anyone can spoof it. NOTE: the zone is ALREADY on Cloudflare (heather/zeus.ns.cloudflare.com) — no new account needed, you just need the login. NIC CANNOT DO THIS (Aga ruled 5 Aug: he does TMA only). Three records + one Google Admin toggle, ~20 min, step-by-step supplied in chat. 🔴 Publish DMARC at p=none FIRST and monitor 2-4 weeks — never jump BWTA straight to reject. | AGA | P7 Search | 20 min | ●●●●● | |
| 39 AGA-141 | 🎯 750 WELCOME SPLITS — 🔴 REWRITTEN 12 Aug, BOTH of this row's blockers are DEAD. (1) 'AC returns split data BLENDED so no arm can be declared' is…full detail →🎯 750 WELCOME SPLITS — 🔴 REWRITTEN 12 Aug, BOTH of this row's blockers are DEAD. (1) 'AC returns split data BLENDED so no arm can be declared' is REFUTED — I re-checked live: campaigns/<id>/campaignMessages returns ONE ROW PER ARM with its own send_amt, opens, clicks and unsubscribes. All 24 arms across the 8 slots are readable right now. No build needed. (2) 'slot 6's arm sells the 50% off' is GONE — that arm's subject became 'fit in only one dimension' during the no-offer-in-welcome work, and the only '50%' left in those bodies is width:50% CSS. So the collision this row warned about no longer exists. WHAT IS ACTUALLY TRUE, and it is a different problem: at 2–21 sends per arm (c4149's arms have 3, 3 and 2) NONE of these tests can ever reach significance — they are decorative, not wrong. THE ONLY DECISION LEFT, and it is small: leave them (harmless, and per-arm data accumulates for whenever volume arrives) or collapse each slot to its best arm so the arc reads as one email per slot. I lean LEAVE THEM — they cost nothing now that the data is readable, and collapsing is a builder job for you. Say the word either way. | AGA | P7 Search | 20 min | ●●●●● | |
| 40 NIC-22 | ✅ REASSIGNED 31 Jul — the WP Engine security deletions moved to the EXTERNAL DEV (now Step 0 of the migration brief), not Nic. Nic's only remaining…full detail →✅ REASSIGNED 31 Jul — the WP Engine security deletions moved to the EXTERNAL DEV (now Step 0 of the migration brief), not Nic. Nic's only remaining involvement in the hosting migration is dropping DNS TTL to 300s ~48h before cutover. Original detail: `docs/company/HOSTING_MIGRATION_2026-08/NIC_BRIEF_DELETE_EXPOSED_FILES.md`. | NIC | P7 Search | 25 min | ●●●●● | |
| 41 NIC-99 | 🟡 BUILD THE $97 WARM RETARGETING TEST — sequenced AFTER the 45-min sitting (keyword surgery + NIC-97 + MOF pause) and the 17-Aug CPL report. Spec:…full detail →🟡 BUILD THE $97 WARM RETARGETING TEST — sequenced AFTER the 45-min sitting (keyword surgery + NIC-97 + MOF pause) and the 17-Aug CPL report. Spec: ONE Meta campaign, objective OUTCOME_SALES, ~$5/day (~$150/mo from the freed budget), WARM audiences only (site visitors 30d · quiz-starters · quiz-checkout abandoners; EXCLUDE payers via the ever-paid set), creative = the $97 offer straight (no promo framing), destination = quiz root, keep url_tags. Judged ONLY on stamped Stripe sales (checkout_version=quiz_checkout + fbclid), 4-week read. Brief = tma-remarketing-playbook.netlify.app — use its corrected economics (£67.48 full-life per purchase during promos; the £2.41 is withdrawn), so expectations are honest: evergreen-$97 retargeting is UNTESTED, this is a test not a scale-up. Strategic kicker: the Nov BF retargeting push is Nic's build anyway — this warms the audiences, pixel events and creative learnings for it. | NIC | P7 Search | 60 min | ●●●●● | |
| 42 NIC-100 | 🤖 ANDROID: 32% OF USERS NEVER SUCCESSFULLY SIGN IN — REAL, MEASURED, AND STILL UNEXPLAINED AFTER A FULL ON-DEVICE QA PASS (12 Aug). 🔴 DO NOT HUNT A…full detail →🤖 ANDROID: 32% OF USERS NEVER SUCCESSFULLY SIGN IN — REAL, MEASURED, AND STILL UNEXPLAINED AFTER A FULL ON-DEVICE QA PASS (12 Aug). 🔴 DO NOT HUNT A BROKEN SIGN-IN — there isn't one. A tester ran the whole flow on a real Android phone: signing up, signing in, the quiz→app journey, and every failure path. The door WORKS (quiz email → password → in, 3 taps / 25 seconds). Password reset WORKS (verified in our mail provider's logs). The crash theory was already refuted (Play vitals 0.00% crash, 0.10% ANR) and so was the cheap-geo theory (buyer-countries-only, Android 45.6% vs iOS 111%). What remains is the measurement: buyer-geo login iOS 239/242 = 98.8% vs Android 85/125 = 68.0%, and social_login_attempted fires 103x on iOS and ZERO on Android. 🔎 SO THE OPEN QUESTION IS NARROWER AND SHARPER: not 'what is broken' but 'why do a third of Android users abandon a flow that demonstrably works'. The leading candidates are all friction, not failure — no autofill (the emailed 20-character password must be hand-copied every single time), no show-password toggle, no forgot-password link at the moment sign-in fails, and an email-verification wall the app imposes on every quiz-created lead. Those are specced as TMA-A1..A6 in docs/company/ANALYTICS/ANDROID_AUTH_TICKETS_2026-08-12.md. 🔴 The next step is user-level data, NOT another QA script. | NIC | P7 Search | 60 min | ●●●●● | |
| 43 P2 | Stripe-portal cancel intercept: point in-app cancel at portal login (SAVE30 loaded). PRE-CHECK first: Nic confirms webhooks sync portal un-cancels to…full detail →Stripe-portal cancel intercept: point in-app cancel at portal login (SAVE30 loaded). PRE-CHECK first: Nic confirms webhooks sync portal un-cancels to app entitlements | NIC | P6 Retention | 2h | ●●●●● | |
| 44 P1 | Annual pre-anniversary renewal sequence T-45/30/7 + year-in-review recap — via /email-fleet (renewal = known lifecycle coverage GAP). Honest size…full detail →Annual pre-anniversary renewal sequence T-45/30/7 + year-in-review recap — via /email-fleet (renewal = known lifecycle coverage GAP). Honest size +$170–450/mo | AGA | P6 Retention | 3h | ●●●●● | |
| 45 GOV | Lifetime cadence decision: each event ≈$20–30K instant cash vs MRR-multiple hollowing ('53% new' = ceiling). Board-level dial | AGA | P6 Retention | — | ●●●●● | |
| 46 E06 | P1 sends Mon 28 Jul (Aga schedules the 3 drafts) | AGA | P2 Email | — | ●●●●● | |
| 47 E08 | Renewal protection arc spec DONE — awaiting Aga nod, then build | AGA | P2 Email | — | ●●●●● | |
| 48 T11 | 🔴 MEASURED LIVE 6 Aug 2026 — NOT FINISHED, and the finished half is on the wrong URL. The VA reported Part 4 done; audited with the new…full detail →🔴 MEASURED LIVE 6 Aug 2026 — NOT FINISHED, and the finished half is on the wrong URL. The VA reported Part 4 done; audited with the new tools/youtube-analytics/endscreen_audit.py (reads end screens off the public watch page at ZERO API quota): 5 of 9 carry the Link element, 4 DO NOT — missing on 4FoCnqGC-o0, y7X-GuNZF_Y, OyRj1z3sNgs, Z9C7aCRCgn8. AND all 5 that exist point at https://themovementathlete.com/calisthenics-assessment/?utm_source=youtube&utm_medium=organic&utm_campaign=yt_endscreen — the doorway Aga's 3 Aug ruling took OUT of the YouTube path. Not the VA's error: the brief specified that URL on 24 Jul, before the ruling, and nobody re-read it. GOOD NEWS: the Link element works, so YPP genuinely unlocked it and the 60-second test passed. TO DO: (a) swap the target on the 5 existing to https://quiz.themovementathlete.com/?utm_source=youtube&utm_medium=organic&utm_campaign=yt_endscreen ; (b) add it to the 4 missing with that same URL. End screens have NO Data API endpoint — creation is Studio-UI only, so this stays with V — but VERIFICATION is now free and repeatable: python3 tools/youtube-analytics/endscreen_audit.py --expect-url https://quiz.themovementathlete.com/ <ids>. || ORIGINAL: PART 4 — add the UTM'd assessment LINK to end screens on the top 9, now that YPP unlocked the Link element. 🔴 URL CORRECTED 6 Aug 2026 — USE THIS ONE: https://quiz.themovementathlete.com/?utm_source=youtube&utm_medium=organic&utm_campaign=yt_endscreen . The old instruction said https://themovementathlete.com/calisthenics-assessment/?...&utm_campaign=yt_endscreen which CONTRADICTS Aga's 3 Aug ruling that the assessment page comes OUT of the YouTube path: all 869 video descriptions were moved onto the quiz root, so end screens were about to become the last YouTube surface still feeding the retired doorway — and nobody would have noticed, because the old URL still 200s (verified live, 0 hops). A dead instruction that still resolves is worse than one that 404s. 🔴 VA instructed to TEST one video first and STOP if Link is still greyed out. Then ~40 more in batch 2. Context: flagship end-screen click rate is 0.6% vs 1.3% channel average — with no link element available until now. | V | P4 YouTube | — | ●●●●● | |
| 49 BRIEF-4 | Switch Meta campaigns OFF the Lead objective → Purchase | NIC | Nic wiring (brief) | — | ●●●●● | |
| 50 BRIEF-11 | Export each user's “last funnel step” to Customer.io / ActiveCampaign ★ best ROI on this page | NIC | Nic wiring (brief) | — | ●●●●● | |
| 51 BRIEF-10 | Make the quiz funnel segmentable by traffic source in GA4 — CLOSED 7 Aug | NIC | Nic wiring (brief) | — | ●●●●● | |
| 52 AGA-62 | 📊 DO NOT READ THE AUGUST MIX YET — a note, not a task. Annual share reads **57.1%** vs July's 13.8%, which looks like the pricing page working. It is…full detail →📊 DO NOT READ THE AUGUST MIX YET — a note, not a task. Annual share reads **57.1%** vs July's 13.8%, which looks like the pricing page working. It is **4 sales out of 7**, on day 3 of the month. At n=7 that number is consistent with anything from 'no change' to 'huge win' — not decision-grade, and it must not be quoted to Nic, the board or a buyer. Same for retention's '4 cancel-ends MTD' vs July's 110. **Decision-grade at roughly 30–40 sales ≈ 13 Aug** at the current ~2.3/day. The pricing page shipped 31 Jul so this IS its first read and the direction is encouraging — just not yet a result. Close this row on 13 Aug with the real number. | AGA | P7 Search | 1 min | ●●●●● | |
| 53 AGA-30 | 📬 WATCH FOR LISTICLE REPLIES — 🔴 SPLIT INBOX, and it matters for a few weeks. The 3 pitches sent 31 Jul (fitbudd info@, gymnasetips contact@,…full detail →📬 WATCH FOR LISTICLE REPLIES — 🔴 SPLIT INBOX, and it matters for a few weeks. The 3 pitches sent 31 Jul (fitbudd info@, gymnasetips contact@, thehybridathlete) went out BEFORE the outreach mailbox existed, so they were sent from hello@ and signed hello@ — those replies land in **hello@**. Everything from now on sends from **hi@** and is signed hi@, so those replies land there. Watch both until the first three are answered or go cold (~2 weeks), then hi@ only. Each pitch offers free full access plus an optional commission arrangement: if one says yes, provision the free account promptly — a slow reply to an inbound yes wastes the placement. 🔴 If they ask for commission TERMS, that is still OPEN (flagged 25 Jul, never ruled) — hold, don't improvise. Log: marketing/seo/listicle_outreach_log.json (append-only, prevents double-sending). | AGA | P7 Search | 5 min | ●●●●● | |
| 54 AGA-102 | 🔗 RULING NEEDED — strip the BECOME AN AMBASSADOR / affiliate-program links from 759 Nurture Sequence 3 (38/38 emails) and 764 Nurture Sequence 4 (4…full detail →🔗 RULING NEEDED — strip the BECOME AN AMBASSADOR / affiliate-program links from 759 Nurture Sequence 3 (38/38 emails) and 764 Nurture Sequence 4 (4 HTML + 3 text of 15)? On 29 Jul you had these stripped from 751 Nurture Sequence 1 + 758 Nurture Sequence 2. 759 and 764 were never touched because ZERO people had ever entered them. That changed today: connecting the welcome arcs to 751 restored the ladder 751->758->759->764, so the first cohort will reach 759 for the first time ever. I did NOT extend your 29 Jul ruling to them myself — that is the exact over-generalisation you corrected me on today (applying a stuck-cohort ruling to new leads). ALSO FOUND while in there, and worth deciding together: 764 sends the SAME email twice, step 1 camp 3344 and step 3 camp 3856, identical subject 'Some people get stronger every year after 40.' and near-identical body (11,384 vs 11,436 bytes) — one should be deleted. AND 759/764 content has never been sent to a single person, so it has never had a QA or link-rot pass. Say the word and I do all three (I have a proven backed-up method — I stripped 20 emails today the same way). | AGA | P7 Search | 5 min | ●●●●● | |
| 55 AGA-127 | 🎁 COMP PABLO MENDOZA FREE PREMIUM until the rebuild bugs are fixed — Jesse's reply (drafted, in his thread) promises 'I'll have it switched on —…full detail →🎁 COMP PABLO MENDOZA FREE PREMIUM until the rebuild bugs are fixed — Jesse's reply (drafted, in his thread) promises 'I'll have it switched on — nothing for you to do'. pmendozacaballero@gmail.com, sub ended 27 Jul, left over rebuild bugs + price, offered us a full bug list. | AGA | P7 Search | 5 min | ●●●●● | |
| 56 NIC-109 | 🍎 NIC — WHICH REPORT DID THE $2,277 COME FROM? Your Slack note reads '$404 spend across June and July → $2,277 revenue → 5.6 ROAS'. THE SPEND…full detail →🍎 NIC — WHICH REPORT DID THE $2,277 COME FROM? Your Slack note reads '$404 spend across June and July → $2,277 revenue → 5.6 ROAS'. THE SPEND RECONCILES: Apple's own billing for 1 Jun–31 Jul is $377.32 (and $408.24 for 12 Jun–12 Aug), so $404 is real. THE REVENUE DOES NOT: AppsFlyer's aggregate partners report for the same window credits Apple Search Ads with $1,098.82 from 6 buyers — June $99.98 (1 buyer), July $998.84 (5 buyers). That is roughly HALF your figure, so one of the two systems is being read on a different basis and we should not publish either number until we know which. THE MOST LIKELY CAUSE, so you know what to look for: Apple's own dashboard attributes on Apple's window, which counts VIEW-through installs — over 12 Jun–12 Aug Apple recorded 165 tap-installs but 305 view-installs, and a view-inclusive basis roughly doubling credited revenue is exactly the shape of this gap. THE ASK: tell us which report and which exact date range the $2,277 came from (Apple Search Ads dashboard? AppsFlyer dashboard? RevenueCat? App Store Connect?) and whether its attribution setting includes view-through. If it is Apple's own view-through figure, both numbers are 'right' and we simply label which basis we quote from now on. 🔴 SEPARATELY, AND THIS IS THE PART THAT CHANGES A DECISION — Aga asked whether the ROAS is branded-only and the measurement says her instinct is right and stronger than she put it: 100%% of the attributed revenue in your window came from OUR OWN brand campaigns (Branded EU $763.89, Branded US-SR $334.93), while the 61%% of the spend that went to competitor and non-branded terms ($230.45 across Competitive-Branded, Non-Branded, Competitive Non-Branded, Non-Direct Competitive Demand Gen and Competitors_MC) returned 15 installs, ZERO buyers and $0.00. So the honest split is: branded 7.5x, non-brand 0.0x, blended 2.9x — not 5.6x across the board. Nothing to change in the account, those five are already paused; this is so the 5.6x is not quoted as if it were repeatable on new keywords. | NIC | P7 Search | 5 min | ●●●●● | |
| 57 V-01 | 📌 PIN 3 YOUTUBE COMMENTS — 5 minutes, 3 clicks in YouTube Studio. Their text is ALREADY CORRECT (I repointed all 40 to the quiz root on 12 Aug);…full detail →📌 PIN 3 YOUTUBE COMMENTS — 5 minutes, 3 clicks in YouTube Studio. Their text is ALREADY CORRECT (I repointed all 40 to the quiz root on 12 Aug); these three are simply not pinned, so they sink below viewer comments and are worth close to nothing. Videos: FLGLf5R5XkM · 0lPJpN1JmOM · PzJdqNuWwDU. DO: open each video's comments in Studio, find our channel comment (it already links to quiz.themovementathlete.com), and choose Pin. Verify free with: python3 tools/youtube-analytics/comment_audit.py <video_id> — it must show pinned=True quiz-root=True. 🔴 ALSO WORTH KNOWING WHILE YOU ARE IN THERE: 9 videos carry MORE THAN ONE of our channel comments competing for the pin (4fstTeS-MUQ TqdYJOGIQsc 1wWx1304S_A 1MFRTIH0pz0 SlaCyUauuVQ lRqo6Tjm2GA Wa89eMBcqQs gy6dOjw58_o Ue8_L6msDYc). The pinned one is correct on all of them, so this is tidiness, not a defect — delete the stragglers only if you are confident which is which. | V | P7 Search | 5 min | ●●●●● | |
| 58 AGA-108 | 🏷️ GTM CONTAINER GTM-PPGJXHH — CHECK WHETHER IT IS YOURS UNDER A DIFFERENT GOOGLE ACCOUNT (10 min, one login). VERIFIED LIVE 5 Aug: the legacy…full detail →🏷️ GTM CONTAINER GTM-PPGJXHH — CHECK WHETHER IT IS YOURS UNDER A DIFFERENT GOOGLE ACCOUNT (10 min, one login). VERIFIED LIVE 5 Aug: the legacy checkout page https://app.themovementathlete.com/register/yearly3/ returns 200, is titled 'Register and pay', carries a LIVE Stripe key (pk_live) and loads TWO GTM containers — GTM-TKGDZR9 (ours, readable via tools/analytics/gtm_read.py) and GTM-PPGJXHH (NOT in the account our service credentials can see). ✅ GOOD NEWS FIRST, so this is not urgent: the money has already moved off this door. Stripe subscriptions created in the last 30d, split by checkout_version — v3 115, quiz_checkout 58, unattributed 48 — and the unattributed cohort ran 1-4/day all July, then STOPPED: last one 2 Aug, and 0 on 3, 4 and 5 Aug. So the legacy door is no longer producing sales; your redirect worked. WHAT IS ACTUALLY OPEN: (a) is GTM-PPGJXHH a container you own under a different Google login (agency, old account, Nic's, a previous contractor)? If yes, add google-analytics-claude-access@the-movement-athlete.iam.gserviceaccount.com as a user and the fleet can read it — the whole question closes. (b) If it is NOT yours, that is a third party firing tags on a page that holds a live Stripe key, which is the part worth knowing. DONE WHEN: you can say 'it is mine, access granted' or 'it is not mine'. Nothing else depends on this today. | AGA | P7 Search | 10 min | ●●●●● | |
| 59 AGA-120 | ⏰ RULE THE DAY-5 TRIAL REMINDER — keep/change/kill, and name the rail that sends it. The checkout page PROMISES buyers a pre-renewal reminder; you…full detail →⏰ RULE THE DAY-5 TRIAL REMINDER — keep/change/kill, and name the rail that sends it. The checkout page PROMISES buyers a pre-renewal reminder; you verified on 10 Jul that a day-5 reminder exists, but which rail sends it (Django, AC or Stripe) was never established, so trial-ender coverage is unprovable. Recommendation on the plan page: KEEP, then one question to Nic to name the rail. | AGA | P7 Search | 10 min | ●●●●● | |
| 60 AGA-130 | 🖱️ DECIDE HOW WE MEASURE CLICKS ON THE TRANSACTIONAL EMAILS — right now we measure NONE. Account-level click tracking in SendGrid is OFF (open…full detail →🖱️ DECIDE HOW WE MEASURE CLICKS ON THE TRANSACTIONAL EMAILS — right now we measure NONE. Account-level click tracking in SendGrid is OFF (open tracking is on), so all 1,106 emails delivered in the last 14 days recorded ZERO clicks by construction — including the abandon email 'Your login is in this email', which sends ~11-14/day at a 29.3% open rate and whose entire job is getting someone back to the checkout. We cannot tell whether one single person has ever clicked it. TWO WAYS, pick one. (A) RECOMMENDED, zero risk, no SendGrid change: put UTMs on that email's links pointing at the quiz root per the LINK RULE, and read the clicks in GA4 — needs one Django template change from Nic, and it measures the thing we actually care about (arrivals at the checkout, not pixel fires). (B) Flip SendGrid Settings > Tracking > Click Tracking to ON — one click, measures every transactional email at once, BUT 🔴 it rewrites links in activation and password-reset emails too, and a mail-security scanner pre-fetching a rewritten one-time link can consume the token and lock a real member out of their account. That risk is why this is your call and not something I switched on. If you pick B, the safe form is per-message click tracking on the abandon email only, which is also a Nic change. | AGA | P7 Search | 10 min | ●●●●● | |
| 61 AGA-132 | ⏳ APPROVE THE 48H ENFORCEMENT FOR NIC — WHEN HIS QUEUE CLEARS (not now, your ruling). You ruled the $97 window BINDING on 7 Aug and said Nic is not…full detail →⏳ APPROVE THE 48H ENFORCEMENT FOR NIC — WHEN HIS QUEUE CLEARS (not now, your ruling). You ruled the $97 window BINDING on 7 Aug and said Nic is not to be interrupted for it. This row is the reminder to hand it to him later, not a task on his brief yet. WHAT HE'LL DO (~45 min): enforce the window at CHARGE time, not just in the UI — once an email has had a window, never mint it a second one; acceptance = an address 48h+ old sees $157 and Stripe charges $157. WHY IT MATTERS AND WHAT IT UNBLOCKS: until it ships, no recovery email may claim the deadline (the server never returns expired today and a lapsed visitor gets a fresh 48h), which is why the abandonment arc's E3 'your window closes tonight' beat cannot ship at all. Approve this and the arc gets its third email; leave it and the arc runs E1-E2-E4 honestly, which is a perfectly good sequence. NOT urgent, by your own ruling. | AGA | P7 Search | 10 min | ●●●●● | |
| 62 AGA-66 | MAKE A FREE TEST ACCOUNT — 10 minutes, unblocks the free-member audit. Three login-walled questions get answered the day the credentials exist: (1)…full detail →MAKE A FREE TEST ACCOUNT — 10 minutes, unblocks the free-member audit. Three login-walled questions get answered the day the credentials exist: (1) which upgrade CTAs a free member actually sees, (2) what the paywall state looks like from inside a free account, (3) whether the $9.99 step-down is reachable at all. Until the account exists these stay unanswerable and the free-member audit cannot start. (Was NIC-36; moved to Aga 3 Aug.) | AGA | P7 Search | 10 min | ●●●●● | |
| 63 NIC-18 | CONFIRM THE PHP STATE — the WP Engine activity log shows 'PHP update from version 8.4.0 to …' at 04:50 on 30 Jul under developer.mrski, and the…full detail →CONFIRM THE PHP STATE — the WP Engine activity log shows 'PHP update from version 8.4.0 to …' at 04:50 on 30 Jul under developer.mrski, and the portal separately reports '5 PHP outdated · 5 environments need updates'. We CANNOT read PHP version from outside (WP Engine doesn't expose it in headers or REST), so the current state is genuinely unknown to us. Two questions: (1) which environment did that update apply to, and to what version — if anything moved DOWN from 8.4.0 that matters; (2) is BWA still on 7.4.33? If NIC-14 is already done, say so and we close it. | WPDEV | P7 Search | 10 min | ●●●●● | |
| 64 AGA-16 | BWA plugin consolidation — TWO active redirect plugins at once (Redirection v5.9.0 with 19 rules, 17 carrying live hits; + SEO Redirection v9.19, NOT…full detail →BWA plugin consolidation — TWO active redirect plugins at once (Redirection v5.9.0 with 19 rules, 17 carrying live hits; + SEO Redirection v9.19, NOT REST-readable so its rules are unknown). 🔴 DO NOT click the dashboard banner offering to 'import 18 redirects' — duplicating rules across two engines is how you get loops. Order: 1) audit SEO Redirection's rule list in wp-admin and send Claude a screenshot 2) only then migrate anything unique into Redirection 3) deactivate SEO Redirection 4) delete the 3 dead plugins (SEOPress, All in One SEO, 301 Redirects — AIOSEO already left a broken llms.txt behind). Keep: Redirection, Yoast SEO. | AGA | P7 Search | 20 min | ●●●●● | |
| 65 AGA-67 | NOINDEX staging. + beta. VIA CLOUDFLARE, THEN CLEAR THE INDEX IN SEARCH CONSOLE — the half of NIC-43 that needs no deploy. Both hosts confirmed…full detail →NOINDEX staging. + beta. VIA CLOUDFLARE, THEN CLEAR THE INDEX IN SEARCH CONSOLE — the half of NIC-43 that needs no deploy. Both hosts confirmed served by Cloudflare (server: cloudflare, cf-ray present); neither serves a real robots.txt (the SPA catches the route and returns HTML), and both return HTTP 200 with no robots header. STEP 1 (Cloudflare, ~5 min): Rules -> Transform Rules -> Modify Response Header -> add X-Robots-Tag: noindex, nofollow for hostname staging.themovementathlete.com and beta.themovementathlete.com. No deploy, instant, one click to reverse. STEP 2 (Search Console, ~10 min): the promo URLs are ALREADY indexed, and noindex only takes effect when Google next recrawls — so after Nic's header is live, use Removals -> Temporary removal on app./register/monthly2/promo/ and /register/lifetimespecial/ to hide them within ~24h while the noindex does the permanent work. Order matters: do the removal AFTER the header exists, or it lapses in 6 months and they return. STEP 3 (~10 min, the interesting one): these are unlisted URLs, so ask HOW Google found them — URL Inspection -> 'Referring page' on both. If something public links to them, that link leaks the offer regardless of noindex, and noindex will not fix it. ACCEPTANCE: curl -I https://staging.themovementathlete.com/ and beta. both return X-Robots-Tag: noindex; both hosts and both promo URLs fall out of the TMA GSC property within ~2 weeks. | AGA | P7 Search | 25 min | ●●●●● | |
| 66 NIC-14 | BWA is on PHP 7.4.33 — end of life, no security updates, and WordPress will stop supporting it (min recommended 8.3). WP Engine portal change +…full detail →BWA is on PHP 7.4.33 — end of life, no security updates, and WordPress will stop supporting it (min recommended 8.3). WP Engine portal change + regression test. Flagged by the WP dashboard itself. | WPDEV | P7 Search | 30 min | ●●●●● | |
| 67 P3a | R1 voluntary saves: Seg A sent 20 Jul (30 emails, 0 saves yet — T-30 offers arm at expiry, too early to judge). Seg B/C question-send awaiting…full detail →R1 voluntary saves: Seg A sent 20 Jul (30 emails, 0 saves yet — T-30 offers arm at expiry, too early to judge). Seg B/C question-send awaiting authorization | AGA | P6 Retention | 30 min | ●●●●● | |
| 68 P3b | 976 Cancelled Win-Back — reason-matched copy through /email-fleet gates (6/7 current CTAs fail QA); reason tags already applied daily by machine | AGA | P6 Retention | 3h | ●●●●● | |
| 69 P5 | D1 activation cliff (D0→D1 ~10%) — durable product fix in RN app; route via CPO → Nic | RN | P6 Retention | — | ●●●●● | |
| 70 E15 | 764 Nurture 4 confirmed ends in silence → wire shoulder hand-raise (#13 unblocked) | AGA | P2 Email | — | ●●●●● | |
| 71 T2 | ✅ MEASURED LIVE 6 Aug 2026 with the new tools/youtube-analytics/comment_audit.py (zero API quota) — THE VA DID THE WORK. All 40/40 videos carry a…full detail →✅ MEASURED LIVE 6 Aug 2026 with the new tools/youtube-analytics/comment_audit.py (zero API quota) — THE VA DID THE WORK. All 40/40 videos carry a channel comment; 37/40 are pinned. She executed Days 1-4. 🔴 BUT ALL 40 COMMENTS POINT AT THE RETIRED /calisthenics-assessment/ DOORWAY (utm_campaign=yt_pinned), not the quiz root — because the comments were written in JULY and the 3 Aug quiz-root retrofit rewrote DESCRIPTIONS ONLY. Third surface the retrofit missed, after the channel About text and the end screens. Not the VA's error in any of the three. OUTSTANDING FOR V (12 min): (a) PIN the 3 that are posted but unpinned — FLGLf5R5XkM, 0lPJpN1JmOM, PzJdqNuWwDU; (b) the duplicate cleanup the brief asked for was NOT done — 9 videos carry 2+ channel comments competing for the pin: 4fstTeS-MUQ, TqdYJOGIQsc, 1wWx1304S_A, 1MFRTIH0pz0, SlaCyUauuVQ, lRqo6Tjm2GA, Wa89eMBcqQs, gy6dOjw58_o (3 comments), Ue8_L6msDYc — delete the OLDER one on each. NOT FOR V: the URL swap on all 40 is MINE — comments.update works on our own comments (50 units each, 2,000 total), so it runs once the retrofit frees the quota. Filed as a FLEET row. Re-verify anytime: python3 tools/youtube-analytics/comment_audit.py --file <ids> | V | P4 YouTube | — | ●●●●● | |
| 72 T5 | Stage 2: resume long-form cadence (≥ 2/mo) + retitle top winners + retention repair (on Aga's go) | AGA | P4 YouTube | — | ●●●●● | |
| 73 T7 | EDITOR REBALANCE (decided 21 Jul): Forge = majority; main TMA = protected floor ~2 long-form/mo on proven clusters (ring dips, strict muscle-up,…full detail →EDITOR REBALANCE (decided 21 Jul): Forge = majority; main TMA = protected floor ~2 long-form/mo on proven clusters (ring dips, strict muscle-up, push-up progressions, handstand, planche prereqs, pull-ups) + package-refresh top ~10 winners (no filming) + cut 2-3 reels per long-form. Reversed the earlier 'abandon main TMA' call after the 77%-organic finding. Dependency: Jesse batch-films monthly. | V | P4 YouTube | — | ●●●●● | |
| 74 T10 | RETITLE/RE-THUMB BRIEF for the top 5 winners -> marketing/youtube/YT_RETITLE_BRIEF_2026-07-23.md. 🔴 CTR + impressions are STUDIO-ONLY (not exposed by…full detail →RETITLE/RE-THUMB BRIEF for the top 5 winners -> marketing/youtube/YT_RETITLE_BRIEF_2026-07-23.md. 🔴 CTR + impressions are STUDIO-ONLY (not exposed by the Analytics API) — so priorities are [MODELLED] from views+retention. READ CTR IN STUDIO FIRST: below ~5.9% = packaging problem (retitle); at/above = retention problem (do NOT touch the title). Ring Dips flagship Ikeg_v5fvz4 = biggest prize (652 watch-h at 27.6% retention). 🔴 4FoCnqGC-o0 (What Muscles Do Push-Ups Work) = DO NOT RETITLE — 59.3% retention, it works; the lever is EXPANDING the 93s video. Discipline: one change, 14-day read, revert if flat. | AGA | P4 YouTube | — | ●●●●● | |
| 75 T12 | RETENTION RE-CUT BRIEF -> marketing/youtube/EDITOR_BRIEF_RETENTION_RECUTS_2026-07-24.md. 🔴 KEY MECHANIC: YouTube Studio Editor can TRIM/CUT a live…full detail →RETENTION RE-CUT BRIEF -> marketing/youtube/EDITOR_BRIEF_RETENTION_RECUTS_2026-07-24.md. 🔴 KEY MECHANIC: YouTube Studio Editor can TRIM/CUT a live video IN PLACE (same URL, keeps all views/rankings/comments) but CANNOT add footage. So: flagship Ikeg_v5fvz4 (381.6K impressions, 9.8% CTR, 27.2% retention, 3.1K lifetime watch-h) = TRIM ONLY, NEVER re-upload — 4 years of authority at stake. RC001-003 scripts contain a NEW cold open = requires re-upload; their originals earn only 26-38h/yr so that's acceptable, but AGA MUST DECIDE per video. | V | P4 YouTube | — | ●●●●● | |
| 76 SOC-2 | Nic S1 redirects (302 + query forwarding) → then revert the 11 UTM-forward snippets | NIC | P3 Social | — | ●●●●● | |
| 77 SOC-3 | SMM/VA first manual logs Monday (TikTok · FB Group · production hours · inputs panel) | V | P3 Social | — | ●●●●● | |
| 78 BRIEF-1 | Finish the RevenueCat → Amplitude connection — ✅ VERIFIED DONE 6 Aug | NIC | Nic wiring (brief) | — | ●●●●● | |
| 79 BRIEF-16 | Rebuild Google UAC to optimise to the trial event (later) | NIC | Nic wiring (brief) | — | ●●●●● | |
| 80 BRIEF-26 | Email deep-link infra — OneLink template + ESP wiring (the add-on's no-release half) | NIC | Nic wiring (brief) | — | ●●●●● | |
| 81 BRIEF-18 | T7 — Native product & lifecycle events (retention) | NIC | Nic wiring (brief) | — | ●●●●● | |
| 82 BRIEF-22 | RevenueCat Paywalls SDK — remote paywall & price control the one that ends the release cycle | NIC | Nic wiring (brief) | — | ●●●●● | |
| 83 BRIEF-23 | RN‑5 — iOS ATE/ATT + AppsFlyer always-init | NIC | Nic wiring (brief) | — | ●●●●● | |
| 84 BRIEF-24 | RN‑6 — install-UTM stitch + ➕ email deep-link add-on | NIC | Nic wiring (brief) | — | ●●●●● | |
| 85 BRIEF-25 | RN‑7 — screen-level tracking | NIC | Nic wiring (brief) | — | ●●●●● | |
| 86 AGA-156 | DECISION: 4,464 of 5,846 ActiveCampaign tags (76.4%) are one-person junk from the add_tag_to_quiz_contact() bug — 5,621 minted in a single day, 29…full detail →DECISION: 4,464 of 5,846 ActiveCampaign tags (76.4%) are one-person junk from the add_tag_to_quiz_contact() bug — 5,621 minted in a single day, 29 Oct 2025. The writer has been quiet since Nov 2025 so nothing is getting worse. Worth a bulk delete to make the tag manager usable again? Say go and I scope it (dry-run list first, nothing deleted without your sign-off). | AGA | P7 Search | 5 min | ●●●●● | |
| 87 AGA-09 | WP Rocket on BWA — turn on WebP compatibility + ShortPixel/Imagify bulk-convert (zero WebP on the site today; 30-50% off every image). Optional same…full detail →WP Rocket on BWA — turn on WebP compatibility + ShortPixel/Imagify bulk-convert (zero WebP on the site today; 30-50% off every image). Optional same screen: Delay JS execution, then click through a few pages. | AGA | P7 Search | 10 min | ●●●●● | |
| 88 T13 | RC001-003 re-cut decision. CLAUDE'S RECOMMENDATION: TRIM IN PLACE, do NOT re-upload, and do NOT prioritise them. Originals earn only 26-38 h/yr each…full detail →RC001-003 re-cut decision. CLAUDE'S RECOMMENDATION: TRIM IN PLACE, do NOT re-upload, and do NOT prioritise them. Originals earn only 26-38 h/yr each (~90 h/yr combined). A re-upload starts at zero authority, competes with the original, and the new-cold-open scripts are only needed for the re-upload path. The topics are ALREADY covered by the new production queue (11 handstand eps ready) — if we want a better shoulder-mobility-for-handstand video we should PUBLISH A NEW ONE, not resurrect a weak old one. Contrast: the flagship trim is worth doing because it's 3,100 lifetime watch-h at 27% retention — 30x the scale. | AGA | P4 YouTube | — | ●●●●● | |
| 89 NIC-107 | BACKLOG (Aga: park it, not to be raised again) — checkouts-v3 header renders '4.9 average rating'. House rule is 4.7/4.8; 4.9 is retired. Aga has…full detail →BACKLOG (Aga: park it, not to be raised again) — checkouts-v3 header renders '4.9 average rating'. House rule is 4.7/4.8; 4.9 is retired. Aga has seen it and ruled it PARKED on 11 Aug 2026. Pick up only if the header is being edited for another reason. | NIC | P7 Search | 5 min | ●●●●● |
marketing/seo/SEO_HUMAN_QUEUE.json · P6 Retention → tools/retention/retention_status.json · P2 Email → tools/email-plan/email_status.json · P4 YouTube → tools/youtube-analytics/yt_status.json · P3 Social → tools/social-pipeline/social_status.json · Nic wiring → docs/company/ANALYTICS/ANALYTICS-BRIEF-SITE/index.html (p-todo). A task not in one of these does not exist — write it there in the same session it is discovered.| Priority | Action | Owner | Status | Value |
|---|---|---|---|---|
| P0 · now | Send R1 — cancel-queue save (112 members) | Aga | Email assembled & hardened, CSV ready. Still 0 saved. Import to AC → send. | $1,591/mo at stake — fastest MRR in the plan |
| P0 · by 12 Jul | July-4 lifetime cart — final sends + close | Aga | Cart closes ~12 Jul (2 days). Confirm Fri–Sun sends are scheduled; close on time. | One-time cash; deadline is real |
| P0 · decision | Approve or amend the $3K/mo paid strategy | Aga | The plan is built & on the 🧪 tab, awaiting your go. Nothing paid executes until this. | Unlocks the whole paid engine |
| P1 · now | Intercom push: “your 3 free workouts are waiting” → signed-up, no-workout pool | Claude drafts → Aga sends | Reachable TODAY — ~40.9K app users are in Intercom, no release, no export needed. | Free retargeting (~500/wk enter this pool) |
| P1 · now | Intercom push / 72h step-down → paywall-viewed, no-trial pool | Claude drafts → Aga sends | Same Intercom path, reachable now. | Free retargeting (~180/wk) |
| P1 | Launch ASA brand-defence keywords | Nic (paid) | We currently bid $0 on “the movement athlete” — a competitor can conquest it for pennies. ~$3–5/day. | Protects the brand term cheaply |
1 · Trial launch (PAYER 1): Task 20 ✅ cleared 10 Jul — the only remaining blocker is Task 19 (checkout Stripe wiring, Nic — 📦 zip handed off 10 Jul, ~half a day, preview ↗). 2 · No cold ad spend before Task 9 (PAYER 2) — every paid dollar lands on the web checkout, whose purchase event is double-counted and misses ~1 sale in 5. ✅ 21 Jul: Nic shipped the server-side Stripe webhook + transaction_id dedupe — verify on 24h of fresh data, then this stop lifts.
| Priority | # | Task | Owner | Release? | Blocks |
|---|---|---|---|---|---|
| P0 | 20 ↗ | AC Day-5 trial reminder automation | Aga | Hard stop: no trial may run until this sends (promised on the checkout page) | |
| P0 | 19 ↗ | Checkouts v3 — Stripe wiring (8 pages) · 📦 HANDED OFF 10 Jul — zip on Aga’s Desktop (brief + message inside) · preview: tma-checkouts.netlify.app ↗ · ~half a day · 50% = evergreen at Stripe (no window checks) · ⚠️ PR into custom_checkout_app only AFTER July-4 closes (Sun 12 Jul) | Nic | 🟢 no | All trial + 50%-off revenue |
| P0 | 9 ↗ | Web quiz purchase event — server-side + deduped | Nic | 🟢 no | All cold ad spend — ✅ 21 Jul: server-side Stripe webhook + transaction_id dedupe shipped (was 1.52× dup, 81% capture); ⏳ verify on fresh data |
| P0 | 5 ↗ | T5 — AppsFlyer conversion events ★ | Nic | 🟢 no | Repairs Google + Meta + ASA at once |
| P0 | 1 ↗ | Finish RC→Amplitude (remaining lifecycle toggles + $amplitudeUserId) | Nic | 🟢 no | Lead→Trial, Trial→Paid rows |
| P0 | 2 ↗ | RC→Meta CAPI ✅ set 16 Jul | Nic | 🟢 no | Mapping live (app events: fb_mobile_purchase/StartTrial/Subscribe). Now dedup (Task 3) |
| P0 | 3 ↗ | CAPI dedup (event_id) | Nic | 🟢 no | Trustworthy cost-per-purchase |
| P1 | 4 ↗ | Meta: Lead objective → Purchase | Nic | 🟢 no | Cold prospecting (after 2+3). Optimise to fb_mobile_purchase, NOT Subscribe (renewals inflate it) |
| P1 | 11 ↗ | “Last funnel step” export → CIO/AC ★ | Nic | 🟢 no | ~$1–2.6K/mo free retargeting |
| P1 | 8 ↗ | GA4 → Google Ads conversion import — ✅ purchase REGISTERS in Ads via an independent GTM tag, NOT the GA4 import (Nic 20 Jul) → GA4's 1.52× doesn't contaminate Ads; ✅ 21 Jul: transaction_id + VALUE + currency now on the tag (both new & legacy checkouts), and LEADS register too via a generate_lead GTM tag; if GA4 import ever enabled → SECONDARY only; ✅ published 22 Jul; ⏳ ads-UI check after checkout testing (Task 19) | Nic | 🟢 no | Any Google return |
| P1 | 7 ↗ | Apple Search Ads cost data | Nic | 🟢 no | ASA pilot — Month 1 (first channel to spend) |
| P2 | 13 ↗ | Geo exclusions ET/TZ/NG/BD — forward guardrail | Nic | 🟢 no | Verified live 16 Jul: NO active campaign targets these geos (~$4/30d Meta spillover only). Re-apply when UAC returns |
| P1 | 14 ↗ | Meta Custom Audiences (warm pool) ✅ done 16 Jul | Aga | 🟢 no | Both built: quiz-leads-no-purchase + all-time customers |
| P1 | 10 ↗ | GA4 quiz funnel segmentable by source | Nic | 🟢 no | Reading paid landing→start |
| P2 | 6 ↗ | Meta pixel event = "TMA Page View" ✅ identified 16 Jul | Nic | 🟢 no | Passive View-Content CC; no campaign uses it — KEEP, don't delete |
| P2 | 12 ↗ | Intercom push — anon-install registration | RN team → Aga | 🔴 yes | Signed-up pools already push-reachable (~41K, no release); only anon installs need this |
| P2 | 15 ↗ | Google Ads API Basic access — ✅ GRANTED 20 Jul; spend auto-pulls daily | Nic | 🟢 no | Campaign-level ROAS |
| P2 | 16 ↗ | Rebuild UAC → optimise to trial | Claude→Nic | 🟢 no | Blocked until 5+8 |
| REL | 17 ↗ | T4 — canonical user identity (keystone) | RN team | 🔴 yes | Every server-side integration |
| REL | 18 ↗ | T7 — native product + lifecycle events | RN team | 🔴 yes | D1/D7/D30 retention |
checkout.themovementathlete.com is settled (Aga) — not a task. Full click-to-expand steps for every row live on the Master Brief.CHECKOUT_ATTRIBUTION_CODE_AUDIT_GROUND_TRUTH_2026-08-03.mdA MEASUREMENT DISCONTINUITY LANDED TODAY — do not compare any checkout number across it. Both estates stopped minting Stripe records on page load. So from 3 Aug: phantom trial/subscription counts collapse BY DESIGN (not churn, not a demand drop) and checkout conversion rate jumps BY DESIGN (the denominator stops counting bounces). The pre-3-Aug conversion rate was fiction and is not a baseline. Clean data starts 4 Aug · first honest read ~11 Aug · reliable ~17 Aug. The quiz abandoner audience also shrinks by design and gets higher-intent — judge that lane on recovered purchases, never list size.
| Date | Estate | What shipped | How we verified | Closes |
|---|---|---|---|---|
| 3 Aug | quiz-checkout | Intent no longer mints on page load. Mints on password blur (≥8), tap-to-load on the card area, or pay. stampPaywallView kept, so abandon recovery keeps a real audience. Plan switches cancel the prior incomplete sub. | Curled the served /static/quiz_checkout/stripe-client.js (200, 17,446 B): maybePrefetch 0 · DOMContentLoaded 0 · stampPaywallView 3 · cancel-incomplete present | NIC-41 ✓ |
| 3 Aug | checkouts-v3 | create-intent deferred off bare page load. Mints on valid email blur (or prefilled warm email) so stamp-abandon-email still has an intentId. Coupon recreate() cancels the prior incomplete sub. Live on trial / 50off / normal / 30trial. | Curled the served /static/checkouts_v3/stripe-client.js (200, 23,573 B): no load-time mint · stamp-abandon-email 3 · cancel-incomplete 2 · header names the change | NIC-19 ch.1 ✓ |
| 3 Aug | both | Code audit of the LIVE repos became ground truth — replacing an Apr-2026 clone. Established the two estates never share an abandon mechanism, and that pay-click identity, provision_buyer_from_intent, UTM→Stripe metadata writes and the server Meta CAPI webhook were already built and must never be re-proposed. | Read MovementAthlete-Django + MovementAthlete-React-ProdTemp directly | doc ✓ |
| 31 Jul | quiz-checkout | Quiz checkout shipped; quiz root → /2026-quiz-checkout-v1/ preserving the query string; password login gate closed; buyer excluded from abandon sends via mark_purchased. | Live page reads + Aga confirm 2 Aug | shipped |
| 21–22 Jul | both | Server-side Stripe → GA4 + Meta CAPI purchase webhook (/payments/analytics/stripe-purchase/, event_id = PI id); Google Ads leads + value-bearing purchases; GTM published. | 200 OK on a real USD 9.99 payment | shipped |
| Task | Time | What it is — and why it is still open |
|---|---|---|
| NIC-16 | ~10m | 🔴 DO THIS FIRST — it is a measurement, not a build. One purchase with ?utm_source=nictest → read the Stripe intent metadata. The write already exists on both estates, so this test may close the task outright and tells us whether the real gap is only first-touch rehydrate for bare landings. |
| NIC-19 ch. 4 + 5 | ~1h | ch.4 debris sweep: /lead 404 · past_due backlog · success-page UI/analytics gate on succeeded · RETURN_URL TLS (proxy reflection, not a source typo). 🔴 ch.5 pe= recipient → Stripe metadata — THE EMAIL GATE, see below. |
| NIC-30 + NIC-37 + NIC-46 | ~60m batched | One React rebuild+redeploy closes all three. Re-verified 3 Aug: the site still serves index-CEOCAHYQ.js, the same hash the defect was filed against — 7 app-test hits, addToCartValue:0 on 1wk_trial, the dead Buy button, and two "4.9★" strings against the settled 4.7 ruling. |
| NIC-34 | ~1h | Quiz success page needs an fbq Purchase with eventID parity against the CAPI webhook that already exists. Halved by the audit — do not scope it as greenfield. |
| NIC-45 | ~30m | Phase 2, correctly deferred — carry OBSTACLE / FUTURE_STAKES / AGE / GENDER into the checkout so recovery emails stop being generic. Nothing waits on it. |
| Question | Quiz estate | checkouts-v3 |
|---|---|---|
| Is there an abandon email? | Yes — subject "Your login is in this email" (free first workouts + login details) | No — none today. An hourly job can TAG those people into AC for the recovery lane. Tagging is not a send — never report it as an abandon email. |
| Who gets it | Someone who opened the CHECKOUT page with an email on it (almost always prefilled by the quiz handoff) and did not buy. No password, no card needed — landing on checkout with an email is enough. | n/a |
| When | ≈1 hour after the checkout visit. | n/a |
| What does NOT trigger it | Starting the quiz. Typing an email earlier in the quiz and never reaching checkout. | n/a |
| Who is visible in Stripe | Recovery is fed by stamp-paywall-view (Redis), not Stripe buyer_email. | Typed a valid email → Stripe customer + open payment/sub created, email on both records → visible even if they never pay. Bounced without typing an email → not in Stripe at all (the 3 Aug defer-mint working). |
The two emails everyone mistakes for the abandon. "…your results are ready!" fires when they finish the assessment; "One Last Step: Verify Your Account" fires when the account is created/verified. Both hit the same person at almost the same second, so they sit together in the log and look like the abandon. They are not it, and "results are ready" was NOT rewritten into the abandon. The abandon is ~1h later, so it can never appear in the same second. Separately: live sending was held off by a safety switch on Nic's side — fixed 4 Aug, a new build was deploying to Azure Prod. ⚠️ Until that build is confirmed live, "no abandons in the log" proves nothing either way — verify before quoting it.
"Gave us their email but never reached checkout" is a DIFFERENT audience — and the abandon email cannot see it. That lane only knows people who hit the checkout page. Email-only quiz drop-offs sit earlier in the quiz / in ActiveCampaign, and are a separate segment we can build if we want it. Do not assume abandoner counts include them, and do not size the recovery opportunity as if they did.
Coupons & affiliates — Nic's recommendation, 4 Aug. Keep the free trial and the quiz checkout clean — no coupon box on either. Affiliates go to the FULL-PRICE checkout, which already has a coupon field; a prettier affiliate link can just redirect there. That also covers future coupons for our own list — same page, same box. A separate affiliate checkout is optional and branding-only — more to maintain, and not needed to support coupons. Do not scope one as a requirement.
The email-revenue attribution chain is 2 legs of 3 complete. ✅ Leg 1 — AC estate reached 100% UTM-tagged (1,101/1,101 links, 0/21 automations dark) on 3 Aug. ✅ Leg 2 — both checkout estates already flatten utm_*/gclid/fbclid into Stripe metadata at create-intent. ❌ Leg 3 — NIC-19 change 5, the pe= recipient field, ~30 minutes. Until it lands we can say "email made this sale" but never which contact, which automation, which slot — so the Email/Lifecycle fleet stays DARK on revenue against its 29 Aug charter gate, and the Evolution Law cannot name a losing slot on conversions.
| System | Status | What it needs |
|---|---|---|
| RevenueCat | healthy | Source of truth — 194 subs. Nothing to fix; it feeds tasks 1, 2, 5. |
| Amplitude | connected · partial | RC→Amplitude connected. 8 of 15 lifecycle rc_* event types live in Amplitude · auto-checked 12 Aug Task 1 — enable the remaining lifecycle toggles + set $amplitudeUserId. |
| AppsFlyer | events broken | af_purchase fires ~2× vs 194 subs → every paid app channel unmeasurable. Task 5 ★ repairs all three at once. |
| Meta Ads | optimising wrong | Trained on 5,171 Leads vs 172 Purchases. Tasks 2, 3, 4, 6. |
| Google Ads | signal shipped · verify | Now receives Leads + value-bearing Purchases via independent GTM tags (Nic 20–21 Jul, Conv ID 18226881983). ⏳ confirm in Ads UI. Tasks 8, 13, 16. |
| GA4 | partial · fix shipped | App events good; web purchase was 1.52× / 81% capture — Nic 21 Jul: server-side Stripe webhook + transaction_id dedupe live. ⏳ re-verify. Tasks 9, 10. |
| Apple Search Ads | no cost data | Best-converting paid source but spend not flowing → CAC unknown. Task 7. |
| Web quiz + checkout | server-side shipped · verify | Where every ad dollar lands. Nic 21 Jul: server-side Stripe webhook + dual-checkout (new + legacy) capture live. Task 9 — verify on fresh data. |
| Native app | identity missing | No shared user id across SDKs. Tasks 17 + 18 — external RN team, store release. |
| Backend / CRM | not exporting funnel stage | Task 11 ★ export “last funnel step” to Customer.io/AC → ~$1–2.6K/mo free retargeting. |
| Intercom | push live · ~41K reachable | Checked 10 Jul: ~40,900 signed-up app users are push-reachable NOW (no release) → the no-workout/no-trial retargeting can go by push today. But anonymous never-signed-up installs are not in Intercom (0 of ~41K devices lack an email) → Task 12 needs the RN loginUnidentifiedUser() change. |
RevenueCat → Amplitude — live event status: 8 of 15 lifecycle rc_* event types live in Amplitude · auto-checked 12 Aug Enable any remaining lifecycle toggles + reconcile ±5% vs RC (Task 1). The 06:00 job re-checks daily.
Conversion tracking shipped (Nic): Google Ads now sees Leads (generate_lead GTM tag) + value-bearing Purchases (value + currency + transaction_id, both new & legacy checkouts); and a new server-side Stripe → GA4 + Meta CAPI webhook (/payments/analytics/stripe-purchase/) fires purchases even when the browser fails — 200 OK on a real USD 9.99 payment. Full analysis + screenshot proof: docs/company/ANALYTICS/TRACKING-SYSTEM/. ✅ 22 Jul: GTM published (Nic). ⏳ Ads-UI conversion check sequenced after checkout testing (Task 19).
begin_checkout is page-load-inflated (3,037 fires/14d vs ~2 real intents/day) — any funnel % using it is fiction; fix = fire on Pay-tap only + session dedup (part of Task 9/10). · 32 new app events (onboarding, workouts, paywall) started flowing into GA4 ~8 Jul — good, and they power the per-source funnel. · The web purchase event detail lives in Task 9.⛔ SETTLED — don't re-propose
The arguments that were heard and answered. 8 of 12 still binding. Every ruling carries what would legitimately reopen it — bring that evidence, not the argument again. Generated from canonical_strategy.json on every refresh; status is verified against live sources, never inherited from the source doc.
Decided 2026-07-08 · source: Decision Matrix §1.4 + §7 · SoT §3 · status verified 2026-08-04 vs CHECKOUT_ATTRIBUTION_CODE_AUDIT_GROUND_TRUTH_2026-08-03.md §0
Decided 2026-07-08 · source: Decision Matrix §7 · status verified 2026-08-04 vs canonical_facts.rc_events_live (8 events, as_of 2026-08-04) + email self-learn W32
Decided 2026-07-08 · source: Decision Matrix §6 + §7 · SoT §3 Tooling · status verified 2026-08-04 — no contrary evidence in the estate
Decided 2026-07-08 · source: Decision Matrix §7 · status verified 2026-08-04 vs cockpit Funnel 1 · Web tab per-screen data
Decided 2026-07-08 · source: Decision Matrix §7 · status verified 2026-08-04 vs CHECKOUT_ATTRIBUTION_CODE_AUDIT_GROUND_TRUTH_2026-08-03.md §0 (served-file read, 3 Aug)
Decided 2026-07-08 · source: Decision Matrix §7 · §0 "we may not have the volume" · status verified 2026-08-04 vs canonical_strategy.facts.measurement_gate
Decided 2026-07-08 · source: Decision Matrix §1.1 · status verified 2026-08-04 vs TMA-PAID-EXPERIMENT-ROADMAP-JUL2026.md
Decided 2026-07-08 · source: Decision Matrix §1.2 · status verified 2026-08-04 vs TMA-PAID-EXPERIMENT-ROADMAP-JUL2026.md L186
Decided 2026-07-08 · source: Decision Matrix §7 · §1.2 · status verified 2026-08-04 vs TMA-PAID-EXPERIMENT-ROADMAP-JUL2026.md
Decided 2026-07-08 · source: Decision Matrix §7 · status verified 2026-08-04 vs canonical_strategy.facts.never_lifetime
Decided 2026-07-08 · source: Decision Matrix §1.3 · status verified 2026-08-04 vs SPRINT_DOC_REGISTRY.md changelog 2026-07-10
Decided 2026-07-08 · source: Decision Matrix §7 · §1.3 · status verified 2026-08-04 vs SPRINT_DOC_REGISTRY.md changelog 2026-07-10
| Document | What it is | Audience |
|---|---|---|
| Growth Plan ↗ | Plan of record — two funnels, Sprint Contract, lever map, to-dos (its §5 dashboard = this cockpit). estate-synced daily since 20 Jul (source recovered into the shared repo — headline numbers auto-refresh) | Aga + Nic |
| Decision Matrix ↗ | Approved decisions + Sprint Contract §0.5, dated targets | Aga + Nic |
| Strategy Source of Truth ↗ | Wins on any contradiction — amend first, propagate after | Aga + Nic |
| Sprint brief — Nic ↗ | Nic's build list: wiring tiers, A/B #17, events | Nic |
| RC→Amplitude Taxonomy ↗ | LOCKED tracking plan — canonical rc_* names, namespace law | Nic + analytics |
| Native Event Catalog ↗ | Native app event catalog & wiring spec | Nic |
| Scoreboard Operating Model ↗ | How the numbers flow: pulls, gates, Sheet, cadence | Aga + Nic |
| Measurement Architecture (internal) | How our tracking works end-to-end (ad→lead→paid→retention) — the data-room measurement doc; incl. the 21 Jul conversion-tracking delivery + screenshot proof. docs/company/ANALYTICS/TRACKING-SYSTEM/ | Aga + Nic + buyer (DD) |
| Retention Plan ↗ | R1/R2/R3 churn defense (folded in as Retention-Now) | Aga + Nic |
| Quiz Funnel Versions ↗ | The three live quiz funnels compared — traffic by source, funnel, AOV, retention, lead quality per version; Nic's 7 Aug documentation recorded against a live wire-check (7 Aug 2026) | Aga + Nic |
| Sprint Doc Registry ↗ | THE list — folded-in map, update protocol, sharing boundary | Aga + Nic + CEO fleet |
| 📚 Full docs hub ↗ | Everything above in one page (+ Growth North Star Hub, Nic growth brief) | Aga + Nic |
📚 THE SEO DOCS (canonical pair, tabbed): TMA + BWA/channel master = marketing/seo/TMA_SEO_GEO_ANALYSIS_AND_PLAN_2026-07-20.md (v1.3 — §12 TMA deep-dive · §12.6 priority queue P1–P15 · §12.7 standing rules/cadence) + its browsable HTML twin (same folder, tabs: CHANNEL PLAN | TMA DEEP-DIVE | 🤝 AFFILIATE SEO | 🍎 ASO | 📮 OUTREACH — new 24 Jul, = §12.10: affiliate content BLOCKED for SEO, commercial-intent traffic routes to the quiz; BWA buying-intent = 6 clicks/28d; own product beats commission ~10×. 🔴 SEO = lane 1 of 5 — the affiliate strategy folds one lane at a time: SEO ✅ → blog/content pages (next) → YouTube → social → email, each ruling in its own doc, all inheriting one channel-agnostic doctrine (EXPERT_PROMPTS/AFFILIATE/). Never re-derive doctrine per channel. Full verdict marketing/AFFILIATES/AFFILIATE_INCOME_ANALYSIS_2026-07-24.md, fleet /affiliate-income, and the cockpit's own 🤝 Affiliates tab). Fleet constitution: marketing/seo/PAGE_CONVERSION_FLEET_PLAN_2026-07-21.md. W1 shipped (23–24 Jul): 7 TMA pages live w/ conversion layer + homepage H1/title · 7 pre-footer banners fixed · nav+footer chrome → quiz root · 19 typo fixes · best-app rebuild in gates. 🔴 measurement gate: quiz-root 301 strips UTMs — Nic one-liner pending; all UTMs dark until then. NEW rules: quiz-ROOT links everywhere · L2 video-embed on every touched article · pricing law: content pitches $157/yr (~$3.02/wk), $24.97/mo BANNED.
📮 OUTREACH — publisher placements & partner reactivation · owner tma-outreach-lead · run it with /outreach-fleet · status 5 Aug: BUILT, GATED, and deliberately PAUSED on new cold pitching
🤖 START HERE — the full handoff is §9 of marketing/seo/OUTREACH_MASTER_2026-07.md, rendered as the 📮 OUTREACH tab of the SEO master HTML. §9 alone tells any person or LLM where the lane is, what exists, what is open and on whom, and what happens next. §0 is the 60-second version.
🔴 The strategic answer (4 Aug, §7): 1,211 AI-engine sessions over 180 days produced ZERO attributed leads — against ~12 expected at the site rate (p≈1/180,000, so not noise). The visitors are fine (homepage 169s, /progressing-with-bodyweight/ 541s, and they reach /pricing/) — the counting is broken, and it is our own doing: every internal quiz CTA carries utm_source=tma, and GA4 restarts a session on campaign change, so a ChatGPT visitor's origin is erased the instant they click. Cross-domain is already configured, so those UTMs buy nothing. Verdict: do NOT fund more listicle pitching yet — not because the mechanism is disproven, but because we could not tell if it worked. Fix AGA-73 → wait 28 clean days → re-read → then decide.
Where it stands: 3 cold pitches sent 31 Jul, 0 replies, their ONE follow-up due 5–7 Aug (then the campaign is closed — no third touch, ever) · 3 partner emails written and gate-passed, held for Aga (AGA-97) · 🔴 Rick Donohoe is the one that matters: 65 clicks, 2 customers, £37.47, actively converting, never once contacted.
Two gates now run in code: tma-outreach-gate checks the EMAIL · tools/seo/outreach_lint.py checks the DOCUMENTS the email is written from and blocks --send on failure — because the 31 Jul near-miss was a document, not an email. Adversarially tested 8/8 caught, 3/3 legitimate ignored; it immediately found a real miss the human review had passed over.
🔴 Open on Aga: send/hold the 3 partner emails · commission TERMS, unratified since 25 Jul — the first "what are the terms?" holds a live conversation indefinitely · 2 web forms (AGA-29) · watch BOTH inboxes (AGA-30 — the 31 Jul pitches reply to hello@, everything since to hi@). Open on Aga: AGA-73 (the internal-UTM hygiene fix).
🏛️ SEARCH FLEET (24 Jul): chartered under the CMO — tma-seo-growth-owner executing owner · lanes SEO/AEO/GEO/ASO-parked · daily brief (marketing/seo/SEO_DAILY_BRIEF.md: AGA TODAY ≤3 · NIC TODAY ≤3, zero tokens) · Mon self-learn adjudicates the 14-day watches + writes the weekly CMO/CEO report. 🤖 AI-SEARCH SQUAD: ChatGPT referrals = 70–100% post-landing survival (Nic, REPORTED) → 2 new seats: tma-ai-citation-hunter (supply: citations/panel/listicles) + tma-ai-landing-optimizer (demand: pre-sold visitor → quiz-start); GEO items promoted to the FRONT of the weekly batch; north star = AI-referred leads→customers. Week-1 order: Nic S1 → auto link-migration → doorway (=citable object) → GEO ×2 → Aga's 2 words → listicle send → schema (Nic) → 8 light passes → 1–2 rebuilds. Master: marketing/seo/TMA_SEO_GEO_ANALYSIS_AND_PLAN_2026-07-20.md §12.8–12.9.
GSC 28d: BWA 3160 clk · pos 5.5 · TMA 2014 clk · pos 8.6. Organic quiz-starts: 8/7d · 44/28d.
Link integrity: PASS — 19 published links preserve UTMs + _gl.
SEO work logged: 31 title/meta live-changes · 5807 total actions (last 2026-08-11). conversion-layer draft live-pending (page 253529).
AGA TODAY (≤3): 1) 🟢 GO/NO-GO: mail a 500-contact test segment of the 6,813 app-side contacts who finished the survey but never did a workout (NP verdict lever 1, $0, ranked DO-FIRST). The fleet drafts the activation email through /email-fleet + segment_guard.py and shows you the rendered email + segment PASS before anything sends — this row is only your GO to draft-and-show. Rates behind it are MODELLED (skeptic-flagged): read the 500-test click rate before touching all 6,813. (5m) 2) 🟢 GO/NO-GO: give '758 Nurture Sequence 2' (14,600 contacts) a quiz-root CTA slot — NP verdict lever 2, $0 ('fix the tap before the bucket'). Email currently sends the $97 quiz ~1 lead/8 days while 14 sales came from everywhere else. SISTER ROW to AGA-129 (weekly broadcast repoint) — one GO can cover both. Evolution-Law compliant path: ONE slot change through /email-fleet, not an arc rewrite; fleet executes end-to-end on your GO. (5m) 3) 🔴 TELL THE PHP DEV: calisthenicsacademy.co's Divi is 4.4.8 — the "only PBE" assessment is WRONG, and upgrading PHP first will break the site. Measured live 10 Aug: the ACTIVE THEME on CA is Divi 4.4.8, and Elegant Themes only added PHP 8.0 support in Divi 4.8 — so the theme predates PHP 8 support by four minor versions. Page Builder Everywhere is real but is NOT the only blocker and is the smaller one (its entire footprint is ONE copyright line in one widget area — the widget is Divi's own, so it moves to Divi's native empty Footer Area #1 and PBE deactivates; no replacement plugin). Likely cause of the wrong assessment: BWA runs Divi 4.27.7 (per NIC-14), so a check done there would look clean — CA was never separately verified. ⚠️ We CANNOT read PHP version from outside WP Engine, so I cannot tell you what PHP CA is on today — ask the dev. THE ASK: before the dev touches PHP on CA, they update Divi first (Elegant Themes Updater is installed and active — needs the ET API key, so confirm the Divi licence is still live), and ideally the whole job moves onto Rocket staging as part of the migration instead of running on the live site. Full plan, plugin disposition table and verification commands: docs/company/HOSTING_MIGRATION_2026-08/CA_PHP8_DIVI_UPGRADE_ADDENDUM_2026-08-10.md (10m)
NIC TODAY (≤3): 1) 🔴 REPOINT 'QLG | ADV+ OFF | MC | Control' TO THE QUIZ ROOT — 5 min in Ads Manager. All 4 of its live ads still land on https://quiz.themovementathlete.com/2026/, the old page that does NOT show the 97 dollar offer. Change the destination on all four to https://quiz.themovementathlete.com/ (the ROOT). Nothing else: leave url_tags, budgets, geo and the objective exactly as they are, and do NOT pause the campaign. EXACT LIST — adset 'General / T2 / FM 30-54 US/Canada' (GBP 9.62/day, geo US+CA): ad 120253084939360728 and ad 120253084939340728. Adset 'General / T2 / FM 30-54 AUS, FR, UK SING, NZ' (GBP 9.62/day, geo NZ GB IL AU FR): ad 120245527336080728 and ad 120245527336070728. WHY THIS AND NOT A PAUSE — measured live 11 Aug on the Meta API, 30 days, unified attribution: MC Control produced 129 leads at GBP 2.43 each, the biggest lead source on Meta (56% of the two QLG lines' 230 leads; 57 in the last 7 days). Pausing would cut Meta lead volume by more than half in the window we are banking leads for Black Friday. Repointing keeps all 129 leads/month and sends them to the page that shows the 97 AND enrols them into 993 SALES SEQUENCE, whose 10 Sep gate the whole paid plan waits on. Zero cash change either way — the account's daily budget is GBP 45.48 before and after. This is also exactly what Aga's 10 Aug ruling asked for ('RE-AIM into the ONE geo-locked lead line, quiz root'); NIC-97 was built as a clone, which the spec allowed, so MC Control itself was never touched. TWO OPTIONAL EXTRAS while you are in there: (a) drop IL from the second adset's geo — Israel is the only country in this campaign not on the buyer list (US UK CA AU DE SE NL FR); (b) pause the third adset 'General / T2 / FM 22-65 North America' — it holds a GBP 7.00/day budget but all SIX of its ads are paused, so it cannot deliver and spent GBP 0 in the last 7 days. Hygiene only, frees nothing. VALIDATION (2 min): after saving, open each of the 4 ads' preview and confirm the click goes to the quiz root and lands on the page showing the 97 offer, then confirm the campaign is still ACTIVE and its daily budget is unchanged at GBP 9.62 + 9.62. (5m) 2) 📲 RN TEAM — IN-APP PAYWALL UI REBUILD. Sent to the RN devs 5 Aug; awaiting their estimate then build. 🔴 THE LINK TO SEND (standalone dev site, 6 Aug — the claude.ai artifact links died twice; Aga ruled the handoff gets its OWN site, never a page under the revenue plan): https://tma-paywall-brief.netlify.app/ — the brief; it links the design contract (/spec.html) and the package zip on the same domain. All three verified live 6 Aug. Scope is UI-ONLY across ALL THREE existing pricing pages (full price / 30% off / 50% off) — RevenueCat, purchase, restore, terms and analytics already wired; offers already configured. Estimate 5h max in 6 milestones. THE ASK ON THEM: all three pages on the ONE component via variant='full'|'off30'|'off50', not three rebuilds — three drifting copies is how the current bug survived, and separate rebuilds roughly double the cost. WHAT TO WATCH FOR ON RETURN: (1) no price literal anywhere in the UI — every figure derived from the store product at render time, retry state if offerings fail; (2) they confirm the estimate BEFORE starting rather than pushing past 5h; (3) the on-device pass includes a NON-USD currency, the actual defect being fixed. Also in the package: 7 broken strings below the fold. NOT in scope (Aga, 6 Aug): the RN-1 identity + push add-on — Nic is already working on those. (15m) 3) 🤖 FLEET — 'THE ENGAGED LIST' IS NOT ONE LIST. ✅ DIAGNOSED 11 Aug, one decision left. Live AC tag census: 7 Day Engagers 1,666 · 30 Day 2,567 · Engaged(237) 2,912 · 60 Day 6,153 · 90 Day 7,172 · 180 Day 12,296 · 360 Day Engagers 19,826 · Active Users 14,054. AGA-119 says 'the engaged (~6,367)' and names NO tag — that figure sits between the 60-day (6,153) and 90-day (7,172) cohorts, so the audience for the plan's headline email has never actually been defined. 🔴 THE CHOICE SWINGS THE AUDIENCE 12x AND THE MODELLED PRIZE WITH IT. Two further findings: (1) tag 237 'Engaged' — the one the cockpit reports as 'engaged-reachable' — appears in exactly ONE file in the estate (tools/email-plan/email_cockpit_sync.py:60) and in no doc, so it is a DASHBOARD METRIC, not a proven send audience; its fall from 6,038 (31 Jul trust audit) to 2,912 is consistent with a rolling-window tag shedding the June-July intake wave (2.4x May, Facebook-heavy — CONFIRMED timeline event), i.e. mechanical decay, not list death [PLAUSIBLE, not confirmed — the maintenance rule is undocumented]. (2) History warns against reaching for the big tags: past promos went to 12,000-44,747 and 60-90% of every one went to people who had not engaged in a year. REMAINING: pick the tag with tma-email-deliverability + tma-segment-integrity-officer, write the rule down, and register a >20% drift check in estate_selfcheck.py. (60m)
🌱 Where the leads come from (generate_lead by lane · 7d / 28d)
Attribution-dark: 24.2% (target ≤25% — the leads hiding here are mostly owned content + YouTube)
| 7d | 28d | share | |
| TMA.com content CTAs | 29 | 62 | 13.6% |
| BWA article CTAs | 2 | 8 | 1.8% |
| YouTube descriptions | 3 | 3 | 0.7% |
| AI answer engines | 6 | 6 | 1.3% |
| Organic search (untagged) | 8 | 44 | 9.7% |
| Organic social | 3 | 7 | 1.5% |
| 2 | 3 | 0.7% | |
| Other referral | 2 | 17 | 3.7% |
| Paid ads | 78 | 188 | 41.3% |
| Other | 2 | 4 | 0.9% |
| 🔧 Internal / QA traffic | 1 | 3 | 0.7% |
| ⚠ Attribution-dark (unmeasurable) | 23 | 110 | 24.2% |
| TOTAL | 159 | 455 |
Auto-refreshes daily (launchd com.tma.seodailysync) + on every SEO session; source of truth = master .md v1.2 + wp_changelog.jsonl + SEO_HUMAN_QUEUE.json.
✅ 31 BWA page titles/metas refreshed LIVE (keyword-front, year, on-brand; originals in wp_changelog.jsonl, revertible; Google recrawling). ✅ WordPress write access unlocked both sites (Yoast meta API-writable on BWA). ⏳ best-calisthenics-workout conversion-layer built as a DRAFT (page 253529, live untouched, 9 blocks + QA clean) — awaiting Aga go → apply live + 14-day rankings watch. ✅ AI-citation baseline (AI_CITATION_PANEL_2026-07-21.md). ▶ IN PROGRESS: the citable Assessment landing page (deep-research + agent build).
A competitor (Wild Dynamics) owns the "calisthenics assessment / find my level" query — our own product's query. The quiz app itself can't be ranked/cited (it's a JS web-app); we need a citable content page about the assessment (→ button into the quiz). We also own no "best app" category slot and are mis-framed "not for advanced". Full lead-gap map: AI_CITATION_PANEL_2026-07-21.md.
This is a CONVERSION + CTR play, not a ranking play. Organic is already the #2 channel (33,714 sessions/90d, behind only Direct) — ~9,800 content sessions/mo become only ~146 quiz-starts/mo (1.5%). BWA already ranks (pos 2–5 head terms); the click ceiling is AI Overviews. The lever is converting traffic we ALREADY have. Ranking/GEO moat work is real but 3–6 months = bench behind the churn fix.
Adversarial audit findings (20 Jul, live-checked): ① Crawl-delay: 10 sits in BOTH robots.txt — Bing honours it, and Bing powers ~87% of ChatGPT citations: we are literally throttling ChatGPT's index (5-min fix). ② ChatGPT already recommends our free assets + the QUIZ unprompted (homepage 253 · quiz 75+ · best-program 50 · 28-day challenge 32 · handstand PDF 29) — LLMs recommend TOOLS; double down on the citable objects. ③ TMA is absent from every ranked "best calisthenics app" listicle (Fitloop, GainStrong, Caliverse, Cali Move…) — the exact lists ChatGPT reads for the money query. ④ BWA hands only ~79 sessions/mo to product properties — the blog→product bridge barely exists. ⑤ GA4 "conversion" is mis-set to session_start — organic→lead is invisible until fixed.
| Domain | Clicks | Impressions | Avg position | Read |
|---|---|---|---|---|
| bodyweighttrainingarena.com (BWA) | 3,529 | 1.9M | 4.7 | the bigger asset ranks for high-intent head terms |
| themovementathlete.com | 2,119 | 366K | 8.3 | brand hub; ~35% impressions on off-brand product-review pages |
| Page | Traffic | Verdict |
|---|---|---|
| /best-calisthenics-workout/ | ~800 clk/mo · pos 2–3.5 | THE lever ranks for "calisthenics workout" (476 clk), "workouts", "program", "routine". CTR suppressed to 0.5% (vs 7–10% expected) — AI Overview eats clicks. Fix = convert the ~800/mo already landing here, not chase rank. |
| /elbow-lever-progression/ | 18 clk/mo | low priority "elbow lever" impressions are intent-polluted (plumbing hardware term). Little clean fitness demand. |
| "calisthenics workout plan" 1.1M impr | 0.01% CTR | IGNORE SERP-feature artifact — same page/same position gets 50× the CTR on a real term. Not a real lever. |
best-calisthenics-workout → quiz-starts ~$40–85/mo + measurement · SEO ownerWhat — Add a prominent, UTM-tagged quiz CTA to the BWA page that already gets ~800 organic visits/mo, then (bigger) redesign it as the highest-converting version. MODELLED conversion; CONFIRMED traffic.
Why — The traffic already exists and is mostly wasted. This is a conversion play, not a ranking play — days/weeks, not the 3–6 months SEO usually needs. It also wires the organic→quiz→paid measurement loop that makes this whole tab real. Gated on Task 19 (checkout must work, or we make leads not cash).
best-calisthenics-workout — "Get YOUR personalised plan — free 5-minute assessment" (⚠ "60-second" is BANNED per Aga ruling — quiz avg is 3.2 min) — linking to the quiz with ?utm_source=bwa&utm_medium=organic&utm_content=best-calisthenics-workout.bwa / organic quiz-starts within 7 days; kill if <30/mo (intent mismatch)./quiz-design using the 7-point playbook below — conversion-decided aesthetics, MEDIA_INTEGRITY_LAW, 2× design-director SHIP./quiz-design (conversion-decided aesthetics, MEDIA_INTEGRITY_LAW, 2× design-director SHIP):| 1 · Match intent to offer exactly. Hero = "Get YOUR personalised calisthenics plan — free 5-minute assessment" (outcome-first; "60-second" BANNED per Aga ruling). The quiz literally IS what they searched for. |
| 2 · Frame as a personalised assessment, not a generic free tool — higher perceived value, maps to the product. |
| 3 · CTAs at every scroll depth — above fold, mid-article, sticky/end. The current single bottom CTA is where the traffic leaks. |
| 4 · Real social proof beside the CTA — calisthenics-beginner testimonials from the tagged DB. |
| 5 · Mobile-first + fast — organic search is mostly mobile; dated pages leak hardest there. |
| 6 · UTM-tag every CTA — measures the organic→quiz→paid loop AND makes this tab's numbers real. |
| 7 · Don't touch what ranks — keep the URL, headings, body that earn pos 3.5. Conversion LAYER over the page, never risk the rankings. |
The BWA redesign only pays if the checkout works (Task 19). Beautifully-converted traffic into a broken paywall makes leads, not cash — Task 19 gates the value of the whole play.
Non-technical, click-by-click. Full guide: marketing/seo/DO_NOW_SEO_TASKS_2026-07-21.md. Log into bodyweighttrainingarena.com/wp-admin first. Order: ①② first (biggest + easiest), ⑤ → Nic.
| # | Task · why | Exact steps | Who · time |
|---|---|---|---|
| ① | Remove robots.txt "Crawl-delay:10" — it throttles Bing = throttles ChatGPT indexing | WP admin → Yoast SEO → Tools → File editor → in the robots.txt box delete the line Crawl-delay: 10 → Save. Verify: visit /robots.txt, no "Crawl-delay". (TMA site → Nic.) | Aga · 5 min |
| ② | Retarget the footer "TAKE A FREE ASSESSMENT NOW!" button — it leaks every page's hottest click to a legacy un-tracked page | Divi → Theme Builder → edit the Footer template → click the button's gear → Link tab → paste https://quiz.themovementathlete.com/?utm_source=bwa&utm_medium=blog&utm_campaign=footer&utm_content=footer-cta → ✓ → Save. | Aga · 10 min |
| ③ | Bing Webmaster + IndexNow — the ChatGPT front door (instant indexing) | bing.com/webmasters → sign in → "Import from Google Search Console" (verifies both sites) → Sitemaps: submit sitemap_index.xml → URL Submission: submit /calisthenics-level-assessment/. | Aga · 25 min |
| ④ | Fix the page title (show the year) — Divi is overriding Yoast | Divi → Theme Options → Integration (ensure Yoast controls the title); then Pages → the assessment page → Yoast box → confirm SEO title has "(2026)" → Update. If fiddly → Nic. | Aga/Nic · 5 min |
| ⑤ | Consolidate SEO plugins (keep Yoast) — 3 conflict; ⚠️ redirect risk | HAND TO NIC. Backup first → confirm redirects live in the Redirection plugin → deactivate All in One SEO + SEOPress → keep Yoast. Do NOT DIY. | Nic · 30 min |
| Tier | Move | Status |
|---|---|---|
| 🥇 T1 | Prove best-cal overlay → roll across top 20 pages (3–5× conversion, same traffic — THE big lever) · internal-link every article → the Assessment page | pilot draft ready; rollout on 14-day-watch pass |
| 🥈 T2 | Build the citable "Calisthenics Level Assessment" page (own "find my level" vs Wild Dynamics) · comparison page + listicle outreach · claim the open "assessment-first/adaptive/injury-safe" category slot | ▶ BUILDING NOW (assessment page) |
| 🥉 T3 | Proprietary 100k-user data pages · programmatic skills hub (skill_tree.json) · digital PR + Bing/IndexNow | compounding, months |
| ⚙️ | Needs Aga/Nic: Bing/IndexNow · plugin consolidation · modified-date un-freeze · GA4 key-event fix · publish gates · outreach send | queued |
⚡ THE EXECUTION MODEL (Aga's "fastest possible way" question — answered): BWA runs WordPress with the REST API live and Application Passwords enabled (verified this session — plus Yoast/SEOPress/Redirection endpoints). One 5-minute password creation (task 0.0) and Claude edits titles, metas, content, creates draft articles, and manages redirects DIRECTLY — no Nic paste-loop. Rule of engagement: Claude does everything Claude can (all writing, all page edits as drafts, all analysis, generators, monitoring); Nic only for true technical (GA4 admin, robots.txt/server, theme/CSS, plugins, data exports); Aga only decides (approves batches, clicks Publish, sends outreach). Safety: Claude never publishes live — everything lands as a DRAFT or revision for one-click approval.
Why — Verified live: BWA's REST API is enabled with Application Passwords. With one credential, Claude executes tasks 0.4, 1.2, 1.3, 1.5, 1.6, 1.7, 2.3, 2.4 itself (as drafts) instead of producing paste-jobs for Nic. This single task collapses the plan's calendar time by weeks. It is also the answer to "how do we publish articles easily" — Claude writes → REST draft → you click Publish.
claude-seo → Add → copy the generated password once..env.local as BWA_WP_USER/BWA_WP_APP_PW + TMA_WP_USER/TMA_WP_APP_PW (never in code — house rule).tools/seo/wp_publish.py — read/update/create posts + titles + meta, drafts-only by default, with a change-log of every edit.Redesign path (the "how can we redesign" question): content-level redesign (overlays, CTAs, capsules, sections) = post-HTML → Claude via API. Site-level design (theme/CSS/templates) = one-off Nic snippet or child-theme change — Claude writes the exact CSS/PHP, Nic installs once.
Crawl-delay: 10 from both robots.txt CLAUDE drafts · NIC uploads 5 minWhy — Audit find: both domains tell crawlers to wait 10s between requests. Google ignores it, but Bing honours it — and Bing's index powers ~87% of ChatGPT citations. We are literally rate-limiting the index that decides whether ChatGPT can cite us (~8,640 URLs/day ceiling, slow fresh indexing).
/robots.txt — no Crawl-delay line.Why — ChatGPT search retrieves from Bing's index — a page Bing hasn't indexed cannot appear in a ChatGPT answer, regardless of Google rank. Bing WMT also ships a FREE AI Performance report (new Feb–Jun 2026) = a zero-cost citation tracker for Copilot/Bing AI. This is the gate to the entire GEO play.
bing.com/webmasters → sign in (any Microsoft account) → "Import from Google Search Console" — verifies both properties in two clicks, no DNS work.sitemap_index.xml, TMA wp-sitemap.xml); submit manually if not.Why — The property's "key event" is currently session_start — meaningless — so every organic-conversion report is polluted, and the real lead signal (survey_started) isn't counted as a conversion anywhere. Until this is fixed we cannot prove any lift from any task in this plan. Also: BWA→quiz is cross-domain; if stitching is off, blog-driven quiz-starts mis-attribute and the overlay test (1.1) can't be read.
✅ 23 Jul — cross-domain half CLOSED (Aga): GA4 "Configure your domains" verified already correct (BWA + all TMA subdomains listed), AND the GTM Conversion Linker typo (bodyweighttrainingare.com → bodyweighttrainingarena.com) fixed + container published in GTM-TKGDZR9 — BWA gclid/ads cross-domain linking now works (allow 24–48h). Remaining in this task: steps 1–2 (key-event toggles) + the 48h verify.
session_start.survey_started, take_personalized_assessment, landed_on_pricing_page (and AppOnboarding_completed)./best-calisthenics-workout/ CLAUDE writes + ships draft via API · AGA picks variantWhy — Our flagship ranks pos 3.5 for "calisthenics workout" (88,956 impressions/28d) but wins only 0.54% of clicks — the snippet loses even when shown. Title + meta + first-200-words is the ONE lever that can't hurt rankings (H1/URL/headings untouched — SEO-owner veto enforced). Honest recovery: 0.54%→1.2–1.8% = +600–1,100 sessions/mo MODELLED.
gsc_pull.py; auto-flag a revert if position drops >0.5 for 2 weeks.Why — We can't manage what we can't see: today we have zero visibility on whether ChatGPT/Perplexity/AIO cite us vs Caliverse/Cali Move/Fitloop for the queries that matter. A fixed panel makes citation share a tracked number, free.
marketing/seo/AI_CITATION_PANEL.md with date-stamped runs; monthly re-run = geo-strategist standing duty.Why — ~9,800 sessions/mo convert at 1.5%. The overlay plan (already written, evidence-led, rankings-safe by the Overlay Law) lifts the biggest pages. Ledger-honest expectation: +50–150 starts/mo MODELLED (L-015). ⚠ Gated on Task 19 checkout — converted traffic into a broken paywall makes leads, not cash.
BWA_CONVERSION_LAYER_PLAN_2026-07-20.md (the open decision from the 20 Jul session).utm_source=bwa), "5-minute assessment" copy (never "60-second" — BANNED).Why — LLMs recommend TOOLS: our own GA4 shows ChatGPT already sends people to the quiz unprompted (75+ landings/180d) with no dedicated public page selling it. /calisthenics-assessment/ already exists and ranks (14 clk/28d, pos 9.4) — upgrading it into THE citable free-tool object is near-zero build for the highest-leverage GEO asset we can own.
/calisthenics-assessment/: answer capsule ("What is the free calisthenics assessment?"), what-you-get, how-it-works, proof, FAQ schema, quiz CTA with UTM. Copy: outcome-first, "5-minute" (never "60-second").Why — The engines already chose these URLs (homepage, /the-best-calisthenics-program/, free guides, 28-day challenge…). Reinforcing an existing citation compounds far faster than begging for a first one — we feed pages the models already trust.
Why — The money query ("best calisthenics app") is answered by ChatGPT from ~9 ranked listicles — TMA is a named winner in NONE (Fitloop, GainStrong, Caliverse, Cali Move, Hybrid Calisthenics own the slots, several smaller than us). Getting added is worth more than any on-site tweak for the recommendation query class. We even have an affiliate program page to offer.
Why — Adding statistics is the single strongest on-page GEO tactic (+≈41% AI visibility), and ~44% of ChatGPT citations come from the first third of a page. Our proprietary numbers (96 levels, 25 pull-up progressions, 100k+ users) are the uncopyable version of this.
Why — Leg-press/cable-machine/gym-gloves/belt reviews (~35% of TMA impressions) train Google AND the LLMs that we're a generic review site — diluting the exact entity ("the progressive-calisthenics authority") that GEO depends on. Audit nuance: calisthenics-adjacent gear (gloves for bars, dip bars) gets KEPT/merged, not pruned.
noindex,follow per URL (Yoast bulk edit) — NOT deletion.Why — A visible current year lifts AI citation odds ~30%; Perplexity cites <30-day content ~82% of the time; live titles still saying "2024 picks" actively signal staleness to both Google and the engines.
skill_tree.json CLAUDE builds generator + pages · NIC hosts · AGA approves structureWhy — The single strongest topical-authority play: nobody else has 102 skills / 496 progression rungs with video, machine-readable and drift-locked. The audit re-scoped this from "months of editorial" to a generator project (1–2 weeks) — the exact pattern that already emits our internal skill-tree docs. Ship by Nov–Dec to catch the January search wave.
skill_tree.json + media DB → 100+ static pages (per-skill: full ladder, capsules, FAQ+HowTo schema, timeline placeholders, hub interlinks) + the master hub page.Why — Today a ChatGPT citation of BWA builds a brand nobody buys. Publisher schema + a visible "a The Movement Athlete publication" byline makes every BWA citation feed the TMA entity — the thing ChatGPT's parametric memory actually learns.
Why — Original research is the highest-citation content type (data-rich pages show ~340% higher AI citation); nobody can copy our progression timelines. Perplexity cites it within days; ChatGPT durably.
Why — Authority for each skill is split across multiple mediocre pages (front-lever spread over 4+ URLs at 0.1–0.4% CTR). One definitive guide per skill concentrates links, relevance, and snippet strength.
Why — Google AI Overviews and Gemini lean on structured data; Person schema builds the Jesse entity. Plugin configuration, not a dev project.
Why — Brand mentions correlate ~3× more strongly with AI citation than backlinks; the same claim distributed across many publications lifts citations up to +325%. This is how ChatGPT's training-memory learns "The Movement Athlete = progressive calisthenics authority."
Why — Reddit/forums feed Perplexity heavily; podcasts (transcripts crawled) build the Jesse entity. Must be genuinely human — astroturfing risks bans and brand damage, so Claude preps, never posts.
Why — "How long will it take me to get X skill?" is un-answerable by a static AI reply — engines cite the tool instead. Reuses skill_tree.json; sequenced after 1.2 proves the tool-citation pattern.
llms.txt both domains (zero cost; helps Perplexity/Claude marginally; NO ChatGPT expectation — OpenAI doesn't read it). Nic uploads.Nothing executed. Expert seat tma-geo-strategist built + registered (reports to SEO owner). The two decisions for Aga: ① GO on Tier 0 (near-free, this week) ② Phase-1 GO on the BWA overlays (1.1, gated on Task 19). Full analysis: marketing/seo/TMA_SEO_GEO_ANALYSIS_AND_PLAN_2026-07-20.html (open locally) / .md (canonical v1.1).
yt_status.json · master doc: marketing/youtube/YT_MASTER.md · goal: thousands of leads + salesThe "84% ads / paid surface" label was a single 28-day ad burst. Over the trailing 365 days the main channel was 77% ORGANIC (~236K views, 55K from search) with ~zero ad spend for 11/12 months. It's a real evergreen-search asset — the problem is conversion + supply + retention, not traffic.
| Stage | What | Target |
|---|---|---|
| 1 · Instrument + convert ≈done | Assessment CTA + UTM on every search-earning video (49 done). Converts the ~20K organic views/mo we already get; makes YouTube→lead measurable for the first time. | ~250–400 quiz-starts/mo · 45–125 trials/mo MODELLED |
| 2 · Rebuild supply + retention | Resume long-form ≥2/mo on proven clusters · retention repair (THE ranking lever) · retitle/re-thumbnail winners (human, 1-at-a-time) · chapters. | ~500–1,000 quiz-starts/mo MODELLED |
| 3 · Scale proven converters | 2nd search cluster (assessment-intent) + amplify the videos GA4 proves convert (organic + targeted paid on known winners). | thousands of leads + hundreds of sales/mo MODELLED |
🔴 CORRECTED 3 Aug 2026: the 'sessions EXPLODED 210→897/28d' claim was an ARTIFACT — editing a description triggers ~4 link-validator hits that GA4 logs as youtube sessions, and a bulk pass over ~870 descriptions manufactured ~3,700 of them. REAL YouTube→site traffic is ~7/day (~199 per 28d), engaging at 38%. Proof: 26 Jul, the one day the job did not run, drew 32 sessions against 700–850 on edit days; plus 0.21% Apple vs 37% site-wide, hour peak/trough 2064:1, Belgium 700 vs US 53, 4-second sessions. YouTube Partner won 24 Jul; the growth machine and 12-lever ledger stand. Leads are genuinely small, not dark — and the honest question is now 'the channel barely sends traffic to the site', not 'the traffic does not convert'. · Stage: CAPABILITY fleet (ruled 3 Aug 2026) — graded on VIEWS, exception-only. Levers: retention recuts + new-upload packaging. Tripwire back to REVENUE: views/28d ≥45,000.
| Videos with the assessment CTA | 49 live |
| Comments posted / remaining | 29 posted · 20 to go (human-paced, ~10/day) |
| End screens | 9 / 0 |
129 REAL youtube sessions (~9.2/day) · 3 leads · 0 checkouts (28d, GA4 pull 2026-08-12)
| ✅ | ✅ DONE (VA confirmed 24 Jul): end screens on the top 9 (Video+Subscribe). Link element was greyed out at the time — now re-doing with links, see T11. | Aga/VA |
| ✅ | ✅ DONE (VA confirmed 24 Jul): all 9 posted comments pinned. | VA (dispatched 21 Jul) |
| ☐ | ✅ MEASURED LIVE 6 Aug 2026 with the new tools/youtube-analytics/comment_audit.py (zero API quota) — THE VA DID THE WORK. All 40/40 videos carry a channel comment; 37/40 are pinned. She executed Days 1-4. 🔴 BUT ALL 40 COMMENTS POINT AT THE RETIRED /calisthenics-assessment/ DOORWAY (utm_campaign=yt_pinned), not the quiz root — because the comments were written in JULY and the 3 Aug quiz-root retrofit rewrote DESCRIPTIONS ONLY. Third surface the retrofit missed, after the channel About text and the end screens. Not the VA's error in any of the three. OUTSTANDING FOR V (12 min): (a) PIN the 3 that are posted but unpinned — FLGLf5R5XkM, 0lPJpN1JmOM, PzJdqNuWwDU; (b) the duplicate cleanup the brief asked for was NOT done — 9 videos carry 2+ channel comments competing for the pin: 4fstTeS-MUQ, TqdYJOGIQsc, 1wWx1304S_A, 1MFRTIH0pz0, SlaCyUauuVQ, lRqo6Tjm2GA, Wa89eMBcqQs, gy6dOjw58_o (3 comments), Ue8_L6msDYc — delete the OLDER one on each. NOT FOR V: the URL swap on all 40 is MINE — comments.update works on our own comments (50 units each, 2,000 total), so it runs once the retrofit frees the quota. Filed as a FLEET row. Re-verify anytime: python3 tools/youtube-analytics/comment_audit.py --file <ids> | VA (dispatched 21 Jul) |
| ✅ | Confirmed 21 Jul: end-screen 'Link' IS greyed out (no approved associated website). End screens use Video + Subscribe for now; assessment link still lives in description + pinned comment. | Aga |
| ✅ | ✅ RESOLVED 24 Jul — CHANNEL IS NOW A YOUTUBE PARTNER (Aga accepted terms; API confirms isChannelMonetizationEnabled=true). The earlier ~845h watch-hour gap estimate is MOOT — eligibility cleared by a route the duration-classified estimate didn't model. 🔴 UNBLOCKS: end-screen LINK element + website CARDS across all 875 videos (was the single biggest conversion ceiling). Also unlocks monetisation on 1.27M lifetime views. NEXT: VA verifies the Link element is actually clickable (Part 4 of the VA sheet). | Aga/Nic (parked) |
| ✅ | ✅ DONE 23 Jul — BRAND BUG closed. All 51 episode briefs specified #0C71C3, HARD BANNED by Brand Brief v1.3 (the only SoT per CLAUDE.md); briefs carried a FABRICATED carve-out ('applies to the app-rebuild, not YouTube') — false. ROOT CAUSE fixed in agents/13-THUMBNAIL-DIRECTOR.md + fleet_config.json (brand_blue -> brand_accent #00D4AA). All 51 episode JSONs rewritten incl. nested frame_primary/frame_alt/shotsheet graphic specs; originals backed up to marketing/youtube/scripts/thumbnail-colour-backup-2026-07-23/. All 49 Notion cards rebuilt. 🔍 LIVE-VERIFIED on the board: 49/49 carry #00D4AA, 0 carry the banned hex. Ath fleet unregressed. Ritesh now sees brand-correct thumbnail briefs. | Aga (decide) + Claude (apply) |
| ✅ | ✅ SUPERSEDED + FIXED 24 Jul. v1 schedule picked 8 on search-demand alone and spanned SIX footage clusters — contradicting the batch-by-footage instruction in the same message (Aga caught it). v2 = TWO clusters, one switch: ring-dips EP001/EP002 → muscle-up EP006/EP041/EP009/EP043/EP039/EP003. Notion prioritised P0-P9 at '03 Ready to EDit'. Message: marketing/youtube/RITESH_MESSAGE_FINAL_2026-07-24.md. 🔴 LESSON: selection must be scored on FOOTAGE CLUSTER + collision + gate score together — demand alone produces an unworkable edit order. | Aga (approve) |
| ☐ | Run yt_lead_report.py weekly from early Aug — first YouTube→lead numbers | Claude |
| ⏸ | Stage 2: resume long-form cadence (≥ 2/mo) + retitle top winners + retention repair (on Aga's go) | Aga+Claude |
| ◐ | EDITOR REBALANCE (decided 21 Jul): Forge = majority; main TMA = protected floor ~2 long-form/mo on proven clusters (ring dips, strict muscle-up, push-up progressions, handstand, planche prereqs, pull-ups) + package-refresh top ~10 winners (no filming) + cut 2-3 reels per long-form. Reversed the earlier 'abandon main TMA' call after the 77%-organic finding. Dependency: Jesse batch-films monthly. | Editor + Jesse |
| ☐ | RETITLE/RE-THUMB BRIEF for the top 5 winners -> marketing/youtube/YT_RETITLE_BRIEF_2026-07-23.md. 🔴 CTR + impressions are STUDIO-ONLY (not exposed by the Analytics API) — so priorities are [MODELLED] from views+retention. READ CTR IN STUDIO FIRST: below ~5.9% = packaging problem (retitle); at/above = retention problem (do NOT touch the title). Ring Dips flagship Ikeg_v5fvz4 = biggest prize (652 watch-h at 27.6% retention). 🔴 4FoCnqGC-o0 (What Muscles Do Push-Ups Work) = DO NOT RETITLE — 59.3% retention, it works; the lever is EXPANDING the 93s video. Discipline: one change, 14-day read, revert if flat. | Aga (approve) + Claude (applies 1 at a time) |
| ☐ | 🔴 MEASURED LIVE 6 Aug 2026 — NOT FINISHED, and the finished half is on the wrong URL. The VA reported Part 4 done; audited with the new tools/youtube-analytics/endscreen_audit.py (reads end screens off the public watch page at ZERO API quota): 5 of 9 carry the Link element, 4 DO NOT — missing on 4FoCnqGC-o0, y7X-GuNZF_Y, OyRj1z3sNgs, Z9C7aCRCgn8. AND all 5 that exist point at https://themovementathlete.com/calisthenics-assessment/?utm_source=youtube&utm_medium=organic&utm_campaign=yt_endscreen — the doorway Aga's 3 Aug ruling took OUT of the YouTube path. Not the VA's error: the brief specified that URL on 24 Jul, before the ruling, and nobody re-read it. GOOD NEWS: the Link element works, so YPP genuinely unlocked it and the 60-second test passed. TO DO: (a) swap the target on the 5 existing to https://quiz.themovementathlete.com/?utm_source=youtube&utm_medium=organic&utm_campaign=yt_endscreen ; (b) add it to the 4 missing with that same URL. End screens have NO Data API endpoint — creation is Studio-UI only, so this stays with V — but VERIFICATION is now free and repeatable: python3 tools/youtube-analytics/endscreen_audit.py --expect-url https://quiz.themovementathlete.com/ <ids>. || ORIGINAL: PART 4 — add the UTM'd assessment LINK to end screens on the top 9, now that YPP unlocked the Link element. 🔴 URL CORRECTED 6 Aug 2026 — USE THIS ONE: https://quiz.themovementathlete.com/?utm_source=youtube&utm_medium=organic&utm_campaign=yt_endscreen . The old instruction said https://themovementathlete.com/calisthenics-assessment/?...&utm_campaign=yt_endscreen, which CONTRADICTS Aga's 3 Aug ruling that the assessment page comes OUT of the YouTube path: all 869 video descriptions were moved onto the quiz root, so end screens were about to become the last YouTube surface still feeding the retired doorway — and nobody would have noticed, because the old URL still 200s (verified live, 0 hops). A dead instruction that still resolves is worse than one that 404s. 🔴 VA instructed to TEST one video first and STOP if Link is still greyed out. Then ~40 more in batch 2. Context: flagship end-screen click rate is 0.6% vs 1.3% channel average — with no link element available until now. | VA (dispatched 24 Jul) |
| ☐ | RETENTION RE-CUT BRIEF -> marketing/youtube/EDITOR_BRIEF_RETENTION_RECUTS_2026-07-24.md. 🔴 KEY MECHANIC: YouTube Studio Editor can TRIM/CUT a live video IN PLACE (same URL, keeps all views/rankings/comments) but CANNOT add footage. So: flagship Ikeg_v5fvz4 (381.6K impressions, 9.8% CTR, 27.2% retention, 3.1K lifetime watch-h) = TRIM ONLY, NEVER re-upload — 4 years of authority at stake. RC001-003 scripts contain a NEW cold open = requires re-upload; their originals earn only 26-38h/yr so that's acceptable, but AGA MUST DECIDE per video. | Ritesh (brief written, unsent) |
| ☐ | RC001-003 re-cut decision. CLAUDE'S RECOMMENDATION: TRIM IN PLACE, do NOT re-upload, and do NOT prioritise them. Originals earn only 26-38 h/yr each (~90 h/yr combined). A re-upload starts at zero authority, competes with the original, and the new-cold-open scripts are only needed for the re-upload path. The topics are ALREADY covered by the new production queue (11 handstand eps ready) — if we want a better shoulder-mobility-for-handstand video we should PUBLISH A NEW ONE, not resurrect a weak old one. Contrast: the flagship trim is worth doing because it's 3,100 lifetime watch-h at 27% retention — 30x the scale. | Aga (decide) |
| ✅ | ✅ ANSWERED 6 Aug 2026 BY ME — no human needed, and the answer is NO. Queried the YouTube Analytics API (insightTrafficSourceType) month by month, then bisected by week. ADVERTISING views: Jan-15 Jun = 0 · 16-30 Jun = 23,545 · 1-7 Jul = 45,523 · 8 Jul onward = 1, 7, 1 · ALL of August = 0. So a campaign ran ~16 Jun - 7 Jul, bought 69,068 views at 4.4 s/view (5,043 minutes = 84 watch-hours total), and has been OFF for a month. NOTHING TO KILL. Its window maps onto the 2-12 Jul LIFETIME $297 promo plus a late-June warm-up, so it was almost certainly the promo push. 🔴 THE FINDING THAT OUTLIVES THE TASK: those 69,068 views are 40% of the channel's Jan-Jul view total and they are worth ~4 seconds each — so ANY view-count read spanning 16 Jun - 7 Jul is inflated by paid garbage. Same class as the crawler artefact in yt_traffic_truth.py, different source. Do not compare a Jun/Jul view number to anything, and if this campaign type is ever re-run, kill it on sight. || ORIGINAL: ⚠️ L9/P011: ADVERTISING traffic = 69,076 views → 84 watch-hours (~4.4 sec/view), 3rd-largest view source. Skeptic catch. Is ANY YouTube ad campaign live? If yes it is buying 4-second garbage views — kill it. Answer + action by 28 Jul. | Aga/Nic — SAME DAY |
| ✅ | ✅ PROVEN 6 Aug 2026 IN PRODUCTION DATA — no DebugView walk needed. The gate asked whether generate_lead fires with source intact. Live GA4 (property 177471782, last 28d): generate_lead fired 377 times, attributed across 17 NAMED sources — tma 36, (not set) 29, startup2013.lpages.co 10, bodyweighttrainingarena.com 5, l.facebook.com 4, youtube.com 3, ActiveCampaign 2, linktr.ee 2 and more. Source attribution is intact and the pixel works; the companion events all fire too (form_start 531, form_submit 433, add_to_cart 1,160, begin_checkout 871, purchase 517). ⚠️ ONE AMENDMENT TO THE TASK AS WRITTEN: it specified the chain '/calisthenics-assessment/ -> quiz -> generate_lead', but Aga's 3 Aug ruling took that doorway OUT of the YouTube path, so the chain to care about is now quiz-root -> generate_lead. The gate it protected — 'every lead prediction is gated on this' — is satisfied either way. YouTube's own 3 leads/28d are real but tiny, exactly consistent with the crawler finding that true YouTube traffic is ~7 sessions/day. || ORIGINAL: 🔴 L1/P007 THE GATE: prove the lead pixel end-to-end — UTM-tagged session through /calisthenics-assessment/ → quiz → generate_lead fires in GA4 DebugView with source intact. Every lead prediction is gated on this + the Nic redirect brief (P006). By 31 Jul. | Claude+Nic |
| Reachable engaged | In value loops now | List health | Email revenue |
|---|---|---|---|
| 2445 | 15792 across 11 sequences | bounce 0.03% · unsub 0.29% (cumulative, plan sequences) | DARK (native rail unattributed; Stripe UTM job = plan task #17) |
THU 23 Jul gate: 967 test batch TRIPPED the unsub wire (1.07% vs 0.5% gate; bounces/opens healthy) → 3,450 WAVES HELD pending arc maturation + Postmaster complaints glance — the test batch did its job. 758 pristine. 976 static 0.56% (cancelled-audience artifact, no deterioration). Aga's held-7,629 release RUNNING (979 at 3,498). Payment-recovery: first 18 SENT Thu AM; 4 new drafts today; FIRST RECOVERY logged. P1 annual sends Mon 28 Jul.
| Task state | |
|---|---|
| ✅ | R1 cancel-save Wave A |
| ✅ | 976 Cancelled Win-Back: switched on + triggers fixed (ROUTE:WinBack + wb-eligible only) |
| ✅ | 98 win-backs + 238 warm → sequences (ID-verified) |
| ✅ | 967 Lead-Magnet Welcome test batch 500 in |
| ✅ | P1 annual arc on shells 4077-79 (lint-PASS, address-free) |
| 🔄 | Thu gate RAN: 967 unsub 1.07% > 0.5% → WAVES HELD (re-read ~Mon after arc matures + Postmaster glance) |
| ⏭️ | P1 sends Mon 28 Jul (Aga schedules the 3 drafts) |
| 🔄 | 976b general arc for the 491 (only 10 have survey reasons) — fleet writes next |
| ⏭️ | Renewal protection arc spec DONE — awaiting Aga nod, then build |
| ✅ | Coverage audits #20/#21/#22 DONE — 3 reports in plan2026-07/ |
| … | Sunset/archive ~8-15K dead after held waves — DELIVERABILITY only (saves $0 on the bill; AC locked till Apr 2027) |
| ⏭️ | 756/757 Abandoned Cart Reminder — UPLOAD THE 4-EMAIL 2026-08 ARC INTO FRESH SHELLS. 🔴 CORRECTED 7 Aug 2026: this row previously described the RETIRED July build (3 emails, AC field 56, %ABANDONED_CHECKOUT_URL%, entry off the 'Subscription Cart Abandoned' tag 63598). That artefact is dead — its Stripe trigger (checkout.session.expired) can never fire on Payment Element, and tag 63598 is now orphaned. THE LIVE ARTEFACT is marketing/email-sequences/quiz-checkout-abandon-2026-08/ (E1-E4, lint PASS 4/4, %TMA_QUIZ_PERSONA% conditional content, API entry via quiz_abandon_sync.py). Both current shells are UNUSABLE: 756's email lacks an unsubscribe link + renders raw markdown, 757's four emails are EMPTY (subjects r/r/e/e) — Aga paused both 5 Aug. So this is: fleet hands over the personalised rebuild (FLEET-42), Aga builds 4 FRESH shells and un-pauses 756 only. E3/E4 wait on the $97-window enforcement decision (AGA-109). Plan: ABANDONMENT_STRATEGY_2026-08-07.md sections 5.2 + 7. |
| ✅ | ✅ Quiz→AC write-back VERIFIED HEALTHY 23 Jul — mid-Jul 'stall' was a FALSE ALARM (lists 202/200/tag 242 discontinued, not broken; leads flow to Stage 1/2 + .TMA LEAD [APP+QUIZ] + 750) |
| ✅ | Payment-recovery: 18 first sends OUT (Thu 07:00) · 4 new drafts · 1 recovery already logged · daily drafts 05:50 |
| ✅ | ❌ AC billing ANSWERED 22 Jul: Commit-Annually LOCKED till 9 Apr 2027 + downgrade collapses discount 56%→35% (50K=$840>$761) — archiving saves $0; sunset for DELIVERABILITY only |
| ⏭️ | 764 Nurture 4 confirmed ends in silence → wire shoulder hand-raise (#13 unblocked) |
| 🔄 | Aga's held-7,629 release RUNNING (979 in-flight 3,498, wave 1 out) |
| ✅ | 🗑️ TAG CLEANUP Phase 1 ✅ COMPLETE (23 Jul): 28,476 zero-sub tags deleted, 0 errors; 34,389→5,827 tags; protected trigger-tags verified alive; P2/P3 proposals next |
Expected: ~$1,100–2,500/mo new + ~$150–300/mo defended. estimates Email alone doesn't close the $4K gap — it builds the repeatable rail and stops the leaks. Every living contact ends in exactly one loop; ~8–15K dead contacts sunset for deliverability (saves $0 on the bill — AC locked till Apr 2027); new leads self-route forever.
| Phase | What |
|---|---|
| 1 · Money now | R1 cancel-save (✅ Wave A sent) · annual-offer 3-email series → the 6,050 warm non-customers ($157 via the trial door, no discount, ~$700–1,500/mo) · switch on 976 Cancelled Win-Back · 967 test batch of 500 |
| 2 · Fill the spine | Route every routable contact into a circulating nurture; wire the shoulder-course revenue lane for spine-finishers |
| 3 · Build defences | Dunning, abandoner coverage, front-door fixes — stop the bleed |
| 4 · Shrink to a healthy list | After the held group finishes: sunset ~8–15K dead contacts for stronger sender reputation (deliverability only — no billing saving, AC locked till Apr 2027) |
What — A one-off 3-email series to the 6,050 people who open our emails regularly but never bought, pitching the annual plan ($157) through the free-trial door. No discounts. MODELLED
Why — Engaged-only targeting is the post-2024 Gmail/Yahoo deliverability rule (mail openers → whole domain lands better), and a short 3-email arc with ONE offer beats endless drips. Based on our real 26.5% warm open + 1.5–2.5% click rates.
bc:annual-jul26 (undo-list saved), gives you the exact clicks.
☐ Switch on 976 Cancelled Win-Back (1 min) + reply GO → fires 98 win-backs + 238 warm
☐ Say yes to the 967 test batch of 500 (5 min)
☐ Review the 3 annual-offer emails when they land (15 min) → ~20-min guided setup
☐ Three 1-min screenshots: 764 ending · 754/755 inactive window · Settings→Billing
☐ Review 976b + renewal emails when rendered → ~20-min guided builds
☐ Approvals: 982 clean-up · retire 978 Evergreen · archives
☐ Read 3 verification reports: abandoner · dunning · trial coverage (10 min)
☐ One AWOS carve-out ruling for the estate-repair builds (2 min)
☐ Tell me the finish date of your held-group release (unblocks the cold routing + archive)
quiz_checkout(v1).
The v2-2 rail has taken zero in August and checkouts-v3 has taken $24.97 in its entire life across 71 starts.Loading… (run tools/finance/build_growth_8020.py)
SoT: docs/company/GROWTH-TEAM/growth_8020.json · method: GROWTH_PROVOCATION_FRAMEWORK.md · full page: GROWTH_80-20_BY_CHANNEL.html
marketing/AFFILIATES/ · commands /affiliate-fleet (out) + /affiliate-income (in) · 📕 MASTER HUB (start here — every affiliate doc linked from one page): marketing/AFFILIATES/AFFILIATE_MASTER_HUB.html📮 Outreach to partners lives in the OUTREACH lane — writing to an existing affiliate (reactivation) is governed by marketing/seo/OUTREACH_MASTER_2026-07.md and gated by tma-outreach-gate, which routes every target COLD vs PARTNER against the export in this tab. 🔴 A partner pitch may never re-offer a commission they already have — that was the 31 Jul near-miss (4 of 10 targets were already ours). The terms and the partner relationship stay owned here; the email is written and gated there. Full doctrine: the 📮 OUTREACH tab of the SEO master.
Two completely different businesses share the word "affiliate". Never conflate them. MONEY OUT = the Tapfiliate programme where we pay partners to send us subscribers (773 partners). MONEY IN = us earning commission by recommending other people's gear on BWA/TMA/YouTube. Different direction, different economics, different fleet. Verdicts differ: OUT = worth fixing. IN = harvest 4 hours and stop.
AFFILIATE_AUDIT_AND_10X_STRATEGY_2026-07-24.md · reproducer analyse_affiliates.pyThe export is NOT the channel — never quote it as lifetime. The CSV shows $588.74 revenue / $187.32 commission. The bank shows £1,748.18 of real affiliate commission actually paid. Rick Donohoe was paid £435.65 on 24 Jul — the day of the export — against $37.47 in it. And Benjamin Whitehead was paid £1,312.53 across three payments and does not appear in the export at all (7× every affiliate in it, combined). The export carries no date range and no date column. Step 0 of any cycle: read the Tapfiliate date-range filter.
This closes the open question in ANALYZING_OUR_TMA_FINANCIALS.md — "affiliate revenue > £66/mo? No → kill Tapfiliate". The answer is NO, don't kill it. It clears its own cost roughly 2–5×. It has simply never been asked to do more.
| Partner | Clicks | Sales | Revenue | Comm | Rate | Read |
|---|---|---|---|---|---|---|
| Abel Ramos elevateforlife.com | 9 | 1 | $157.00 | $78.50 | 50% | Annual sale. Best EPC in the file ($8.72) |
| Rick Donohoe calisthenics-101.co.uk | 65 | 3 | $74.92 | $37.47 | 50% | 4.6% click→sale — the model partner PROTECT |
| Chris Lynn couponbirds.com | 5 | 1 | $157.00 | $31.40 | 20% | 🔴 Coupon intercept — 26.7% of window revenue on 5 clicks |
| Antônio Nunes | 13 | 2 | $74.96 | $14.99 | 20% | Review aggregator |
| Trung Nguyen | 266 | 2 | $49.95 | $9.99 | 20% | 0.75% — volume, no quality (EPC $0.04) |
| Katy chmela | 122 | 1 | $24.97 | $4.99 | 20% | 0.8% — same |
| Eslam Seleem · Veronica Earnhart | 1 · 1 | 1 · 1 | $49.94 | $9.98 | 20% | Likely self-referral |
| 1 · No owner | Never anyone's job, number, or review. An org failure, not a strategy failure. Fixed 24 Jul — tma-affiliate-lead. |
| 2 · Zero assets | 🔴 The onboarding email promises promo videos, graphics, banners and an email sequence behind [PROMOTIONAL ASSETS FOLDER LINK] — the placeholder was never filled. Partners were recruited and given nothing to post. |
| 3 · Stale front door | /affiliate-program/ is 89 words, still titled "Calisthenics Academy", footer © 2025. No terms, payout schedule, tier rules, or proof. Advertises "20-50% · 45-day" — and no document anywhere says how you reach 50%. |
| 4 · Inbound-only recruitment | 773 signups dominated by coupon aggregators and freemail opportunists. 15 coupon/deal domains registered. Nobody has ever gone out and recruited a calisthenics creator. |
| 5 · | Corrected by benchmark. EPC $0.187 sits exactly on the bottom of ClickBank's best fitness offers ($0.18–$0.47); merchant RPC $0.587 is 2.3× benchmark. The traffic converts fine — 1,002 clicks has no statistical power. A supply problem wearing a conversion problem's clothes. |
| # | Lever | Effort | MRR-30 | Conf |
|---|---|---|---|---|
| 1 | Verify attribution before judging anyone. 494 clicks → £0 sits on top of the documented UTM-stripping defect (530 tagged visitors → 1 kept its tag). An under-attributed partner is indistinguishable from a bad one. | ~1h | ✓ | HIGH |
| 2 | Find out who Benjamin Whitehead is. £1,312.53 paid, invisible in the platform. If it's a repeatable partner type, that is the 10×. | ~15m | ✓ | HIGH |
| 3 | Exclude coupon domains + write the agreement (brand-bidding ban, clawback, ≥60-day payout hold for the money-back guarantee, no trial bounty — a documented fraud magnet). | ~2h | ✓ | MED-HIGH |
| 4 | Build the promo kit that was promised. Assets already exist — ASSESSMENT-KIT-2026-07/ + TESTIMONIALS-DATABASE/. Nothing to create, only assemble. Unblocks every partner at once. | ~4h | 30–60d | HIGH |
| 5 | 🎯 Hand-recruit 10 partners — weighted to COMPARISON writers. Head-to-head "TMA vs X" content converts 3–8% vs 0.5–1.5% for "best fitness app" listicles. One partner at ~650 clicks/mo × 2% × $157 ≈ $2,000/mo = 3× the whole window's revenue. Start with Jari Dohmen / calisthenicsworldwide.com (physio + ex-director Dutch Calisthenics Union, podcast, multi-channel) — 18 clicks, 0 sales, never contacted — and re-contract Rick Donohoe. | ~1 quarter | 60–90d | MED |
| 6 | 🔴 REWEIGHT commission to annual — do NOT raise it. We already pay 31.8%, at/above the top of every published band (SaaS 20–30%). But $157 × 40% = $62.80 in one payment gets a creator out of bed; $24.97 × 30% = $7.49/mo does not. The two annual sales = 53% of revenue from 17% of sales. | ~3h | ✗ | MED-HIGH |
A mass blast to the 711 dormant affiliates. Reactivation runs 12–18% under 90 days dormant but only 3–5% at 180+ days — and this list has been dormant since inception. Expected yield: ~20–35 who do something, realistically 0–5 who ever sell. A list of customer-referrals and coupon scrapers caps at 10–15% activation even with excellent onboarding. Against a real deliverability risk to the whole sending domain, it's a bad trade. If anything: one 3-email sequence to the ~53 who once sent a click, then archive (3 touches capture 93.2% of all replies) — and only with tma-email-deliverability's PASS.
AFFILIATE_INCOME_ANALYSIS_2026-07-24.md · doctrine: EXPERT_PROMPTS/AFFILIATE/HARVEST + NEGOTIATE (~4 hours). DO NOT BUILD. Worth $5–50/mo today, realistically <$150/mo at any sane effort ceiling, against a $4,000/mo gap to break-even — it fails MRR-30 by two orders of magnitude. Not an execution problem: we sell a $157/yr subscription at ~100% margin, so on any click we could monetise either way, our own product wins by ~10×. Affiliate income is only rational on traffic that will never buy TMA.
| Segment | Queries | Clicks |
|---|---|---|
| Mentions equipment at all | 26 | 58 |
| └ ANTI-purchase ("no equipment", "DIY dip bars", "build your own") | 18 | 42 |
| └ Genuine equipment interest | 8 | 16 |
| └ └ Actual buying intent ("best parallel bars") | 3 | 6 |
/build-dip-station/ — an article that exists to help people avoid buying one. Total commercial-intent traffic across the whole estate: ~370 visitors/month. Anyone pitching "you have 1.9M impressions, monetise them" is counting the 99.6% who will never buy a product.| Asset | Volume | State |
|---|---|---|
| TMA.com Amazon posts | 10 posts · 222 links, all tagged tmapicks-20 · ~300 clicks/mo | Earnings never once verified HARVEST |
| BWA commercial pages | /best-calisthenics-gloves/ 42 clk · /parallel-bars-buying-guide/ 24 clk (28d) | Live, ranking, not deliberately monetised |
| YouTube — Kensui vests | 8 videos carrying a discount link + code MOVEMENTATHLETE · ~115 views/mo | Terms unknown NEGOTIATE |
The Own-Product Test. own_EPC = P(quiz-start) × P(lead→paid) × LTV × margin. tma-affiliate-cannibalisation-guard holds a HARD VETO — no affiliate link ships on any page, video, email or app surface without its PASS. A $3 commission never gets to displace a $157/yr subscription.
tma-partnerships-growth-owner → tma-growth-lead · both propose-only · registered in the Governance Charter| Command | Seats | Owns |
|---|---|---|
/affiliate-fleetmoney OUT | tma-affiliate-lead · -economics · -recruiter · -activation-manager · -integrity · -skeptic | Net contribution after commission · activation rate · click→paid · EPC · concentration. Integrity vetoes calling revenue "growth" untagged. Skeptic blocks unlabelled windows, inflated denominators, gross-for-net, multipliers without absolute dollars. |
/affiliate-incomemoney IN | tma-affiliate-income-lead · -analyst · -deal-broker · -placement-engineer · -cannibalisation-guard · -income-skeptic | Net publisher income · the Own-Product Test · FTC/Amazon-ToS compliance · the sponsorship flip (brands paying us). Cannibalisation guard holds a hard veto. |
| 1 | What date range did you select when exporting the Tapfiliate report? Everything downstream depends on it. If unsure — re-export with 2018-01-01 → today. | blocks all rates |
| 2 | Who is Benjamin Whitehead? £1,312.53 paid across 3 payments, invisible in Tapfiliate. Off-platform deal, JV, or a differently-named account? | highest-value unknown |
| 3 | Is Rick Donohoe's £435.65 (24 Jul) a Tapfiliate payout or a separate arrangement? It doesn't reconcile to his $37.47. | reconciliation |
| 4 | Do the promised promo assets exist outside the repo (Drive/Dropbox), or were they never built? | unblocks lever 4 |
| 5 | What is the real 20%→50% tier rule, and who is on 50%? Written nowhere; the only statement is a contradicted 2024 email. | needed for terms |
| 6 | Keep coupon/deal sites in the programme at all? Recommendation: no, and the agreement should say so. | Claude recommends NO |
| 7 | 5-min Tapfiliate dashboard check: does it pay recurring/rebill commission, and is the payout hold configurable to ≥60 days? | gates lever 6 |
noreply@tapfiliate.com to hello@ (exact filename match) + tapfiliate.js on the live page. NOT ThriveCart (that's the lifetime-recovery rail only). [VERIFIED]transaction-history-wise.csv (Whitehead ×3, AUD) + Revolut statement (Donohoe, 24 Jul). [VERIFIED]python3 marketing/AFFILIATES/analyse_affiliates.py — if a doc disagrees with the script, the script wins.This tab is the pick-up point if you're starting cold. Everything that runs on a timer or touches the customer inbox is here and in the register above. The standing rule: nothing auto-sends to a member — every member-facing message is a Gmail DRAFT in hello@ that waits for Aga. The only armed exceptions are the R1 T-30 watcher and the upgrade stages (templated, pre-approved copy — never a personal reply).
Daily job tracking — 34 TMA jobs · auto-checked 12 Aug 05:37 from the tma_job_run success stamps (ground truth), not launchctl.
| Lane | Today | Context |
|---|---|---|
| R1 cancel-save | segment sends 1 · T-30 watcher 1 · saves 2 ($34.96) | queue 108 · drafts waiting 0 |
| Dunning (failed renewals) | drafted 0 · auto-sent 0 | drafts <14h old send at the next 08:00/20:00 pass |
| Failed purchase | drafted 0 · recovered 0 all-time | 22 in 90d window — 0 on most days is normal (~9 people/30d) |
| Quiz-checkout abandon | synced to AC 0 · abandoners 112/30d · $10,864 at stake | enrol DISARMED — 756/757 not yet live |
| Job | Schedule | What it does | Last success | Today |
|---|---|---|---|---|
| abandondraft | 06:05 | — | 2026-08-11 06:05 | — |
| asopull | 05:15 | — | 2026-08-12 05:15 | ✓ ran |
| backup | 01:30 | — | 2026-08-12 01:34 | ✓ ran |
| cockpitemail | 05:55 | 07:00 cockpit digest email to Aga (incl. the 🛟 retention rail block) | 2026-08-11 05:55 | — |
| cockpitrefresh | 05:25 | 06:00 full cockpit rebuild + deploy (revenue, funnel, this band, estate sync) | 2026-08-11 11:55 | — |
| cockpitwatchdog | 06:30 · 09:00 | Checks the DEPLOYED cockpit is fresh; alerts if stale | 2026-08-11 09:00 | — |
| emailmetrics | 06:10 | Pulls AC email metrics into the data/email-analytics scoreboard | 2026-08-11 06:14 | — |
| emailselflearn | 08:12 Mon | Weekly email self-learning loop | 2026-08-10 08:33 | — |
| everpaid | 02:10 | — | 2026-08-12 02:10 | ✓ ran |
| failedpurchase | 05:55 | Failed-purchase lane — ONE personal note per died-at-checkout buyer (drafts only) | 2026-08-11 05:55 | — |
| forgeassets | 07:40 Mon | FORGE asset pipeline batch | 2026-08-10 07:40 | — |
| funnelstage | 02:30 | — | 2026-08-12 02:34 | ✓ ran |
| growthsync | every 15 min | Growth workstream auto-sync into the cockpit | 2026-08-12 05:22 | ✓ ran |
| inboxtriage | 06:10 | Support triage — briefs + reply drafts for inbound no lane owns; emails Leih's page. NEVER sends | never | — |
| jobguardian | every 30 min | Every 30 min — detects missed/failed jobs, kickstarts (max 2), then alerts | never | — |
| jobselftest | 04:10 Sun | — | 2026-08-10 12:44 | — |
| mondaybrief | 07:00 Mon | Monday board brief build | 2026-08-10 07:00 | — |
| morningpulls | 05:00 | Morning data pulls (GA4/Stripe caches for the day's reads) | 2026-08-12 05:00 | ✓ ran |
| n973to758 | 09:40 | Migrates contacts from 973 into 758 Nurture Sequence 2 (batch feed) | 2026-08-11 09:40 | — |
| offerreach | 07:10 Mon | — | 2026-08-10 11:20 | — |
| paymentautosend | 20:00 · 08:00 | Sends dunning drafts older than 14h (twice daily) unless needs_aga() holds them | 2026-08-11 20:00 | — |
| paymentrecovery | 05:50 | Dunning lane — drafts personal notes for failed RENEWALS (T1/T2/T3 by invoice age) | 2026-08-11 05:50 | — |
| pmbrief | 05:45 | Daily PM/CEO brief (incl. the 🤖 FLEET TODAY band) | 2026-08-11 05:45 | — |
| quizabandon | every 30 min | Quiz-checkout abandoners → AC fields + tag `quiz-checkout-abandoned`; 756/757 enrol gated on sentinel | 2026-08-12 05:18 | ✓ ran |
| r1daily | 05:35 | R1 cancel-save rail — reply drafts into hello@, T-30 watcher (LIVE sends), saves board, upgrade engine | 2026-08-11 05:40 | — |
| seodailysync | 05:35 | SEO daily brief + task-surface sync | 2026-08-12 05:35 | ✓ ran |
| seoselflearn | 08:45 Mon | Weekly SEO self-learning loop | 2026-08-10 08:46 | — |
| socialdash | 06:30 | Social dashboard rebuild | 2026-08-11 06:31 | — |
| socialkpislack | 06:45 | Social KPI post to Slack | 2026-08-11 06:45 | — |
| socialselflearn | 06:35 Mon | Weekly organic-social self-learning loop | 2026-08-10 06:35 | — |
| transcriptcompress | 03:30 Sun | Weekly Claude transcript compression | 2026-08-10 11:02 | — |
| workout1 | 03:15 | — | 2026-08-12 03:19 | ✓ ran |
| ytchannelabout | 09:05 | YouTube channel About check | 2026-08-11 09:05 | — |
| ytselflearn | 08:30 Tue | Weekly YouTube growth self-learning loop | 2026-08-11 08:30 | — |
| Job | 30 | 31 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | today |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| abandondraft | · | · | · | · | · | · | · | · | · | · | · | · | ✓ | · |
| asopull | · | · | · | · | · | · | · | · | · | · | · | · | ✓ | ✓ |
| backup | · | · | · | · | · | · | · | · | · | · | · | · | ✓ | ✓ |
| cockpitemail | · | · | · | · | · | · | · | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | · |
| cockpitrefresh | · | · | · | · | · | · | · | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | ✗ |
| cockpitwatchdog | · | · | · | · | · | · | · | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | · |
| emailmetrics | · | · | · | · | · | · | · | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | · |
| emailselflearn | · | · | · | · | · | · | · | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | · |
| everpaid | · | · | · | · | · | · | · | · | · | · | · | · | ✓ | ✓ |
| failedpurchase | · | · | · | · | · | · | · | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | · |
| forgeassets | · | · | · | · | · | · | · | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | · |
| funnelstage | · | · | · | · | · | · | · | · | · | ✓ | ✓ | ✗ | ✓ | ✓ |
| growthsync | · | · | · | · | · | · | · | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| inboxtriage | · | · | · | · | · | · | · | · | · | · | · | · | ✗ | · |
| jobguardian | · | · | · | · | · | · | · | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | · |
| jobselftest | · | · | · | · | · | · | · | · | · | · | · | · | ✗ | · |
| mondaybrief | · | · | · | · | · | · | · | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | · |
| morningpulls | · | · | · | · | · | · | · | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| n973to758 | · | · | · | · | · | · | · | ✓ | ✗ | ✗ | ✗ | ✗ | ✓ | · |
| offerreach | · | · | · | · | · | · | · | · | · | · | · | · | ✗ | · |
| paymentautosend | · | · | · | · | · | · | · | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | · |
| paymentrecovery | · | · | · | · | · | · | · | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | · |
| pmbrief | · | · | · | · | · | · | · | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | · |
| quizabandon | · | · | · | · | · | · | · | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| r1daily | · | · | · | · | · | · | · | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | ✗ |
| seodailysync | · | · | · | · | · | · | · | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | ✓ |
| seoselflearn | · | · | · | · | · | · | · | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | · |
| socialdash | · | · | · | · | · | · | · | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | · |
| socialkpislack | · | · | · | · | · | · | · | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | · |
| socialselflearn | · | · | · | · | · | · | · | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | · |
| transcriptcompress | · | · | · | · | · | · | · | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | · |
| workout1 | · | · | · | · | · | · | · | · | · | · | · | · | · | ✓ |
| ytchannelabout | · | · | · | · | · | · | · | ✓ | ✗ | ✗ | ✗ | ✗ | ✓ | · |
| ytselflearn | · | · | · | · | · | · | · | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | · |
| Job | When | What it drafts |
|---|---|---|
| com.tma.r1daily retention/r1_daily.py | 05:35 daily | check_replies.py — replies to R1 cancel-save notes → churn reason logged → Jesse-voice reply drafted in-thread (STOP/bounce → suppressed, no draft). reply_fulfil.py — same for the 986 Welcome-Lifetime + 976 Win-Back reply arcs. |
| com.tma.paymentrecovery retention/payment_recovery_lane.py --draft | 05:50 daily | Dunning — up to 3 notes to members whose renewal failed, by invoice age (T1/T2/T3). fixed 24 Jul was silently dead 22→24 Jul (wrong python — see trap below). Approved drafts sent by send_payment_drafts.py --send. |
| Lane | Owns | Status |
|---|---|---|
| R1 r1_daily.py | Voluntary churn — members who chose to cancel (cancel_at_period_end) | live |
| Dunning payment_recovery_lane.py | Involuntary renewal failure — billing_reason=subscription_cycle, open invoice | live (drafts) |
| Failed-purchase failed_purchase_lane.py | Purchase attempt died — failed charge, no invoice (new OR returning). ≈9 people/30d, ~$2.7K attempted; mostly $297 lifetimes declined by emerging-market issuers | armed 31 Jul (drafts) |
com.tma.failedpurchase (05:55 daily, drafts only) after the pre-flight Aga set: CRO copy review → full-family bug audit → docs. ONE note per person, ever · two variants by decline code (never blames the buyer for a bank block or the bank for a typo) · no links — the conversion mechanic is a reply · skips anyone who ever paid, is suppressed, or is owned by R1/dunning · cannot auto-send (subject+state differ from the dunning lane; guarded by an import-time assert). Detail: register §2.5 ↗ · copy law: hello@ email rules ↗.Before arming, the whole job family was audited for bugs — 30 findings, 8 fixed the same session. Full register: Job Audit Findings ↗.
| Fixed | What it would have done |
|---|---|
| C1 · nothing watched replies to payment emails | Every dunning/failed-purchase note promises "Reply STOP and I won't email you again" — but no scanner watched those subjects, so a STOP never reached suppress.csv and the next touch drafted (and auto-sent) anyway. The fix immediately surfaced a real reply unread since 28 Jul. Live replies now flagged 🔴 NEEDS AGA; STOPs suppress automatically. |
| C2 · upgrade engine would break on arming | A KeyError on a field that never existed — members would be tagged into 990 Monthly→Annual Upgrade Engine (and start receiving it) while the log row was never written, so Stage 2/3 could never fire. Invisible in dry runs. |
| H2 · could have emailed a paying member | The "has this person ever paid?" check wasn't paginated — a serial retrier's successful charge could fall off page 1. Now paginated and fails safe (a Stripe error counts as paid → don't email). |
| M7 · the whole hello@ rail could go dark | Four lanes + the guardian refresh one shared Gmail token with a non-atomic write; two interleaved writes corrupt it → every lane dead until manual re-auth. Now atomic. |
| H1 · H3 · C3(part) · M11 | Malformed plist (job silently unloaded + crashed the ops tooling) · "one note per person" was really per-charge · crash-mid-run re-drafted the same notes · pending charges could be told they failed. |
sent stamps and double-send. Next work block. 11 further items ranked in the findings doc. Standing rule added: never use a bare limit=N Stripe list for a safety check or a count — that class of bug has now bitten twice in two days.CANONICAL register of (a) every lane that SENDS or DRAFTS from
hello@themovementathlete.com, and (b) every scheduled job running on Aga's Mac. If a machine emails a member, drafts a reply, or refreshes a dashboard on a timer, it is listed here.Verified live: 24 Jul 2026 (launchd dumped, logs read, dry-runs executed, Gmail drafts enumerated). Owner seat:
tma-retention-lead(lanes) ·tma-coo-systems(the machine) · Aga approves every send.📅 DAILY TRACKING (added 6 Aug 2026, Aga's ask): the cockpit ⚙️ Daily Jobs tab now opens with a GENERATED tracking band — per-job ran-today (from the
tma_job_run.pysuccess stamps, not launchctl), the 🛟 member-facing-lanes-today table (sends · drafts · saves per lane, same sources as the 06:00 Slack digest), and a 14-day ran-grid. Generator:tools/ops/job_status_band.py, runs inside the 06:00 cockpit refresh; history:tools/ops/jobs_daily_track.csv. This register remains the editable prose source — it renders into the same tab; edit here and the cockpit follows at the next refresh.
tools/retention/HELLO_EMAIL_RULES.mdgoverns every 1:1 email sent from hello@. Set by Aga 31 Jul 2026. One sentence decides everything: it has to look like Jesse just typed it. No bold, no underline, no italics, no template, no images — readability comes from short paragraphs. Links are real clickable anchors in standard blue (never a bare URL to copy-paste), with CRO-reviewed, honest anchor text. This is NOT the marketing estate — the AC/CIO fleet rules (locked footer,email_lint, designed template) do not apply here and would destroy what makes these convert.
Nothing auto-sends to a member. Every member-facing message is created as a Gmail DRAFT in hello@ and waits for Aga. Three explicit exceptions: the two lanes armed
--live(R1 T-30 watcher, and the upgrade stages onceUPGRADE_LIVEis set) — both send templated, pre-approved copy, never a personal reply — and Aga's 14-hour rule (28 Jul 2026), under whichcom.tma.paymentautosendsends dunning-lane drafts older than 14h unlessneeds_aga()holds them. That third exception is scoped to the dunning lane's exact subjects + state file and cannot reach any other lane's drafts (audit-verified 31 Jul).
| Clickable links (31 Jul 2026) | Lane emails whose body contains a URL are now multipart/alternative: text/plain unchanged (bare URL — the fallback, and what needs_aga() reads) plus a minimal HTML twin where the URL becomes a real standard-blue anchor (#1155cc — deliberately not a brand colour; a normal blue link is what a normal personal email looks like). CRO-set anchor text, different per lane on purpose: dunning = "Fix your billing →" (task-framing — an existing member with a known fault; and critically it does NOT over-promise, because the destination is the Stripe login page, not a card form — "Update your card" would mis-set the first-screen expectation on a billing page, the worst place to create doubt); R1 cancel-save = "Keep training →" (outcome-framing, low-pressure — they chose to leave and the email's real goal is a reply; administrative language reads as the process they already rejected). Arrows kept: in a plain-text-style email they are the only visual cue the text is clickable. Future A/B: dunning "Restore your access →", R1 "Turn renewal back on →". Built by r1_lib.build_message() / text_to_html(). Deliberately NOT the marketing template — no logo, footer bar or background; these are 1:1 notes from Jesse and reading as a campaign would convert worse. Bodies with no link (failed-purchase, guardian alerts) stay pure plain text, untouched. Aga's trigger: "make a link an actual clickable link — like click here instead of a dead link they need to copy and paste." |
| Account | hello@themovementathlete.com (Google Workspace) |
| Auth | OAuth token at tools/Gmail Connection/token.json, auto-refreshed by r1_lib._creds() |
| From / Reply-To | The Movement Athlete <hello@themovementathlete.com> — set in tools/retention/r1_lib.py:34-35 |
| Why Gmail, not ActiveCampaign | Plain-text, personal, lands in the main inbox (not Promotions), and replies come back to a human inbox — the replies ARE the save |
| Why never AC-spoofed From | The domain is p=reject. A founder note spoofed through AC fails DMARC. |
| Shared engine | r1_lib.py (creds, Stripe pull, suppression, portal URLs) + check_replies.py (_gget / _gpost / _make_draft) |
Two scheduled jobs create drafts. Nothing else does.
com.tma.r1daily — 05:35 daily — the R1 reply assistant/opt/homebrew/bin/python3 tools/retention/r1_daily.py
Its heartbeat runs 5 steps; two of them create drafts:
| Step | Script | What it drafts |
|---|---|---|
| 2 | check_replies.py |
Walks hello@ for replies to R1 cancel-save emails → appends the reason to reason_log.csv → COMPOSES a genuine Jesse-voice reply to the member's ACTUAL email (local claude CLI, compose_reply() — Aga's 7 Aug 2026 rule after the Pablo Mendoza failure, where the keyword classifier answered only the price line of an 8-paragraph email). Guardrails: responds to every point, plain text, no invented facts/dates, only the standing 30%-off-renewal offer allowed, 60–180 words; output validated, and on ANY failure it falls back to the old PRICE/OTHER templates. Draft goes inside the same Gmail thread. STOP → suppressed, no draft. Bounces → member suppressed, no draft. |
| 2b | reply_fulfil.py |
Same mechanic for the lifecycle arcs that use "hit reply and I'll send it over" — 986 Welcome Lifetime Members + 976 Cancelled Win-Back. Both send from + reply-to hello@, so they land in the same inbox R1 watches. |
Other steps (no drafts): 0 exit-survey sync · 1 T-30 pre-expiry watcher (sends live) · 3 saves scoreboard · 3b upgrade engine · 3c upgrade Stage 2/3 · 4 r1_status.json · 5 cockpit <!--R1STATUS--> patch.
Idempotent via replies_state.json / reply_fulfil_state.json.
com.tma.paymentrecovery — 05:50 daily — the dunning lane/opt/homebrew/bin/python3 tools/retention/payment_recovery_lane.py --draft
Up to 3 personal plain-text notes to members whose card failed, timed off invoice age
(T1 ~1d · T2 ~4d · T3 ~8d; 10–21d = single final note; >21d = skipped, that's win-back
territory). Auto-exits the lane the moment the invoice is paid.
--draft = create drafts for review (current mode). --live = send directly (not armed).
Approved drafts are fired by the one-shot companion send_payment_drafts.py --send, which
hard-filters to this lane's exact subjects + payment_recovery_state.json, so it can never
touch an R1 reply draft.
The lane filters billing_reason == "subscription_cycle" (payment_recovery_lane.py:144).
That is a deliberate, load-bearing choice, and it means:
| Type of lead | Stripe shape | Covered? |
|---|---|---|
| 1. Existing member, renewal failed (card expired/declined) | subscription_cycle invoice, status=open, attempt_count ≥ 1 |
✅ Yes — T1/T2/T3 drafts, auto-exit on payment |
| 2. Purchase attempt died (new OR returning customer, card declined at checkout) | Failed charge with NO invoice — the sub goes incomplete, expires, invoice is voided not open |
🆕 failed_purchase_lane.py (built 24 Jul, drafts-only, not scheduled) |
| 3. Plan change/upgrade failed | subscription_update |
❌ No (1 open, 0 failed attempts — immaterial today) |
Why type 2 is invisible: a first-payment failure never produces an open invoice. Stripe
marks the subscription incomplete, then incomplete_expired, and voids the invoice. The lane
scans open invoices, so it can't see them — and neither can R1 (whose queue is
cancel_at_period_end=true).
Size of the gap (verified live 24 Jul, 90-day charge sweep): 524 failed charges — 486 invoice-backed (dunning lane's job) and 38 with no invoice, collapsing to 23 distinct people, of whom 9 are actionable inside 30 days ≈ $2,673 attempted. Closest existing machinery is the cart-abandonment build (756/757), which targets people who never reached payment — not people whose card was declined at it.
What they actually are: almost entirely $297 lifetime attempts declined by emerging-market issuers (BD · ET · NG · TZ · EC · LB) on a large single cross-border charge, plus a few high-intent repeat triers (jonaekstrom SE — 5 attempts across 2 cards; nurlomen07 BD — 5; fizan18 EC — 5, who eventually converted). The checkout is NOT broken — tested and refuted 24 Jul: in the same 4–8 Jul window 30 charges succeeded ($5,428.71) including $297 lifetimes from AU/ES/MY/GB/HK/US/CA, and Aga's own test card succeeded at 15:58 immediately after two of her cards declined at 15:54–15:57.
Open product question for Aga: for a declined emerging-market card, "try again" is weak. The useful offer is an alternative — a different card, PayPal, or stepping down to a monthly plan. That is an offer decision, not an email one.
failed_purchase_lane.py — the third lane (built 24 Jul 2026)Sibling of R1 (voluntary) and the dunning lane (involuntary renewal). Scans failed charges with no invoice, collapses to one row per person, and drafts ONE personal note — deliberately not a 3-part drip, because ~9 people/month does not justify one.
card_declined,
authentication_required) vs "the card details weren't accepted" (incorrect_number,
incorrect_cvc, expired_card). Never blame the wrong party.reply_fulfil.py (true since
31 Jul 2026 — audit finding C1 proved nothing watched these subjects before then).30d old (cold).
--draft · --live · --days N.
State failed_purchase_state.json · log failed_purchase_log.csv.com.tma.failedpurchase (05:55 daily, --draft), after
Aga's pre-flight: CRO copy review → full-family bug audit → documentation. See
JOB_AUDIT_FINDINGS_2026-07-31.md for the 30 findings and what was fixed.How this lane works (the operating description):
| Who it emails | People whose purchase attempt died at checkout — a failed charge with no invoice (Stripe marks the sub incomplete→incomplete_expired and voids the invoice, so neither R1 nor the dunning lane can see them). ~9 people/30d, ~$2.7K attempted, mostly $297 lifetimes declined by emerging-market issuers. |
| When | 05:55 daily, between dunning (05:50) and the cockpit refresh (06:00). |
| What it produces | ONE Gmail draft per person, ever — deliberately not a 3-part drip; ~9 people/month does not justify one. Drafts wait in hello@ for Aga. |
| Copy | Two variants chosen by decline code — bank-block (card_declined, authentication_required, insufficient_funds, do_not_honor) vs card-details (incorrect_number, incorrect_cvc, expired_card). Never blames the buyer for a bank block or the bank for a typo. 3+ attempts adds an apology line. CRO-reviewed 31 Jul: "cross-border charge" (names the real mechanism for the BD/NG/ET audience), "call your bank to approve it" (the British "give your bank a nudge" didn't travel). Load-bearing element per the review: the dual exit — "if you still want in, reply" carries equal weight to "if the timing was wrong, reply and say so" — which is what makes it read as service, not chasing. |
| Conversion mechanic | A reply. No links in the body at all (nothing for spam filters or the needs-Aga gate to trip on). Jesse then sends a fresh link personally. |
| Skips | founder's own card tests · deny-list · suppress.csv · anyone in the R1 cancel queue · anyone the dunning lane owns · anyone who EVER paid successfully (paginated + fails safe: a Stripe error counts as paid) · <12h old (let Stripe's retry play out) · >30d old (cold) · pending charges. |
| Can it auto-send? | No. send_payment_drafts --auto matches on exact equality with the dunning subjects + its own state file; this lane's subject and state differ. Guarded by an import-time assert so a future copy edit can't silently break it. |
| Replies + STOP | Watched since 31 Jul (audit C1): STOP → suppress.csv automatically; a live reply is surfaced as 🔴 NEEDS AGA in the daily run and counted into r1_status.json. No auto-draft — a drafted "here's your link" with no link is worse than nothing. |
| State / log | failed_purchase_state.json (atomic, saved after every draft) · failed_purchase_log.csv |
| Reaches Aga | 06:00 Slack post + 07:00 cockpit email both carry a 🛟 Retention rail — hello@ block, rendered from ONE shared source (tools/retention/retention_digest.py) so the two can never disagree. Headline is what NEEDS her: drafts waiting (live Gmail count, so carry-over from previous days is included) and 🔴 live payment replies; R1 queue and failed-purchase recovery follow as context. Best-effort by construction — any failure renders empty and the money post is untouched. |
| Daily read | Writes failed_purchase_status.json every run, in every mode (drafted today · people seen in window · touched all-time · recovered = paid after we wrote, the outcome metric · attempted $), then patches the cockpit's <!--FPSTATUS--> block in the 🛟 Retention · R1 tab. It does NOT deploy — the 06:00 refresh (5 min later) does, same as r1_daily. Added 31 Jul after Aga asked where the daily analytics sat: the lane had been armed with documentation but no daily read, the exact drift this estate keeps getting bitten by. |
| Modes | dry (default) · --draft (scheduled) · --live (not armed) · --days N |
Why parked: a new set of checkouts is being built, and they will feed richer information through Stripe. Rather than tune a lane against today's thin signal, we wait and build against the better one. The existing lanes (R1 + dunning) keep running unchanged in the meantime.
🔴 ASKED OF NIC — sent 24 Jul 2026, awaiting confirmation. Do this AT BUILD TIME, not as a
retrofit: the new checkouts must write
plan_slug · plan_id · coupon · user_id · buyer_email into PaymentIntent metadata.
Today's PIs carry an empty metadata object, which is the single reason plan and coupon have to
be inferred from the charge amount. Baking it in while the checkouts are being written costs
nothing; retrofitting it later costs a Nic ticket. Same hook as cart-abandon Path B — one change
serves the cart-abandon bridge, this lane, and Stripe→app user-id attribution.
Un-park checklist: new checkouts live ✅ (checkouts-v3, verified 30 Jul) → confirm PI
metadata is populated ✅ CLOSED by Nic, verified 30 Jul PM (the completed v3 trial's intent
carries buyer_email + user_id, its customer carries email/name/django_user_id/consent_at
— stamped at pay-click; the 24 Jul ask is DONE for everyone who clicks pay. ⚠️ blur-stamp
persistence for abandoners still unconfirmed, and the v3 flow mints phantom subs at page load
= noise in any scan — both on Task 1, tma-nic-attribution-brief.netlify.app) → re-run
--days 30 dry → Aga approves the copy → --draft → read → arm.
com.tma.quizabandon — every 30 min — quiz-checkout abandonment sync (NEW 3 Aug 2026)tma_job_run.py → quiz_abandon_sync.py --sync
The bridge that ended the "silent doorbell" (756/757 Abandoned Cart Reminder active since Oct
2025 with entered: 0). Reads unpaid quiz-checkout subscriptions from Stripe (metadata:
email · persona · offer · plan) → writes AC fields TMA Quiz Persona / Offer / Plan + tag
quiz-checkout-abandoned. Sync mode emails NOBODY. Enrolment (756 in-window E1–E3 →
757 day-4 E4) requires BOTH the QUIZ_ABANDON_ENROL_ARMED sentinel (Aga creates to arm,
deletes to disarm — the UPGRADE_LIVE pattern) and the job being switched to --enrol.
| Who | Paywall VIEWERS who declined (intent is created on checkout page LOAD — they are NOT card-abandoners; copy law: never "you almost paid"). ~10/day, honest recovery sizing $900–1,500/mo at the 3–5% benchmark. |
| Safety | Buyer-check (paginated, fails-safe) at sync AND before EVERY enrolment stage — a buyer can never enter (NIC-21). Suppression honoured. On 756 entry the contact is pulled out of 750/751 (frequency ceiling). Per-person atomic state writes. |
| Sequence | E1 ≤60min mirror-their-plan · E2 +22h objection+guarantee · E3 +40h honest $97-window expiry (the REAL Django-served deadline — no fake revives) · E4 day-4 quarterly $49.97 STANDARD safety net (Aga ruling 3 Aug — no coupon; "$49.97 ≈ half of $97" IS the frame). Exit after day 4 → SPEC-REENGAGEMENT. |
| Owner | .claude/agents/tma-quiz-checkout-abandon-owner.md (doctrine: SPEC-CART-ABANDONMENT). Copy: /email-fleet only, slug quiz-checkout-abandon-2026-08. |
| State / status | quiz_abandon_state.json · quiz_abandon_status.json → cockpit <!--QZABANDON--> (Retention tab) + retention digest (Slack 06:00 / 07:00 email). |
| QA gate | docs/company/GOVERNANCE/QUIZ_ABANDON_QA_2026-08.md must be green before the sentinel is ever created. |
Recovery runs entirely on the address Stripe holds. If that address is wrong, Stripe's own dunning emails and ours both go nowhere, and the invoice just ages out.
Live example (24 Jul): invoice in_1TwYSDI6s6pBAbnmTgXXQivY, $157 Yearly V3 renewal, carries
ryanlion2010@ico**u**d.com — a missing l. The same member exists correctly as
cus_SgECxPLlzALrAn / ryanlion2010@icloud.com, and the app has the correct address (that's
what the failure notification quoted). Two customer records, one typo, zero deliverable
recovery mail. Fix = correct the email on the failing customer record in the Stripe Dashboard
(the repo's Stripe keys are read-only by design), before the T1 draft is sent.
Worth a periodic sweep: any lane email whose recipient bounces should be reconciled against the app's own address for that user id.
| Script | Trigger | Sends |
|---|---|---|
send_r1.py |
manual, per segment | The R1 cancel-queue save note (A ≤30d gets the offer; B/C get the question only) |
pre_expiry_watch.py --live |
inside r1_daily step 1 |
Auto-note the day a cancelled sub enters the 30-day window |
send_upgrade_stage.py |
inside r1_daily step 3c |
Monthly→Annual Stage 2 (day ≥17, human touch) + Stage 3 (day ≥27, $129/yr, 7-day deadline). DRY until the sentinel file tools/retention/UPGRADE_LIVE exists. |
send_concierge.py |
manual | 1:1 concierge note to ACTIVE monthly members. Dry unless --live. |
tools/finance/daily_cockpit_email.py |
com.tma.cockpitemail 05:55 (was written here as 07:00 — WRONG since the 6 Aug morning-chain retime; the plist has said 05:55 all along, corrected 11 Aug 2026) |
The daily cockpit brief — to hello@, internal only. Rebuilt 25 Jul: leads with repeatable net cash → $15K (MRR demoted to secondary), reads kpi_15k.json/data_flow_daily.json from the 06:00 run and degrades gracefully if absent |
~/Library/LaunchAgents/)| Job | When | Runs | Touches hello@ |
|---|---|---|---|
com.tma.r1daily |
05:35 daily | retention/r1_daily.py |
✅ drafts + live T-30 sends |
com.tma.paymentrecovery |
05:50 daily | retention/payment_recovery_lane.py --draft |
✅ drafts |
com.tma.paymentautosend |
20:00 + 08:00 daily | retention/send_payment_drafts.py --auto — 🔴 the only job that SENDS member email with no human in the loop. Aga's 14-hour rule (28 Jul 2026): a lane draft older than 14h sends itself. Absolute exception: needs_aga() HOLDS anything carrying a link outside the allowlist, a checkout, a discount/promo code, an offer, or an unfilled placeholder — those wait for Aga indefinitely. Logs auto-sent to payment_recovery_log.csv; logs /tmp/tma_payment_autosend.* |
✅ SENDS |
com.tma.cockpitrefresh |
05:25 daily | finance/refresh_cockpit_daily.py — 🔴 11 Aug 2026: two ordering bugs closed. It now runs seo/lead_lanes_refresh.py as its FIRST 0d-quater step, because the GA4 lead-origin table was written only by seodailysync at 05:35 — ten minutes after this chain embedded it, so Aga's "where leads come from" table was a full day stale EVERY day (measured: kpi generated_at 05:28 carrying ga4_lanes pulled the previous date). It also runs finance/sales_yesterday_pull.py before the Slack post. Both are policed by finance/tracker_freshness.py, whose AGE and CHAIN-COHERENCE checks fail loudly on Aga's own screen if either regresses |
— (deploys cockpit; since 25 Jul also runs flow_metrics_pull.py → step 0e and kpi_15k_sync.py → step 4a, which own the 🎯 $15K tab. 4a must stay AFTER canonical_facts.json is written. It also posts to Slack — that lane is NOT gated by --no-deploy) |
com.tma.emailmetrics |
06:10 daily | email-analytics/collect_metrics.py |
— |
com.tma.workout1 |
03:15 daily · PLIST WRITTEN, NOT YET LOADED (arming is Aga's, with the AC automation) | retention/workout1_moment.py --apply — THE WORKOUT-1 MOMENT lane. Reads Intercom workouts_finished_number for every 90d-active contact, diffs against last night's snapshot (workout1_state.json), and tags everyone who went 0 → ≥1 since the last run with workout1-yesterday (an AC automation starts on that tag, waits until ~8am, sends one email, ends — AC never has to see the app) plus a permanent workout1-done (the welcome arcs' skip-condition, so an active trainee stops being asked "have you started yet?"). Payer-excluded FOUR ways: Intercom plan prefix · live Stripe both accounts · the EVER-PAID set (lifetime is a one-time CHARGE) · the segment gate before any send. 🔴 First run SEEDS ONLY and refuses to tag — with no prior snapshot every existing member looks like a first-timer; this is the absence of data, not an overridable flag. 🔴 Refuses outright above MAX_COHORT=400/day — that means a stale snapshot or an Intercom backfill, not 400 first workouts. Snapshot advances ONLY after a successful tagging pass, so a crash re-runs the same diff rather than losing the cohort. Why a count diff and not a timestamp: Intercom exposes no last_workout_at (measured 11 Aug across all 76 attributes). Health: workout1_moment.py --health. Seeded 11 Aug 2026 with 15,410 contacts. Doctrine: WELCOME_CTA_DOCTRINE_2026-08-10.md §0 — this is one of the two legal trial asks |
— (writes AC tags; sends nothing) |
com.tma.funnelstage |
02:30 daily | retention/funnel_stage_export.py --apply — stamps TMA_FUNNEL_STAGE (survey_unfinished/no_workout/no_trial) + TMA_FUNNEL_STAGE_AT onto EXISTING AC contacts from Intercom funnel state (90d-active, payer-excluded via plan-prefix + live Stripe cross-check; segment D = abandon-fleet's, never stamped; never creates a contact). Changes-only via funnel_stage_state.json; blind-crawl canary aborts exit 2; clears the stage the day a person converts (stale-label hole closed 7 Aug); self-verifies 8 read-backs per run (non-zero exit → guardian alerts); runs via tma_job_run.py so com.tma.jobguardian kickstarts it after a missed window and alerts on failure. Health: funnel_stage_export.py --health. 🔴 Send gate: any send on these segments needs segment_guard.py PASS (live re-pull + every-email Stripe check + freshness), per Audience Data Dictionary rule 5. Armed 7 Aug 2026 on Aga's D2 GO — master: docs/company/ANALYTICS/AUDIENCE_INTEGRITY_MASTER_2026-08-07.md |
— (writes AC fields; sends nothing) |
com.tma.morningpulls |
05:00 daily | finance/morning_pulls.py |
— |
com.tma.cockpitwatchdog |
06:30 daily | finance/cockpit_watchdog.py — 14 checks (was 13). 🔴 Added 7 Aug 2026: ac-automations → retention/ac_automation_state_check.py, which snapshots every ActiveCampaign automation's status and alerts when one silently changes state. Born the day 750 TMA | Welcome Sequence After Quiz was paused 16:41 and un-paused 18:03 with no replacement arc — 82 minutes in which a new quiz lead got two transactional emails and then nothing, found only by accident because a naming-rule lookup printed the live status. Read-only (GETs only; it cannot pause or resume anything). Exit 2 = UNDETERMINED, not a pass — it refuses to report "no changes" from an empty or truncated fetch. Deliberately not in HEALS: a state change needs a human decision, never a self-heal. |
— (alerts) |
com.tma.socialdash |
06:30 daily | social-pipeline/refresh_social_dashboard.py (venv python; fixed 24 Jul — was .sh via /bin/bash, exit 126 every run). Extended 3 Aug: step 1/4 now pulls Instagram per-reel insights (ig_insights.py), deliberately non-fatal so a bad IG day cannot take the board down |
— |
com.tma.socialkpislack |
06:45 daily | social-pipeline/slack_daily_kpi.py --post — posts the daily social KPI digest to Slack #social-fleet-kpis-updated via SLACK_SOCIAL_WEBHOOK in .env.local. Reads ONLY social_status.json, so it can never disagree with the board; runs 15 min behind socialdash so it always reads fresh data. 🔴 Never quotes GA4's raw YouTube session count (crawler artefact) — uses live.youtube_traffic.real_28d_equivalent |
💬 posts to Slack (no member email) |
com.tma.seodailysync |
05:35 daily | seo/seo_daily_sync.py --deploy (fixed 24 Jul — logs were on T9, spawn failed exit 78; now /tmp/tma_seo_sync.*). Extended 24 Jul: also writes the zero-LLM marketing/seo/SEO_DAILY_BRIEF.md (5 fleet KPIs + AGA/NIC ≤3 human lists from marketing/seo/SEO_HUMAN_QUEUE.json) and mirrors the human lists into the cockpit SEO-LIVE block |
— |
com.tma.pmbrief |
05:45 daily | ops/pm_board.py brief --write — the PM fleet's morning CEO brief → marketing/seo/PM_MORNING_BRIEF.md: what ran without Aga (24h ledger), what needs her (incl. Nic rows awaiting --nic-approved), her ranked top 3, open load per pod, and the integrity flags. Reads all five pod queues through aga_console_sync's own extractors so it can never disagree with the cockpit. 🔴 Runs after seodailysync (05:35) so the queue lists are already fresh, and before Aga's 06:00 start — the whole morning chain was moved earlier on 6 Aug 2026 because it had been building her lists at 06:40, after she sat down. Deterministic only; the judgement layer (the ONE cash constraint) comes from /pm-fleet in session |
— |
com.tma.socialselflearn |
06:35 Mon | social-pipeline/social_selflearn.py — the weekly organic-social self-learning loop (reads social_status.json, adjudicates the week's hook/format experiments, refreshes the board). Row added 10 Aug 2026 — it had been running unregistered since 5 Aug and was found by the guardian's new register-drift check on that check's very first pass. |
— |
com.tma.seoselflearn |
08:45 Mon | seo/seo_selflearn.py (venv python; logs /tmp/tma_seo_selflearn.*) — zero-LLM weekly loop: adjudicates the 14-day GSC watches on every fleet-shipped page (KEEP / WATCHING / REVERT-FLAG — flags only, never auto-reverts), writes the CMO/CEO weekly to marketing/seo/reports/SEO_WEEKLY_REPORT_<date>.md, re-ranks SEO_HUMAN_QUEUE.json, flags the monthly AI-citation panel when due |
— |
com.tma.cockpitemail |
05:55 daily | finance/daily_cockpit_email.py |
✉️ sends to hello@ |
com.tma.mondaybrief |
07:00 Mon | finance/monday_brief.py |
✉️ internal |
com.tma.emailselflearn |
08:12 Mon | email-analytics/run_selflearn.py |
— |
com.tma.forgeassets |
07:40 Mon | forge-assets/run_weekly.py (homebrew python; logs ~/Library/Logs/tma/com.tma.forgeassets.*) — refreshes the FORGE asset DB: catalog → Drive ingest → vision (new content-hashes only, so a quiet week is nearly free) → manifest → verify_forge_db.py gate. 🔴 Written in Python, not bash — tma_job_run.py is explicit that system binaries cannot touch /Volumes/T9 under launchd (§6). 🔴 Logs every run to the fleet ledger including no-ops, and shouts if the Drive source was skipped — an unreachable source is ABSENT, not empty |
— |
~~com.tma.ytretrofit~~ |
RETIRED 10 Aug 2026 | was youtube-analytics/apply_retrofit_all.py --apply — the retrofit COMPLETED (869/869 staged descriptions applied, 0 remaining, verified in quizroot_apply_state.json) so the daily run had become a pure no-op with three unmaintained hard-exit gates. Plist moved to ~/Library/LaunchAgents/_retired-tma/ (reversible). The guard jobs (ytchannelabout) stay |
— |
com.tma.ytchannelabout |
09:05 daily | youtube-analytics/channel_about_guard.py --apply — keeps the YouTube channel's About text on the UTM'd quiz root. Added 6 Aug 2026: the 869-video retrofit moved every description off /calisthenics-assessment/ but never touched the channel's own About prose, which still carried the old untagged doorway — it hid because the 4 structured channel links were all correctly tagged, so the links audit passed and nobody audits prose. 🔴 Runs at 09:05, deliberately BEFORE ytretrofit at 09:15 — the retrofit spends 9,800 of the 10,000 daily units, so anything after it is quota-dead (proven 6 Aug: even a 1-unit videos.list 403'd). Checks by parsing the public About page = 0 units when correct, and spends the 50-unit channels.update only when the text is actually wrong. Ships as a guard, not a one-shot patch, per the retrofit's own lesson that doctrine without a compliance check decays into decoration |
— |
com.tma.ytselflearn |
08:30 Tue | youtube-analytics/yt_selflearn.py |
— (pulls GA4+YT, scores prediction ledger, writes weekly CMO/CEO report to marketing/youtube/performance/weekly-reports/, refreshes yt_status.json) |
com.tma.growthsync |
every 15 min | finance/growth_repo_sync.py --pull-deploy |
— |
com.tma.n973to758 |
09:40 daily · TEMPORARY, self-completing | retention/migrate_973_to_758.py --batch 2000 (homebrew python; logs /tmp/tma_n973to758.*) — Aga-approved 29 Jul reroute of the ~8,799 contacts parked in 973 Nurture Sequence 1 NEW COPY FROM CLAUDE into 758 Nurture Sequence 2, 2,000/day to avoid a send spike. Add-verify-then-remove per contact; guard skips the add if already in 758; state+log in tools/retention/migrate_973_to_758_{state.json,log.csv}. Prints DONE and no-ops when the source is empty → then unload + delete this row (expected ~2 Aug 2026) |
— (triggers 758 Nurture Sequence 2 sends to migrated contacts) |
| com.tma.failedpurchase | 05:55 daily | retention/failed_purchase_lane.py --draft — the third retention lane (§2.5): ONE personal note per person whose purchase attempt died at checkout. Armed 31 Jul 2026 after CRO copy review + the full-family audit (JOB_AUDIT_FINDINGS_2026-07-31.md). Cannot auto-send | ✅ drafts |
| com.tma.transcriptcompress | 03:30 Sun | ~/Library/Scripts/tma/compress_claude_transcripts.sh (canonical: tools/ops/compress_claude_transcripts.sh — re-copy to ~/Library/Scripts/tma/ after editing; runs from home because §6 applies to bash reading the script off T9 too, not just python). Gzips Claude Code session artefacts >7 days old across ALL ~/.claude/projects — the disk hog is per-session subagents/ fan-out logs (60–100 MB each), which REGROW structurally. Skips files any process holds open (lsof-checked); compresses, never deletes; log ~/.claude/transcript-compress.log. Armed 5 Aug 2026 after swap exhaustion froze the Mac (5.3 GB → 3.7 GB same day). Launchd execution proven live via launchctl start before this row was written. 🔴 CORRECTION 10 Aug 2026: that proof predated the 6 Aug wrap — harden_launchd.py then set /bin/bash as the interpreter of the Python wrapper, and the job never ran once after wrapping (exit 2 every attempt, masked by seed-credit). Interpreter fixed to python3 10 Aug; first real wrapped run compressed 181 files / 110 MB freed, verified in ~/.claude/transcript-compress.log | — |
| com.tma.jobguardian | every 30 min + at boot | ~/Library/Application Support/tma/bin/job_guardian.py (boot volume; source: tools/ops/job_guardian.py) — the §6c safety net: detects + kickstarts missed windows, alerts on failures. Deliberately unwrapped so it runs with T9 unplugged. Hardened 10 Aug 2026: 🔴 red alerts (CRITICAL-job miss · deterministic failure · never-ran · T9 unplugged · guardian crash) Cc Aga's Gmail, max one per problem per day; 🟠 FYIs stay hello@-only (12h dedupe). Dedupe stamps only on confirmed delivery. Emails via a vendored stdlib Gmail sender on the off-T9 token (~/.config/tma/gmail_hello_token.json) so the "T9 unplugged" alert can actually send. Stops kicking a job that fails identically twice (deterministic → 🔴 with log tail instead). Detects never-ran jobs (seed credit expires after 3d — the transcriptcompress shape). Re-bootstraps not-loaded plists once/day. Writes guardian_history.jsonl (rolling 7d — feeds the 05:55 email's 🤖 Jobs line + the console band). Watched back by cockpit_watchdog check 15 (guardian_status ts <45 min) | ✉️ alert emails only |
| com.tma.everpaid | 02:10 daily (NEW 10 Aug 2026) | retention/ever_paid_build.py — rebuilds the payer-exclusion set incrementally (~50s; watermark = prior built −2d overlap; auto-FULL every 14d to unwind late refunds; atomic write that refuses an empty set). Kills the RC3 outage class: funnel_stage_export.py (02:30) and segment_guard.py both gate on this file being <48h old, and nothing used to refresh it — both also now self-heal via retention/ever_paid_fresh.py as backstop | — |
| com.tma.abandondraft | 06:05 daily (NEW 10 Aug 2026) | retention/quiz_abandon_manual_notes.py --draft — the personalised quiz-abandon notes previously drafted only when someone typed the command by hand. Drafts only; --live deliberately does not exist — Aga sends | ✅ drafts |
| com.tma.inboxtriage | 06:10 daily (NEW 11 Aug 2026) | retention/inbox_triage.py --draft — the lane for inbound no other lane owns. Classifies hello@ (support · out-of-office · sensitive · vendor · unknown-sender), builds the full customer brief (support_brief.py), drafts a personalised reply, labels the thread SUPPORT — LEIH SENDS, and emails Leih's morning page to hello@. 🔴 NO --send MODE EXISTS (Aga, 11 Aug: "for support emails that you didn't craft, i need you to NOT send them ever — they need to be sent by Leih, only, or Aga"), and send_payment_drafts.py refuses that label explicitly, proven by its own selfcheck. Sensitive mail (refund · injury · legal · anger · press) gets NO draft and is flagged NEEDS A HUMAN DECISION. Out-of-office is detected on the RFC 3834 Auto-Submitted header (never the subject) and becomes a dated row in follow_up_queue.py, +1 day — never a reply, never engagement, never a STOP. | ✅ drafts + emails the page to itself |
| com.tma.offerreach | 07:10 Mon (NEW 10 Aug 2026) | email-analytics/offer_reach_check.py — refreshes data/email-analytics/campaign_automation_map.json, which sequence_scoreboard.py red-banners at >14d. Producer was unscheduled; the map would have crossed the line ~20 Aug | — |
| com.tma.backup | 01:30 daily (NEW 10 Aug 2026) | ops/backup_snapshot.py — hard-linked incremental snapshots of the whole asset estate to ~/TMARepoBackups/, 14 kept. Replaces scripts/backup_repo.sh, which made a FULL ~54 GB copy per run, was never scheduled, excluded APP ASSETS/, and had grown to 233 GB — taking the boot disk to 97% full, the exact condition that froze the Mac in August. --link-dest means every snapshot after the first costs only the delta while staying a complete browsable tree (measured: a 65 GB tree for 12 GB of real disk). Includes .git (9.8 GB) — the origin remote is dead, so the history exists in exactly one place. Refuses to keep a snapshot missing any of marketing/ docs/ data/ tools/ CLAUDE.md, and prunes itself if free space drops under 40 GB. Watched by cockpit_watchdog check 17 (stale >36h or incomplete → 🔴). 🔴 These live on the SAME MAC — they survive the T9 drive dying and accidental deletion, NOT theft, fire or the Mac dying. An offsite copy is still an open decision for Aga. | — |
| com.tma.jobselftest | 04:10 Sun (NEW 10 Aug 2026) | ops/job_selftest.py — tests the smoke detector. Every guard in this estate was proven by inducing a real failure on 10 Aug; nothing re-proved them afterwards, and protection systems rot silently (the watchdog's own alert emails pointed at /tmp paths dead for 12 days). This drill re-induces all six weekly: hung job → killed at timeout (124) · flaky job → absorbed by retry · exit-0-without-writing → rejected by --proof (65) · bash-wrapped plist → caught by the lint · backdated ever-paid set → self-heals · deadman endpoint → rejects a bad secret, accepts a real beat. Writes tools/ops/job_selftest_status.json; a FAILED drill surfaces in Aga's 05:55 email, as does a drill that has not run in >9 days | — |
| com.tma.asopull | 05:15 daily (NEW 10 Aug 2026; RETIMED from 06:20 → 05:15 on 11 Aug 2026 — at 06:20 it landed AFTER both daily surfaces (Slack 05:29, email 05:55), so every morning both rendered the PREVIOUS day's App Store pull. Now first in the morning chain. Backup: ~/Library/LaunchAgents/com.tma.asopull.plist.bak-2026-08-11) | aso/asc_analytics_pull.py --download then aso/aso_daily.py — refreshes data_aso_daily.json (App Store installs/deletes funnel). Was unscheduled: the cockpit card + Slack post quoted a 31 Jul pull as current for 10 days. Both surfaces now also render "as of
Since 29 Jul 2026 (§6c) every job above runs THROUGH
tma_job_run.py— same interpreter, plus T9 mount-wait, persistent logs (~/Library/Logs/tma/<label>.log— NOT/tmpanymore), and success stamps. New job? Write the plist, runharden_launchd.py --apply, add its row here.Wrapper hardened 10 Aug 2026: every job now gets a timeout (default 45 min per attempt,
--timeoutoverrides — funnelstage/everpaid/n973to758 carry 7200s; a hung job used to hang FOREVER, invisible) and one transient retry after 90s (--retry, default 1 — zeroes the 503/timeout class that produced the only 4 genuine failures in ~2,000 runs). Stamps recordretries+timed_out. 🔴 The wrapper's plist interpreter is ALWAYS python3 — reusing the job's own interpreter is the bug that brickedtranscriptcompressfor 4 days (bash "ran" the .py wrapper →import: command not found, exit 2, never once executed; fixed inharden_launchd.pythe same day).Exit-code semantics settled 10 Aug 2026: for CHECKER jobs (
cockpitwatchdog), exit 0 = "the checks RAN" — findings are the checker's own emails, deduped one per finding per day. Exit ≠0 is reserved for "the checker itself could not run". The old semantics (findings → exit 1) made the guardian kick a healthy watchdog into an alert storm — up to 6 duplicate emails/day about one item, plus a false "watchdog MISSED" alarm.
com.ghm.* and com.aga.* jobs belong to Gut Healing Mama / other projects — not TMA, not listed here.
tools/retention/)| File | Holds |
|---|---|
r1_status.json |
Machine state read by the watchdog, the cockpit and any LLM |
sent_log.csv |
Every R1 note sent, by segment |
reason_log.csv |
The churn-reason ledger (214 rows, 24 Jul) |
replies_state.json · reply_fulfil_state.json |
Idempotency for the two draft lanes |
suppress.csv |
STOP replies + bounces — never emailed again |
payment_recovery_state.json · payment_recovery_log.csv |
Dunning lane state; mode = draft / draft-sent |
upgrade_log.csv · upgrade_stage_log.csv · concierge_monthly.csv |
Monthly→Annual engine (990 Monthly→Annual Upgrade Engine) |
known_reasons.csv · survey_state.json |
Exit-survey cross-reference (5,022 rows, 24 Jul) |
| Job logs | ~/Library/Logs/tma/<label>.log (since §6c, persistent, rotated; /tmp paths are dead) |
| Success stamps | ~/Library/Application Support/tma/stamps/<label>.json — last_success = the "did it run?" truth |
| Guardian status | ~/Library/Application Support/tma/guardian_status.json (mirror: tools/ops/guardian_status.json) |
Any launchd job that reads /Volumes/T9 MUST use /opt/homebrew/bin/python3 or
tools/finance/venv/bin/python. NEVER /usr/bin/python3.
Apple's CommandLineTools python has no Full Disk Access, so it cannot even open a file on the external volume — the job dies before your script runs:
/Library/Developer/CommandLineTools/usr/bin/python3: can't open file
'.../payment_recovery_lane.py': [Errno 1] Operation not permitted
This silently killed com.tma.paymentrecovery from the day it was loaded (22 Jul) until 24 Jul —
it never produced a single draft. Every draft in that lane had been made by hand. A launchd job
that fails this way leaves an empty stdout log, so it looks idle rather than broken.
6a. The sibling trap — a job that needs a CLI, not just python. com.tma.seodailysync
ran green for days while silently failing its last step: the plist set no PATH, so
subprocess.run(["netlify", ...]) died with FileNotFoundError: 'netlify' after every
data pull had succeeded. The sync worked; only the deploy was lost, so the cockpit quietly
served stale SEO numbers. Fixed 29 Jul by adding the standard block every other TMA job
already carries:
<key>EnvironmentVariables</key>
<dict><key>PATH</key><string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string></dict>
Law for new jobs: if the script shells out to ANY CLI (netlify, gh, ffmpeg), the plist must
set PATH. launchd's default PATH is /usr/bin:/bin:/usr/sbin:/sbin — Homebrew is not on
it. Verify with launchctl print gui/501/<job> | grep PATH.
Health check (run any time):
launchctl list | grep com.tma # col 2 = last exit status; non-zero = failing
tail -5 /tmp/payment_recovery.err /tmp/tma_r1_daily.err
launchd does NOT backfill a StartCalendarInterval job whose window passed while the Mac
was powered off. (It does catch up after sleep — power-off is the gap.) The morning of
29 Jul the machine booted at 06:39, so the 05:35 (com.tma.r1daily) and 05:50
(com.tma.paymentrecovery) windows were simply lost: no reply scan, no dunning drafts, no
status refresh, and the live T-30 pre-expiry notes never went.
How to spot it — the signature is different from §6:
| §6 python-path trap | §6b power-off skip | |
|---|---|---|
launchctl print |
runs climbing, non-zero exit |
runs = 0 · last exit code = (never exited) |
/tmp logs |
present but empty | absent entirely (/tmp clears on boot) |
| State files | stale | stale, and uptime is younger than the missed window |
Confirm in one line: last reboot | head -3 — if the boot time is after the job's window,
the run was skipped, not broken.
Catch-up runbook (what was run on 29 Jul):
/opt/homebrew/bin/python3 tools/retention/check_replies.py # drafts only, safe
/opt/homebrew/bin/python3 tools/retention/reply_fulfil.py # drafts only, safe
/opt/homebrew/bin/python3 tools/retention/payment_recovery_lane.py # DRY first, always
/opt/homebrew/bin/python3 tools/retention/r1_daily.py # ⚠️ step 1 SENDS LIVE — Aga approves first
All four are idempotent (state files), so a catch-up run cannot double-send. r1_daily.py
needs Aga's word every time because its T-30 watcher sends live member email.
✅ CLOSED 29 Jul 2026 (PM) — see §6c. The guardian + wrapper now make this class of miss self-healing and loudly-alerted. The catch-up runbook above remains valid for manual use.
Every com.tma.* job now runs THROUGH a wrapper, and a new 18th job watches the other 17.
Built + verified the evening of the §6b incident. Source of truth: tools/ops/ in the repo.
1. tma_job_run.py — the universal wrapper (installed at
~/Library/Application Support/tma/bin/, runs under each job's ORIGINAL python so TCC/FDA
identity is unchanged — §6 law respected, bash never touches T9):
- Waits up to 15 min for /Volumes/T9 to mount instead of dying at spawn (the T9
WorkingDirectory was removed from every plist and moved into the wrapper — launchd can no
longer kill a job at boot because the volume wasn't mounted yet).
- Persistent logs: ~/Library/Logs/tma/<label>.log (timestamped per run, 5 MB rotation).
/tmp logs are GONE — §6b's "logs absent after reboot" can't recur.
- Success stamps: ~/Library/Application Support/tma/stamps/<label>.json — last_success
moves only on exit 0. This is the machine-readable answer to "did it actually run today?"
2. com.tma.jobguardian — the safety net (every 30 min + RunAtLoad at boot;
deliberately UNWRAPPED and on the boot volume so it runs and alerts even with T9 unplugged):
- Parses the live plists each pass (plists ARE the manifest — new jobs auto-covered, nothing
to drift), computes each job's most recent scheduled window, compares to its stamp.
- Missed window + 25 min grace → launchctl kickstart the job (max 2 kicks per
occurrence, then alert-only). A power-off morning now self-heals within ~30 min of boot.
- Also checks each pass: job loaded at all · non-zero last exit · T9 mounted · Gmail token
freshness (<14 d) · installed wrapper/guardian hash vs repo copy (drift self-reported).
- Alerts: macOS notification + email to hello@ (existing r1_lib rail, deduped 12 h/issue)
+ status JSON ~/Library/Application Support/tma/guardian_status.json (mirrored to
tools/ops/guardian_status.json when T9 is mounted).
Verified on install night: wrapped paymentautosend ran clean under launchd (homebrew
python through wrapper, TCC intact) · stamp rewound to simulate a §6b miss → guardian
KICKED the job, it ran, re-stamped, next pass clean · guardian caught a real same-night
issue (cockpitrefresh netlify 403, transient — redeployed OK) and its alert email landed in
hello@ at 21:05.
Operational notes:
- harden_launchd.py --apply is idempotent — re-run it after editing wrapper/guardian in the
repo, or to enrol a NEW job (new jobs get wrapped + stamped + guarded automatically; write
the plist, run --apply). --audit = read-only table.
- Plist XML comments were stripped by the rewrite — the annotated originals live in
tools/ops/launchd_snapshots/2026-07-29-2059/ (+ ~/Library/LaunchAgents/tma-backup-*).
- launchctl kickstart can BLOCK while a service is inside its ThrottleInterval — the
guardian's kick calls carry a 120 s timeout for this reason.
- ✅ Root cause closed same session: pmset repeat wakeorpoweron MTWRFSU 05:20:00 applied
with Aga's password (via osascript admin-privileges dialog — plain sudo has no TTY under
the ! runner) and verified: pmset -g sched → "wakepoweron at 5:20AM every day". The Mac
is awake before the earliest window (05:35), so launchd's no-backfill-after-power-off gap
cannot bite. The guardian remains the net beneath it. To inspect/remove:
pmset -g sched · sudo pmset repeat cancel.
# What's sitting in hello@ right now (dry, sends nothing)
python3 tools/retention/send_payment_drafts.py
# Send the approved dunning drafts (this lane only — never touches reply drafts)
python3 tools/retention/send_payment_drafts.py --send
# R1 reply drafts: open hello@ → Drafts → read → send by hand.
# They are personal 1:1 replies. Read them; the reply IS the save.
Send-time doctrine: mornings. Read tools/retention/R1_SYSTEM_STATE.md (current state +
work queue) and tools/retention/AGA_RUNBOOK.md (how to send) before touching a lane.
upgrade-eligible + the UPGRADE_LIVE sentinel file.com.tma.seoselflearn added 24 Jul PM); com.tma.paymentrecovery fixed and re-bootstrapped today.Every other guard in this document runs ON Aga's Mac. If the Mac powers off, panics, or loses its login session, every job and every alarm about those jobs goes silent together — and silence is indistinguishable from health. That was the last uncovered failure class in the estate.
https://tma-deadman-switch.netlify.app — a tiny site on Netlify's infrastructure,
deliberately sharing nothing with the Mac. Source: tools/ops/deadman/.
beat — the job guardian POSTs here every pass (~30 min), authenticated by a
shared secret (tools/ops/deadman/.secret.local, gitignored; Netlify env
DEADMAN_SECRET). The guardian prints deadman ✓ / ✗ on every pass.check — runs on Netlify cron, hourly (17 * * * *) and watches TWO
independent signals: the heartbeat (stale >3h ⇒ the Mac is gone) and the live
cockpit page's own auto-refreshed stamp (stale >30h ⇒ the morning pipeline died
even if the Mac is alive). Alerts Slack, re-notifying at most every 6h, and posts
a single "recovered" when beats resume.🔴 Slack, not email, on purpose: every email rail in the estate runs through the Mac's Gmail token, so email cannot be trusted to report the Mac's own death.
🔴 Two Netlify traps, both paid for on 10 Aug 2026: (1) an env var is invisible to a
function unless set with an explicit --scope functions runtime — the only symptom
is a 403 that looks like a wrong secret; (2) netlify env:set --site X is silently
IGNORED when the working directory is linked to a different site — it writes to the
linked one. Link the directory first, then set. Also: a scheduled function correctly
returns 403 to HTTP — that is proof the schedule is attached, not a fault.
| Date | Change |
|---|---|
| 2026-08-10 (part 2) | THE SECOND HARDENING PASS — closing what the first one left. (1) 🛰️ Off-machine dead-man's switch (§8b) — the last zero-coverage class; proven by forcing both signals stale and watching the Slack alert land, then confirming the 6h rate-limit suppressed the duplicate. (2) Plist lint in the guardian — catches a mis-wired job on the NEXT PASS instead of after 3 days; proven by re-creating the exact /bin/bash-runs-the-python-wrapper bug and watching it fire red. (3) --proof at the wrapper — enforces Aga's own "exit 0 lies when the write is at the end" law in code: a job that exits 0 without refreshing its named artifact is rejected with exit 65; wired into everpaid, emailmetrics, asopull, offerreach. (4) Weekly drill com.tma.jobselftest (Sun 04:10) — re-induces all six failures automatically, because a guard nobody exercises is a guard nobody can trust; a FAILED drill surfaces in the 05:55 email. (5) Slack as the guardian's second alert rail — all alerting rode ONE Gmail token; now a failed send falls through to Slack, with the webhook cached off-T9 (~/.config/tma/slack_alert_webhook, 0600) because the first version read it from .env.local on the drive whose absence is the alert — caught by induced test. (6) Two closers: the 05:45 PM brief now carries the same 24h jobs line (it had zero job health), and the guardian flags any job running without a register row — which found com.tma.socialselflearn unregistered on its first pass. |
| 2026-08-10 | THE HARDENING PASS — "these jobs NEVER break" (Aga's directive). Root-caused all 14 failures in ~2,000 recorded runs to 6 causes and closed each class. (1) transcriptcompress had NEVER run — harden_launchd.py reused the job's own interpreter for the wrapper, so bash "ran" a Python file (exit 2 every run, hidden by seed-credit); plist fixed, harden bug fixed, first real run compressed 181 files / 110 MB. (2) Watchdog exit semantics split: findings now exit 0 + dedupe 1/finding/day — was up to 6 duplicate emails/day + false MISSED alarms (the alarm alarming on the alarm). (3) RC3 class (freshness-gated artifact, unscheduled producer) killed: ever_paid_build.py made incremental (50s vs 60min) + atomic + auto-full/14d, scheduled (everpaid 02:10); segment_guard + funnel_stage_export share one self-heal (ever_paid_fresh.py); offerreach (Mon 07:10) + asopull (06:20) scheduled; guardian's token check repointed to the LIVE off-T9 token (false alarm was due 14 Aug). (4) Wrapper: per-attempt timeout (45min default, 7200s on the 3 long jobs) + one 90s transient retry. (5) Guardian: red-only Cc Aga (her ruling — digest + red-only), dedupe-after-delivery, vendored off-T9 Gmail sender, deterministic-failure stop (2 identical failures → 🔴 with log tail, no more kicks), never-ran detection, not-loaded re-bootstrap, guardian_history.jsonl, crash self-report; watchdog check 15 watches the guardian back. (6) Digest: 05:55 email carries a 🤖 Jobs line from 24h history; console band humanized; dead /tmp paths in alerts fixed; 4 unguarded cockpit-refresh steps soft-failed. ytretrofit retired (869/869 done). Everything proven by induced failure, not assertion — deterministic ladder (kick→kick→🟠→🔴+stop), watchdog finding (exit 0, one email, dedupe verified), hung job (killed at timeout, exit 124), flaky job (absorbed, exit 0, retries:1 stamped), heal (backdated 72h → rebuilt 46s → re-read artifact). Final guardian pass: 29 ok · 0 kicked · 0 issues. |
| 2026-07-31 | Failed-purchase lane ARMED (com.tma.failedpurchase, 05:55 daily, drafts-only) after Aga's pre-flight: CRO copy review (2 line edits — "cross-border charge", "call your bank"; dual-exit structure kept as load-bearing) → full-family bug audit, 30 findings → documentation. 8 fixed same session, incl. 🔴 C1: nothing was watching replies to dunning/failed-purchase emails — a STOP never reached suppress.csv (fix immediately surfaced a real unread reply from 28 Jul) · C2: upgrade_tag KeyError that would have broken 990 Monthly→Annual Upgrade Engine the day it armed · H2: unpaginated ever_paid could have emailed a paying member · M7: non-atomic shared Gmail token write (would take every hello@ lane dark) · H1: my own malformed plist that also crashed harden_launchd.py. Full register + 12 open items: JOB_AUDIT_FINDINGS_2026-07-31.md. Also corrected §0 (14-hour rule is a third auto-send exception) and §2.5's false reply-pipeline claim. |
| 2026-07-29 | §6c — HARDENING SHIPPED (PM session). All 17 jobs rewrapped via tools/ops/harden_launchd.py --apply: universal wrapper (T9 mount-wait · persistent ~/Library/Logs/tma/ logs · success stamps), new 18th job com.tma.jobguardian (30-min + boot safety net: parses live plists, kickstarts missed windows, alerts via notification + hello@ email + status JSON). Missing PATH fixed on cockpitemail + seoselflearn. Verified: simulated §6b miss auto-kicked + self-healed; real cockpitrefresh netlify 403 caught + alerted same night (transient — manual redeploy OK). Plist snapshots: tools/ops/launchd_snapshots/2026-07-29-2059/. Root cause ALSO closed same session: pmset repeat wakeorpoweron MTWRFSU 05:20:00 applied + verified (§6c). |
| 2026-07-29 | Registered com.tma.n973to758 (09:40 daily, homebrew python, /tmp logs — §6-compliant; temporary, self-completing) — Aga-approved reroute of the ~8,799 contacts parked in 973 Nurture Sequence 1 NEW COPY FROM CLAUDE (whose emails contain zero offer links) into 758 Nurture Sequence 2, batched 2,000/day. Pilot 5 contacts verified end-to-end (758 record active, 973 record(s) gone, duplicate records cleaned); batch 1 (1,995) launched same session. Unload + delete the §4 row when it prints DONE (~2 Aug). Same session: ambassador/affiliate links stripped from all 32 live emails of 751 Nurture Sequence 1 + 758 Nurture Sequence 2 via PUT /api/3/messages/{id}; originals in marketing/email-sequences/_BACKUPS/751-758-pre-linkfix-2026-07-29/. |
| 2026-07-29 | §6b added — the power-off skip. Mac booted 06:39, so the 05:35 + 05:50 windows were lost (runs = 0, /tmp logs absent). Caught up by hand: check_replies 0 new · reply_fulfil 0 new · dunning dry 0 due today (in-lane 20, +1 recovered) · r1_daily full run on Aga's approval → T-30 watcher sent 3 live (mike.goldman@me.com · mail.christophe.martel@gmail.com · fatma.tbee@gmail.com), survey 5,029 rows, queue 111 / $1,568.57, cockpit patched. hello@ drafts before and after: 0. |
| 2026-07-29 | One draft added by hand — reply to Goran (goranf@gmail.com, lifetime, abs progression stuck) in his existing thread. Not a lane draft, so send_payment_drafts --auto can never touch it (it hard-filters to the 3 dunning subjects). Awaiting Aga/Leih. |
| 2026-07-24 | Registered com.tma.seoselflearn (Mon 08:45, venv python, /tmp logs — §6-compliant from day one) — the SEO fleet redesign's weekly self-learn: pure-Python watch adjudication (GSC per-page 14d post vs pre; REVERT-FLAG never auto-reverts) + weekly CMO/CEO report + human-queue re-rank. Loaded + verified (launchctl list), dry-run + real run both clean (12 armed watches found, 0 flags). |
| 2026-07-24 | Extended com.tma.seodailysync — now also renders marketing/seo/SEO_DAILY_BRIEF.md (5 fleet KPIs: organic quiz-starts d7/d28 generate_lead · GSC both domains · AI-referral sessions · link-integrity guard · changelog throughput 24h) + the ≤3/≤3 AGA/NIC human lists from the new tracked queue marketing/seo/SEO_HUMAN_QUEUE.json (§12.7-E contract), mirrored into the cockpit SEO-LIVE block. Also fixed a latent bug: gsc_pull summary --json returns a LIST, but the renderer only read dicts — the cockpit GSC cells had silently rendered "—" since the block was born. Zero LLM tokens throughout. |
| 2026-07-24 | Fixed com.tma.socialdash — every run since creation exited 126: launchd ran the .sh via /bin/bash, which has no Full Disk Access to /Volumes/T9 (§6 class). Ported to tools/social-pipeline/refresh_social_dashboard.py run by the finance venv python; plist rewritten + reloaded; verified live (board deployed to tma-social-scoreboard.netlify.app). The .sh stays for manual use. |
| 2026-07-24 | Fixed com.tma.seodailysync — never spawned (exit 78, no log files ever created): StandardOutPath/StandardErrorPath pointed at /Volumes/T9 paths launchd cannot create (§6 class — same law, log-path variant). Moved logs to /tmp/tma_seo_sync.out|.err, reloaded. NOTE for new jobs: logs go to /tmp, never onto T9. |
| 2026-07-24 | Fixed com.tma.paymentrecovery — /usr/bin/python3 → /opt/homebrew/bin/python3, added PATH + WorkingDirectory to match com.tma.r1daily. plutil OK, re-bootstrapped, dry-run verified (3 candidates found). Backup: com.tma.paymentrecovery.plist.bak-2026-07-24. |
| 2026-07-24 | Sent all 5 waiting drafts (4 dunning T2/T3 + 1 R1 reply). hello@ drafts → 0. |
| 2026-07-24 | ⏸️ Aga parked the failed-purchase lane — new checkouts incoming will feed richer Stripe data; build against that signal, not today's. R1 + dunning continue unchanged. PI-metadata requirement moved to a build-time ask on the new checkouts. |
| 2026-07-24 | Built failed_purchase_lane.py (§2.5) — the third lane. Dry-run clean: 30d window → 14 people, 9 actionable, 5 correctly skipped. Not scheduled, not armed. |
| 2026-07-24 | Established that Stripe holds every failed purchase attempt (charge + decline code + email + cardholder name + amount) but carries NO plan/coupon — metadata is empty on both charge and PaymentIntent. Plan is inferable from amount only. Nic fix = write plan_slug/coupon/user_id into PI metadata — the same hook already briefed for cart-abandon Path B (NIC-CART-ABANDON-STRIPE-AC-BRIDGE-2026-07-22.md). |
| 2026-07-24 | This register created. |
| 2026-07-23 | 18 of 22 dunning drafts approved + sent. |
| 2026-07-22 | com.tma.paymentrecovery first loaded (broken from day one — see §6). |
Why this exists. Aga's pre-flight rule before arming the failed-purchase lane: "look for any inconsistencies, anything that could break or any bugs for all the jobs, document them all — and then we execute." This is that register.
Scope: all 21 retention/email scripts + the launchd wrapper/guardian + the live plists + live state files, read against
AUTOMATION_JOB_REGISTER.md. Read-only audit by an independent agent; every finding below was re-verified by hand before any fix. Status key: ✅ FIXED (this session) · 🔴 OPEN · ⚪ ACCEPTED (known, not worth fixing yet).
30 findings. 8 fixed today (every one that could email the wrong person, plus the two that could take the whole rail down). The remainder are documented, ranked, and none of them block the failed-purchase lane.
The single most valuable find had nothing to do with the new lane: nothing was watching
replies to dunning emails. Every dunning and failed-purchase note promises "Reply STOP and I
won't email you about it again" — and no scanner watched those subjects, so a STOP never
reached suppress.csv. Within one minute of the fix going in, it surfaced a real reply that had
sat unread since 28 Jul: Simon replying "I've already cancelled the subscription" to a
dunning note. Nobody had ever seen it.
| # | Sev | What was wrong | Fix |
|---|---|---|---|
| C1 | CRITICAL | STOP replies to dunning/failed-purchase were never captured. check_replies walks only R1 threads; reply_fulfil.RULES covered only the 986/976 arcs. A member could reply STOP and still get T2 three days later — auto-sent, no human in the loop. The failed-purchase lane's entire conversion mechanic ("hit reply and I'll send you a fresh link") also had no pipeline. |
reply_fulfil.py now watches all 4 payment subjects (imported, never retyped). STOP → suppress.csv automatically. Live replies are surfaced as 🔴 NEEDS AGA and counted into r1_status.json — deliberately no auto-draft, because an auto-drafted "here's your link" with no link is worse than nothing. |
| C2 | CRITICAL | upgrade_tag.py:286 read row['annual_saving'] — a key that never existed. KeyError on every live tagging, swallowed by a blanket except. AC fields + upgrade-eligible tag were written first, so 990 Monthly→Annual Upgrade Engine would start emailing while the log row was never written → Stage 2/3 could never fire, conversion detection never ran, member re-processed daily. Dry runs never reached the line. |
row['annual_saving_129']. Latent (gated behind the UPGRADE_LIVE sentinel) — it would have broken the day you armed the upgrade engine. |
| C3 (part) | CRITICAL | Non-atomic state writes + write-once-at-end. A crash mid-run lost the whole run's state → next run re-drafted the same notes → two identical drafts, both auto-sendable. | failed_purchase_lane: atomic save_state() (tmp+rename) after every draft. Dunning/autosend pair: see 🔴 O1. |
| M7 | MEDIUM→HIGH | The shared Gmail token was written non-atomically. Four scheduled lanes + the guardian all refresh through one token.json; two interleaved writes corrupt it and every hello@ lane goes dark until manual re-auth. |
r1_lib._creds() now writes tmp+os.replace. |
| H1 | HIGH | The new lane's plist (written earlier this session) had --auto inside an XML comment — illegal XML. plutil passes but plistlib throws: job never loaded, and harden_launchd.py crashed outright, so no job could be wrapped or enrolled. Self-inflicted, caught by the audit. |
Comment reworded (no -- sequences) + a warning note in the plist so it can't recur. harden_launchd.py --audit verified working again. |
| H2 | HIGH | ever_paid() used unpaginated customers limit:10 + charges limit:100. A serial retrier (one had 21 attempts; Payment-Element checkouts can mint a customer per attempt) could push the successful charge off page 1 → "your payment didn't go through" emailed to a PAYING member. |
Fully paginated via a new _paged() helper, and fails SAFE: any Stripe error now returns True (treat as paid → don't email). |
| H3 | HIGH | "One note per person, ever" was actually one note per charge — state keyed by charge id, so a retrier who failed again next week got a second "one-time" note. | Now also skips any email already in state. |
| M11 | MEDIUM | Charge filter was status != "succeeded", which swept up pending charges → a payment still processing could be told it failed. |
status == "failed" only. |
Guarded against regression: an assert at import time fails loudly if the failed-purchase
subject ever collides with a dunning subject — the invariant that keeps its drafts human-gated.
31 Jul (later): O1 is FIXED (struck through below, kept for the record). O6 remains the gate on arming 976 Cancelled Win-Back — paginate the customer lookup first, or an active paying member can be tagged
wb-eligibleand offered a win-back discount.
| # | Sev | Finding | Why it can wait |
|---|---|---|---|
| ~~O1~~ | ✅ FIXED 31 Jul | ~~The dunning ↔ autosend pair still has C3's double-send risk~~ — CLOSED. Aga ruled "fix it" 31 Jul. New tools/retention/state_io.py: atomic_write_json() (temp file in the same dir → fsync → os.replace, so a crash leaves the old file or the new one, never a truncated one) + lane_lock() (shared, non-blocking flock). Both payment_recovery_lane.py and send_payment_drafts.py now take the SAME lock around their whole run and exit 0 if the other holds it — a skipped pass is free, an overlapping pass is the double-send. Both also save state per item immediately after a touch/send, so a crash costs at most one member instead of the run's stamps. read_json() survives an empty/truncated file without the delete-the-file "fix" that re-drips T1. Verified: atomic round-trip + no temp files left · second process correctly excluded · both real scripts skip when the lock is held · live dry run of the lane leaves all 31 records byte-identical. |
— |
| O2 | HIGH | send_payment_drafts lists at most 100 drafts, unpaginated; Gmail returns newest-first, so the oldest (ripest to send) fall off first. Approved dunning email silently never sends and nothing reports it. |
Only bites above 100 simultaneous drafts. Follow nextPageToken. |
| O3 | MEDIUM | r1_queue_emails() fails open (except: return set()): if the subscriptions call fails while invoices succeed, the "R1 owns them" skip silently evaporates → two lanes email the same person in one week. |
Should raise and abort the run instead. |
| O4 | MEDIUM | Autosend does no send-time spacing or re-check: after an outage, T2 and T3 for one member can both be >14h and fire in the same pass; it never re-reads suppress.csv or re-checks whether the invoice got paid. |
One send per recipient per pass + re-check suppress/invoice. |
| O5 | MEDIUM | Guardian treats a long-running job as MISSED — compares only last_success, never the last_attempt stamp. com.tma.n973to758 runs ~95 min → burns both kicks and raises a false alert. |
Cosmetic today (kickstart on a running service is a no-op), but it cries wolf. |
| O6 | MEDIUM | saves()/check_saves cap customer lookups at 10 records → a member whose active sub sits on an 11th record is misread as "expired unsaved" → wb_tag marks them wb-eligible → when 976 Cancelled Win-Back activates, an active paying member gets a win-back discount. |
Latent until 976 is armed. Paginate before then. |
| O7 | MEDIUM | upgrade_tag._lifetime_paid caps at 100 invoices → undercounts "you've paid $X" precisely for the longest-tenure members the offer targets. |
Latent behind UPGRADE_LIVE. |
| O8 | MEDIUM | migrate_973_to_758.api() retries a POST after an ambiguous timeout → duplicate AC records → contact runs 758 Nurture Sequence 2 twice in parallel. |
Job self-completes ~2 Aug; check already_in_dst before retrying if it recurs. |
| O9 | MEDIUM | Crash-mid-run can duplicate reply drafts (check_replies/reply_fulfil save state only at end). |
Human-gated (Aga reads before sending). Same fix pattern as C3. |
| O10 | MEDIUM | Two unguarded cross-lane pairs: cart-abandon vs failed-purchase (same person, same day, two emails) once the blur-write ships; and send_upgrade_stage doesn't skip current R1 cancel-queue members. |
Both latent. Add the state-file checks when either ships. |
| O11 | MEDIUM | R1 can never re-touch a repeat canceller — already_sent() is a lifetime set, so someone saved in July is invisible if they cancel again next year. |
Design gap that grows quietly. Key on (email, subscription) or expire on re-entry. |
| O12 | LOW ×10 | draft_body reads only text/plain (HTML-only edited draft → empty body → needs_aga sees subject alone) · sent stamp hits the first matching record when a person has two open invoices · missing internalDate → infinite age → immediate send · tz off-by-one on manual evening runs · watcher send-count scraped from stdout · EMAIL_FIXUPS typo remap not applied in the new lane · reply_fulfil searches in:anywhere (spam included) · retention_cockpit_sync unescaped regex replacement · wb_tag never retries NOT_IN_AC · wrapper keeps a stale last_error. |
Hygiene. Documented for whoever touches these next. |
The audit checked AUTOMATION_JOB_REGISTER.md against reality and found these drifted:
--live exceptions" contradicted §4's own autosend row — the 14-hour rule is a third exception. Now stated in §0.reply_fulfil.py" was false as coded — true only as of today's C1 fix./tmp log paths (§6c moved logs to ~/Library/Logs/tma/ on 29 Jul).send_payment_drafts.py's docstring still calls itself a "one-shot companion"; it is a twice-daily scheduled auto-sender.:144 → :177); §8's job count (14) predates the current 18 + guardian.Can
send_payment_drafts --autoever send a failed-purchase draft?
No. Its filter requires BOTH: subject exactly equal to one of the three dunning subjects
(imported from payment_recovery_lane.SUBJECTS — list membership, not prefix match) AND the
recipient present in payment_recovery_state.json. The failed-purchase subject diverges at
character 5 and its recipients live in a different state file. R1 and reply_fulfil drafts all
carry Re: prefixes and cannot match either. Unrecognised drafts are skipped and counted
"left untouched".
The invariant previously hung on string constants in two files with nothing guarding it — now covered by an import-time assert.
A limit=100 scan with no pagination silently truncated a Stripe census on 30 Jul (undercount
4×) and would have emailed a paying member here. Full sweep:
r1_lib.fetch_queue · payment_recovery_lane.failed_renewal_invoices ·
failed_purchase_lane.failed_purchase_charges · cart_abandon_reconcile._recent_payment_intents ·
upgrade_tag._active_subs · migrate.enumerate_active_in_src · failed_purchase_lane.ever_paid (fixed today)upgrade_tag._lifetime_paid (O7) · saves/check_saves customers (O6) ·
send_payment_drafts Gmail drafts (O2)reply_fulfil._search maxResults=50 (self-heals across daily runs)Standing rule going forward: never use a bare limit=N list for a safety check or a count.
Audit + fixes: 31 Jul 2026. Companion: AUTOMATION_JOB_REGISTER.md (canonical job register) ·
cockpit ⚙️ Daily Jobs tab (live health).
Set by Aga, 31 July 2026. Governs every email the retention rail sends or drafts from
hello@themovementathlete.com: R1 cancel-save, the T-30 watcher, dunning/payment-recovery, failed-purchase, reply-fulfilment, concierge, upgrade stages — and any lane added later.The one sentence that decides every question below:
It has to look like Jesse just typed it.
These emails work because they are indistinguishable from a personal message. Every rule here exists to protect that. When a new idea conflicts with it, the idea loses.
Two completely different systems share the word "email". Never apply one's rules to the other.
| hello@ 1:1 rail (this document) | Marketing estate (ActiveCampaign / Customer.io) | |
|---|---|---|
| What | Personal notes from Jesse to one member about their account | Campaigns, sequences, broadcasts |
| Looks like | A person typed it | A designed email |
| Governed by | This file | CLAUDE.md EMAIL RULE → /email-fleet, locked footer, email_lint.py, white canvas, design guide |
| Template | None. Ever. | Canonical template, mandatory |
| Sent by | Gmail API from hello@ | AC / CIO |
Running these through the marketing fleet would destroy the thing that makes them convert. Running marketing emails under these rules would fail the lint gate. Keep them separate.
No bold. No underline. No italics. No colour, no headings, no dividers, no images, no logo, no footer bar, no background colour, no buttons. A person writing to one other person uses none of these. (Aga, 31 Jul: "remove the bold so it really looks like Jesse just typed it.")
The renderer supports **bold** / _italic_ / __underline__ markers — do not use them in
lane copy. They exist only so a future exception can be made deliberately, not by drift.
Note the deliberate difference from the marketing law. The estate rule ("bold the key line, italic the human beats, one underline") is correct for campaigns and wrong here. Same goal — easy reading — opposite means: in a personal email, readability comes from short paragraphs, not from formatting.
This is how people actually type, and it is the ONLY readability tool available under rule 1. Never stack the bad news, the reassurance and the instruction into one block. Break them.
(Aga, 31 Jul: "make a link an actual clickable link — instead of a dead link they need to copy
and paste.") A URL sitting on its own line becomes a real anchor.
- Standard link blue #1155cc — Gmail's own. Not a brand colour: branded links read as
a campaign at the exact moment we need trust.
- Keep the → arrow — under rule 1 it is the only visual cue the text is clickable.
- Built automatically by r1_lib.build_message() / text_to_html(). Never hand-build MIME.
Never "click here" (generic, tests poorly). Never over-promise what the first screen shows.
- Dunning → Fix your billing → — task-framing. It must NOT say "Update your card",
because the link lands on the Stripe login page (enter email → Stripe sends a sign-in
link → then the card form). Promising a card form and delivering a login box creates doubt
on a billing page, which costs more than any click-rate gain.
- R1 cancel-save → Keep training → — outcome-framing. They chose to leave; administrative
language reads as the process they already rejected. The email's real goal is a reply.
- Different lanes get different anchors on purpose — the member's psychological state
differs. Any new anchor goes through the email-copywriter agent first.
text/plain (markers stripped, bare URL) + text/html (the clickable twin). Non-negotiable:
- plain-text clients and forwards must work;
- send_payment_drafts.needs_aga() reads the plain part — lose it and the safety gate goes
blind (audit finding L1);
- a body with no link stays pure plain text — no HTML part at all (e.g. failed-purchase
notes, guardian alerts). Don't add HTML for its own sake.
Reply-to is always hello@, a human reads it, and the copy says so. Where a lane can convert on
a reply instead of a link (failed-purchase: "hit reply and I'll send you a fresh link"),
prefer the reply — it has no links to distrust and it starts a conversation.
Every lane's subject must be watched by reply_fulfil.py so STOP suppresses and a live
reply is surfaced. (This was missing until 31 Jul — audit finding C1.)
Jesse
Head Coach, The Movement Athlete
--
You're receiving this because <the specific reason>. This isn't a marketing campaign.
Reply STOP and I won't email you about it again.
Jesse is Head Coach, never "founder". Say why they're getting it, in one honest clause. The STOP promise is real — rule 6 makes it work.
Copy branches on the actual failure reason (bank block vs card details). No invented urgency, no fake deadlines, no claims we haven't verified. If we caused it, say so.
Every member-facing message is a Gmail draft waiting for Aga. Three explicit exceptions —
the R1 T-30 watcher, the upgrade stages (both --live, templated pre-approved copy), and the
14-hour auto-send rule scoped to the dunning lane only. A new lane arms drafts-only.
Aga's pre-flight law: run the copy through the email-copywriter (CRO) seat, audit the whole
job family for bugs, document it into the register + cockpit — then arm.
| Thing | File |
|---|---|
| Renderer (multipart, links, markers) | tools/retention/r1_lib.py → build_message() · text_to_html() · strip_markup() |
| Dunning copy + anchor | tools/retention/payment_recovery_lane.py (BODIES, make_draft) |
| R1 copy + anchor | tools/retention/copy/seg-*.txt · send_r1.py · pre_expiry_watch.py |
| Failed-purchase copy | tools/retention/failed_purchase_lane.py (no links — reply mechanic) |
| Reply + STOP capture | tools/retention/reply_fulfil.py (PAYMENT_SUBJECTS) |
| Safety gate | tools/retention/send_payment_drafts.py (needs_aga) |
Canonical companions: docs/company/GOVERNANCE/AUTOMATION_JOB_REGISTER.md (what each lane
does) · JOB_AUDIT_FINDINGS_2026-07-31.md (known bugs + open items).
Changing any rule here changes what a real member receives. Update this file in the same session, or it is drift.
| Job | When | Runs | hello@ |
|---|---|---|---|
| r1daily | 05:35 daily | retention/r1_daily.py | ✉️ drafts + T-30 sends |
| paymentrecovery | 05:50 daily | retention/payment_recovery_lane.py --draft | ✉️ drafts |
| failedpurchase | 05:55 daily | retention/failed_purchase_lane.py --draft | ✉️ drafts |
| paymentautosend | 08:00 + 20:00 | retention/send_payment_drafts.py --auto — the only unattended sender; dunning lane only, 14-hour rule | ✉️ SENDS |
| jobguardian | every 30 min + boot | ops/job_guardian.py — kicks missed windows, alerts hello@ | ✉️ alerts only |
| n973to758 | 09:40 daily (temporary) | retention/migrate_973_to_758.py --batch 2000 | — |
| cockpitrefresh | 06:00 daily | finance/refresh_cockpit_daily.py | — |
| emailmetrics | 06:10 daily | email-analytics/collect_metrics.py | — |
| morningpulls | 06:15 daily | finance/morning_pulls.py | — |
| cockpitwatchdog | 06:30 daily | finance/cockpit_watchdog.py | — |
| socialdash | 06:30 daily | social-pipeline/refresh_social_dashboard.py | — |
| seodailysync | 06:40 daily | seo/seo_daily_sync.py --deploy | — |
| cockpitemail | 07:00 daily | finance/daily_cockpit_email.py | ✉️ to hello@ (internal) |
| mondaybrief | 07:00 Mon | finance/monday_brief.py | ✉️ internal |
| emailselflearn | 08:12 Mon | email-analytics/run_selflearn.py | — |
| seoselflearn | 08:45 Mon | seo/seo_selflearn.py | — |
| ytselflearn | 08:30 Tue | youtube-analytics/yt_selflearn.py | — |
| ytretrofit | 09:15 daily | youtube-analytics/apply_retrofit_all.py --apply | — |
| growthsync | every 15 min | finance/growth_repo_sync.py --pull-deploy | — |
com.ghm.* / com.aga.* jobs belong to other projects — not TMA, not listed.Any launchd job that reads /Volumes/T9 MUST use /opt/homebrew/bin/python3 or the finance venv — NEVER /usr/bin/python3. Apple's CommandLineTools python has no Full Disk Access, so it dies with [Errno 1] Operation not permitted before the script runs, leaving an empty log — so it looks idle, not broken. This killed paymentrecovery silently for two days. Health check: launchctl list | grep com.tma (col 2 = last exit status).
python3 tools/retention/send_payment_drafts.py # dry: what's in hello@ python3 tools/retention/send_payment_drafts.py --send # send dunning drafts only # R1 reply drafts: open hello@ → Drafts → read → send by hand (the reply IS the save)
AUTOMATION_JOB_REGISTER.md · lane state tools/retention/R1_SYSTEM_STATE.md · how to send tools/retention/AGA_RUNBOOK.md.
Per-platform reach, saves and leads, with every tile stamped by verification state.Open ↗
Leads 142 / 28d (next rung 30/wk) · 11,378 social sessions · youtube 9,293s/6L · facebook 1,073s/84L · instagram 964s/51L · pinterest 48s/1L · untagged 1257 · YT tagged 100% (866/869, auto ~195/day) · leads =
generate_leadonly · every tile stamped VERIFIED/MANUAL/MODELLED/DARK/MOCKtools/social-pipeline/social_status.json(incl.workstream— edit there, never here) · board generatorbuild_dashboard.py· syncsocial_sync.py· daily 06:30com.tma.socialdash· YT retrofitcom.tma.ytretrofit09:15 · social mddocs/company/ANALYTICS/NIC_SOCIAL_TASKS_2026-07-24.md· memorysocial_scoreboard_v2_worldclass_2026-07-24