diff --git a/studies/202512_GenesysCX/ctm-token-calculator/docs/Appendix 4 - CCaaS Platform Benefit Calculations (ASIA_Conservative).PDF b/studies/202512_GenesysCX/ctm-token-calculator/docs/Appendix 4 - CCaaS Platform Benefit Calculations (ASIA_Conservative).PDF new file mode 100644 index 0000000..b907845 Binary files /dev/null and b/studies/202512_GenesysCX/ctm-token-calculator/docs/Appendix 4 - CCaaS Platform Benefit Calculations (ASIA_Conservative).PDF differ diff --git a/studies/202512_GenesysCX/ctm-token-calculator/docs/Appendix 4 - CCaaS Platform Benefit Calculations (AnZ_Conservative).PDF b/studies/202512_GenesysCX/ctm-token-calculator/docs/Appendix 4 - CCaaS Platform Benefit Calculations (AnZ_Conservative).PDF new file mode 100644 index 0000000..2e079ff Binary files /dev/null and b/studies/202512_GenesysCX/ctm-token-calculator/docs/Appendix 4 - CCaaS Platform Benefit Calculations (AnZ_Conservative).PDF differ diff --git a/studies/202512_GenesysCX/ctm-token-calculator/docs/Appendix 4 - CCaaS Platform Benefit Calculations (Consolidated).pptx b/studies/202512_GenesysCX/ctm-token-calculator/docs/Appendix 4 - CCaaS Platform Benefit Calculations (Consolidated).pptx new file mode 100644 index 0000000..76ea5f8 Binary files /dev/null and b/studies/202512_GenesysCX/ctm-token-calculator/docs/Appendix 4 - CCaaS Platform Benefit Calculations (Consolidated).pptx differ diff --git a/studies/202512_GenesysCX/ctm-token-calculator/docs/Appendix 4 - CCaaS Platform Benefit Calculations (EMEA_Conservative).PDF b/studies/202512_GenesysCX/ctm-token-calculator/docs/Appendix 4 - CCaaS Platform Benefit Calculations (EMEA_Conservative).PDF new file mode 100644 index 0000000..eef0e49 Binary files /dev/null and b/studies/202512_GenesysCX/ctm-token-calculator/docs/Appendix 4 - CCaaS Platform Benefit Calculations (EMEA_Conservative).PDF differ diff --git a/studies/202512_GenesysCX/ctm-token-calculator/docs/Appendix 4 - CCaaS Platform Benefit Calculations (NA_Conservative).PDF b/studies/202512_GenesysCX/ctm-token-calculator/docs/Appendix 4 - CCaaS Platform Benefit Calculations (NA_Conservative).PDF new file mode 100644 index 0000000..458ca8b Binary files /dev/null and b/studies/202512_GenesysCX/ctm-token-calculator/docs/Appendix 4 - CCaaS Platform Benefit Calculations (NA_Conservative).PDF differ diff --git a/studies/202512_GenesysCX/ctm-token-calculator/docs/Genesys Solutions - CTM ROI - ASIA - Conservative - v.1.4 (1).PDF b/studies/202512_GenesysCX/ctm-token-calculator/docs/Genesys Solutions - CTM ROI - ASIA - Conservative - v.1.4 (1).PDF new file mode 100644 index 0000000..958ef08 Binary files /dev/null and b/studies/202512_GenesysCX/ctm-token-calculator/docs/Genesys Solutions - CTM ROI - ASIA - Conservative - v.1.4 (1).PDF differ diff --git a/studies/202512_GenesysCX/ctm-token-calculator/docs/Genesys Solutions - CTM ROI - AnZ - Conservative - v.1.4 (1).PDF b/studies/202512_GenesysCX/ctm-token-calculator/docs/Genesys Solutions - CTM ROI - AnZ - Conservative - v.1.4 (1).PDF new file mode 100644 index 0000000..4a5911d Binary files /dev/null and b/studies/202512_GenesysCX/ctm-token-calculator/docs/Genesys Solutions - CTM ROI - AnZ - Conservative - v.1.4 (1).PDF differ diff --git a/studies/202512_GenesysCX/ctm-token-calculator/docs/Genesys Solutions - CTM ROI - EMEA - Conservative - v.1.4 (1).PDF b/studies/202512_GenesysCX/ctm-token-calculator/docs/Genesys Solutions - CTM ROI - EMEA - Conservative - v.1.4 (1).PDF new file mode 100644 index 0000000..85fce65 Binary files /dev/null and b/studies/202512_GenesysCX/ctm-token-calculator/docs/Genesys Solutions - CTM ROI - EMEA - Conservative - v.1.4 (1).PDF differ diff --git a/studies/202512_GenesysCX/ctm-token-calculator/docs/Genesys Solutions - CTM ROI - NA - Conservative - v.1.4 (1).PDF b/studies/202512_GenesysCX/ctm-token-calculator/docs/Genesys Solutions - CTM ROI - NA - Conservative - v.1.4 (1).PDF new file mode 100644 index 0000000..3c674ac Binary files /dev/null and b/studies/202512_GenesysCX/ctm-token-calculator/docs/Genesys Solutions - CTM ROI - NA - Conservative - v.1.4 (1).PDF differ diff --git a/studies/202512_GenesysCX/ctm-token-calculator/docs/ctm_ai_labour_estimate.md b/studies/202512_GenesysCX/ctm-token-calculator/docs/ctm_ai_labour_estimate.md new file mode 100644 index 0000000..dc09cba --- /dev/null +++ b/studies/202512_GenesysCX/ctm-token-calculator/docs/ctm_ai_labour_estimate.md @@ -0,0 +1,198 @@ +The Genesys ROI documents claim 5 AI feature benefit categories: + +Agent Copilot (voice + digital handle time + ACW) +Email AI (Auto-Respond + Auto-Suggest) +Speech & Text Analytics +Supervisor Copilot (AI Translate, AI Summary, Admin) +Predictive Routing +None of these are turn-key. Each requires configuration, tuning, and enablement effort. The original case has zero implementation cost. + +Framework — four LoE dimensions per feature +Every AI feature carries four kinds of effort: + +Dimension What it is Scales with +Fixed setup One-time base configuration — instance creation, settings, rules, permissions, security Roughly constant per feature +Variable configuration Per-scope effort — per queue, per language, per intent, per wrap-up code, per KB article Multipliers × unit count +Iterative tuning Test → measure → adjust cycles. Non-negotiable for AI features. Typical: 3-6 cycles before production stability Complexity of the feature +Enablement & change Agent/supervisor training, adoption support, communications, super-user network Headcount + geography +Plus a steady-state annual line that everyone forgets: + +Steady-state What it is +Annual optimization Retraining, KB refresh, drift correction, model tuning as customer behavior shifts +Per-feature LoE — Genesys-claimed feature set +Below is my working LoE structure for the calculator. Hours are for a typical medium-complexity implementation. CTM-specific amplifiers follow in the next section. + +1. Agent Copilot +Per the Genesys documentation you shared, the setup dimensions are: create the Copilot instance, configure settings, configure NLU (intents), configure rules, configure queues, configure per-language variants, wrap-up code configuration, AI Studio for custom summaries, testing, permissions, KB integration for answer highlighting. + +Activity Unit Hours per unit Notes +Base Copilot instance setup Fixed 80-120 Per language variant (one instance per language) +Settings, rules, permissions config Fixed 40-60 +NLU / intent modeling Per 10 intents 30-50 Includes utterance generation, training, validation +Wrap-up code mapping Per 20 wrap-ups 8-12 Includes utterance training per code +Queue configuration Per queue 1-3 Critical CTM scaling factor +Custom summary templates (AI Studio) Per template 20-40 If custom summaries wanted +Knowledge base article preparation Per 100 articles 20-40 Only if KB used for answer highlighting — separate from KB creation +Testing / tuning cycles Per cycle 80-120 Plan for 4-6 cycles Y1 +Agent training Per 100 agents 8-12 Blended live/self-paced +Supervisor / admin enablement Per site 16-24 +Typical medium implementation (10 queues, 1 language, 100 intents, 100 wrap-ups, 500 agents, 4 tuning cycles, 500 KB articles): ~1,500 hours. + +2. Email AI (Auto-Suggest + Auto-Respond) +Activity Unit Hours per unit +Base Email AI setup Fixed 60-100 +Intent library for email Per 10 intents 40-60 (higher than voice — more text nuance) +Response template library (Auto-Suggest) Per 20 templates 30-50 +Auto-Respond flow design Per flow 60-100 (business rules, escalation logic, guardrails) +Integration to systems of record for response Per integration 80-200 +Testing / tuning cycles Per cycle 100-160 +Agent training on suggested/edit vs. auto Per 100 agents 6-10 +Typical medium implementation: ~1,200-1,800 hours. + +Critical note: Auto-Respond at any meaningful rate requires integration to case/order/account data — doesn't work without the ESB. Auto-Suggest is more forgiving. This shapes phasing. + +3. Speech & Text Analytics +Activity Unit Hours per unit +STA topic/program setup Fixed 80-120 +Program per language Per language 60-100 +Topic library — compliance Per 20 topics 20-30 +Topic library — CX / operational Per 20 topics 20-30 +Category / phrase library tuning Per cycle 60-100 (plan for 3-5 cycles) +Dashboard / report configuration Per dashboard 20-30 +Supervisor enablement Per site 8-16 +Typical medium implementation: ~600-1,000 hours. + +4. Supervisor Copilot +Activity Unit Hours per unit +Supervisor Copilot instance & settings Fixed 40-60 +AI Translate configuration per language pair Per pair 8-16 +AI Summary insight configuration Fixed 40-60 +Alerting rules & thresholds Per rule set 20-40 +Supervisor training Per 10 supervisors 8-16 +Typical medium implementation: ~300-500 hours. + +5. Predictive Routing +Activity Unit Hours per unit +PR model configuration Fixed 60-100 +Data source setup and validation Fixed 40-80 +Per-queue optimization Per queue 2-4 +Baseline measurement & A/B Per cycle 80-120 (plan 2-3 cycles) +Model retraining automation Fixed 20-40 +Typical medium implementation: ~500-800 hours. + +Cross-cutting activities (allocate across features) +These are the ones that get missed and blow budgets: + +Activity Unit Hours +KB curation & prep (source-of-truth for Copilot, Email AI, and STA) Per 100 articles 40-80 +KB governance setup (versioning, ownership, refresh cadence) Fixed 100-200 +AI governance framework (drift detection, model versioning, escalation paths) Fixed 120-200 +Data pipeline / integration to systems of record Per SoR 200-500 +Testing environment setup Fixed 80-160 +Program management overhead Per month program duration 40-80 +Regulatory / compliance review for AI features Per feature 20-60 +CTM-specific amplifiers +Now the ugly part. Every parameter above gets multiplied at CTM scale: + +Parameter Typical medium CTM +Tails 10-50 1,000+ (6-10× amplifier on queue-configuration line items) +Languages 1-3 7+ (English, French, Spanish, German, Mandarin, Cantonese, Japanese) +Sites 1-3 9 (change management overhead compounds) +Agent count 100-500 ~1,900 (training scales linearly) +Regions 1 4 (NAM, EMEA, AUZ, APAC) — program management overhead compounds +Systems-of-record integration 1-2 pre-built 0 today, ESB Nov 2026+ +KB maturity Unknown Unknown — flag as major risk +Amplifier math for Agent Copilot at CTM scale +Using the LoE table above at CTM parameters, mid-range hours: + +Activity CTM units Hours +Base Copilot instance × 7 languages 7 700 +Settings/rules/permissions 1 50 +NLU/intent modeling — 300 intents (large enterprise) 30 1,200 +Wrap-up codes — 500 codes 25 250 +Queue configuration — 1,000 queues at 2 hrs each 1,000 2,000 +Custom summary templates — 15 templates 15 450 +KB article preparation — 5,000 articles 50 1,500 +Testing/tuning — 6 cycles 6 600 +Agent training — 1,900 agents 19 190 +Supervisor enablement — 9 sites 9 180 +Agent Copilot subtotal ~7,100 hours +Full CTM AI implementation LoE +Feature Estimated hours +Agent Copilot 6,500 - 8,500 +Email AI (Auto-Suggest + Auto-Respond) 3,000 - 4,500 +Speech & Text Analytics 1,500 - 2,500 +Supervisor Copilot 600 - 900 +Predictive Routing 1,200 - 1,800 +Feature subtotal 12,800 - 18,200 +Cross-cutting (KB, governance, PM, integration) 4,000 - 7,000 +Total Y1 implementation LoE 16,800 - 25,200 hours +Translating to dollars +I don't know your PS rate, but for context using industry-standard blended rates: + +Blended rate Y1 implementation cost range +$175/hr (offshore-heavy blend) $2.9M - $4.4M +$225/hr (typical NTT DATA blended) $3.8 million - $5.7 million +$275/hr (onshore-heavy specialist) $4.6M - $6.9M +Plus annual steady-state at 15-20% of implementation = $430K - $1.4M/yr recurring for ongoing optimization, tuning, KB refresh, model retraining. + +What this does to the case +Adding implementation costs to the model: + +Component Y1 Y2 Y3 3-Year +Platform license $2.79M $2.79M $2.79M $8.37M +AI token costs (Realistic) $2.0M $3.5M $5.0M $10.5M +AI implementation LoE (new) $3.8 million-5.7 million $0.6M-1.1M $0.6M-1.1M $5.0M-7.9M +Legacy platform takeouts ($2.0M) ($2.0M) ($2.0M) ($6.0M) +Realistic AI benefits ($1.5M) ($4.5M) ($7.5M) ($13.5M) +NET +$5.1M to +$7.0M +$0.4M to +$0.9M -$1.1M to -$1.6M +$4.4M to +$6.3M +In the current model, program is net-negative $4-6M over 3 years even in Realistic scenario. Y1 is the ugly year because implementation cost front-loads. Y3 is when benefits catch up — barely. + +And that's using Genesys's own claimed benefits, unadjusted. If we apply the realistic haircuts we discussed earlier (Y1 benefit realization at 30-50%), the picture gets worse. + +Calculator amendment +Add to the spec: + +New dataclass in inputs.py +@dataclass +class ImplementationEffort: + feature: str + fixed_setup_hours: float + per_queue_hours: float + per_language_hours: float + per_100_intents_hours: float + per_20_wrapups_hours: float + per_100_kb_articles_hours: float + tuning_cycles: int + hours_per_tuning_cycle: float + per_100_agents_training_hours: float + per_site_enablement_hours: float + +@dataclass +class ImplementationCosts: + ps_blended_rate: float # $/hr — user-configurable + steady_state_pct_annual: float # default 0.175 (17.5%) + ramp_phasing: dict[str, float] # feature -> % of hours in Y1 vs Y2 +New module tokencalc/implementation.py +def calculate_feature_loe(effort: ImplementationEffort, + sites: list[SiteInput], + feature_scope: FeatureScope, + complexity_params: ComplexityParams + ) -> pd.DataFrame: + """Returns hours breakdown by activity for one feature.""" + +def calculate_implementation_cost(loe_by_feature: dict, + ps_rate: float, + phasing: dict + ) -> pd.DataFrame: + """Distributes hours across Y1/Y2/Y3 with steady-state tail.""" +New Streamlit page: "Implementation Effort" +LoE inputs per feature (adjustable, defaults from table above) +Complexity multipliers (queue count, language count, KB size — pulled from Inputs page) +PS rate slider +Steady-state % slider +Output table: hours × rate by feature, by year +Warning: "Implementation cost was NOT in the original Genesys business case." +Smell-test rule to add +If total AI implementation cost < 15% of AI benefit claim, flag as under-modeled. Industry benchmark: 20-40% of Y1 benefit claim goes to implementation. + diff --git a/studies/202512_GenesysCX/ctm-token-calculator/docs/ctm_ai_labour_estimate_V2.md b/studies/202512_GenesysCX/ctm-token-calculator/docs/ctm_ai_labour_estimate_V2.md new file mode 100644 index 0000000..8cb05d3 --- /dev/null +++ b/studies/202512_GenesysCX/ctm-token-calculator/docs/ctm_ai_labour_estimate_V2.md @@ -0,0 +1,64 @@ +Correction #1 — Email Auto-Suggest is part of Copilot +You're correct, and I got this wrong. Per Genesys documentation, Agent Copilot handles both voice and digital channels (email, chat, messaging) — including Auto-Suggest for email. Email Auto-Suggest is not a separate product; it's a Copilot capability on the email channel. + +Auto-Respond is the separate line — that's Email Bot / Digital Bot flow territory, distinct from Copilot's suggest-and-agent-approves model. + +That collapses my LoE table. Corrected structure: + +Feature (corrected) What it actually is +Agent Copilot Voice + digital channels (including email Auto-Suggest, chat suggest, messaging suggest) — one implementation +Email Auto-Respond Automated response without agent — separate flow, needs system-of-record integration +STA Standalone product +Supervisor Copilot Standalone product +Predictive Routing Standalone product +So I was double-counting Copilot email work. Email AI as I described it was inflated. + +Correction #2 — The hours were wrong. Let me show my work. +You called it correctly. 16,800-25,200 hours is 8-12 person-years of labour for five features on a mature platform. That's absurd on its face. Genesys Cloud is not custom software; these features are configuration, not development. Let me diagnose where I inflated: + +Where I over-estimated +Queue configuration at 2 hours × 1,000 queues = 2,000 hours for Copilot alone. Nonsense. Copilot doesn't require per-queue configuration in the way I implied. Enable Copilot on the account, configure once, apply to queues via policy. Real work: ~50-100 hours to define which queue types get Copilot, plus per-queue-type tuning, not per-individual-queue. + +NLU / intent modeling at 40 hours per 10 intents = 1,200 hours for 300 intents. Overstated. Copilot leverages Genesys's pre-built LLM understanding — you're not training an NLU from scratch. You're configuring wrap-up code mappings and validating auto-summary quality. Real work: ~200-400 hours for a large enterprise intent library, not 1,200. + +KB article prep at 40 hours per 100 articles = 1,500 hours for 5,000 articles. Double-counted. KB curation is either (a) already done in your KB platform and Copilot indexes it, or (b) a separate KB maturity project — not an AI implementation line item. Real work: ~200-400 hours for connector setup and indexing configuration, plus a separate flag that says "KB curation is a prerequisite project, not part of AI implementation." + +Testing/tuning at 100 hours × 6 cycles. Overstated. Test cycles for a hosted AI feature aren't full regression cycles — they're validation of prompt output quality and adjustment. Real: ~40-60 hours per cycle, 3-4 cycles typical. + +Training at 10 hours per 100 agents. Wildly high. This is a Copilot UI change — 30 minutes of orientation, some job aids, super-user support. Real: ~1-2 hours per 100 agents for train-the-trainer + material creation. + +Cross-cutting at 4,000-7,000 hours. Inflated by carrying forward the double-counted items above. + +Corrected LoE — Genesys AI features at CTM scale +Working from realistic effort, not vendor-services-inflation: + +Feature Realistic hours (CTM scale) +Agent Copilot (voice + digital, all languages) 1,200 - 1,800 +Email Auto-Respond (separate from Copilot; needs integration) 800 - 1,400 +STA (topics, programs, tuning for 7 languages) 800 - 1,200 +Supervisor Copilot 200 - 400 +Predictive Routing 400 - 700 +Feature subtotal 3,400 - 5,500 +Cross-cutting (governance, PM, testing environment, integration coordination) 1,000 - 1,800 +KB readiness project (separate line — prerequisite) 500 - 1,500 (flagged separately) +Total Y1 AI implementation 4,400 - 7,300 hours +Annual steady-state (Y2, Y3) 500 - 900 hours +That's 2-3.5 person-years of Y1 effort across 5 features. Still substantial — this is a real, multi-workstream program at CTM scale — but not the fantasy 12 person-years I had before. + +Corrected cost impact +Blended rate Y1 implementation Annual steady-state +$175/hr $770K - $1.28M $88K - $158K +$225/hr $990K - $1.64M $113K - $203K +$275/hr $1.21M - $2.01M $138K - $248K +Updated combined case +Using $225/hr blended rate and Realistic scenario: + +Component Y1 Y2 Y3 3-Year +Genesys CX 3 platform license $2.79M $2.79M $2.79M $8.37M +Base platform implementation $1.5M — — $1.5M +AI token costs $2.0M $3.5M $5.0M $10.5M +AI implementation (corrected) $1.3M $0.16M $0.16M $1.6M +Total future-state cost $7.6M $6.45M $7.95M $22.0M +Current-state takeout ($7.3M) ($7.3M) ($7.3M) ($21.9M) +AI benefits (realistic) ($1.5M) ($4.5M) ($7.5M) ($13.5M) +Program net -$1.2M +$5.35M +$6.85M +$11.0M diff --git a/studies/202512_GenesysCX/ctm-token-calculator/notebooks/ctm_business_case_corrected.ipynb b/studies/202512_GenesysCX/ctm-token-calculator/notebooks/ctm_business_case_corrected.ipynb new file mode 100644 index 0000000..7fd3fae --- /dev/null +++ b/studies/202512_GenesysCX/ctm-token-calculator/notebooks/ctm_business_case_corrected.ipynb @@ -0,0 +1,8717 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "aeb959ef", + "metadata": {}, + "source": [ + "# CTM × Genesys CCaaS — Corrected Business Case\n", + "\n", + "**Thesis:** the Genesys/Broadreach business case presents **$13.6M annual / $15.0M 3-year benefits**\n", + "against a cost case of **$4.3M/yr CCaaS licences + $2.4M professional services + $167K training** —\n", + "but every claimed benefit is produced by AI capabilities whose **token consumption**,\n", + "**implementation effort**, and **double-billing against the existing platform** are absent from that\n", + "cost case. This notebook keeps Genesys's benefits **verbatim** and corrects the cost side only.\n", + "\n", + "| Layer | Source | Treatment |\n", + "|---|---|---|\n", + "| Benefits | *Appendix 4 — CCaaS Platform Benefit Calculations (Consolidated).pptx*, slides 5–21 | **Verbatim** — phased by Genesys's own deployment schedule, scaled to the deck's 3-yr totals |\n", + "| Base costs | Same deck (TCO slides 5–6) | Verbatim, plus the Genesys ramp programme (12 months licence-free) |\n", + "| AI token consumption | `tokencalc` engine, published Genesys meter rates | **Added** — missing cost #1 |\n", + "| AI implementation effort | `docs/ctm_ai_labour_estimate_V2.md` | **Added** — missing cost #2 |\n", + "| Existing platform run-off | Term contracts to 31 Dec 2027, per-region | **Added** — missing cost #3 (double-billing) |\n", + "\n", + "Confidence legend: 🟢 confirmed (published/contractual) · 🟡 estimated (working assumption) · 🔴 unknown.\n", + "\n", + "*Scope note: AI implementation uses the V2 hours-range × blended-rate model; the activity-level\n", + "`ImplementationEffort` engine sketched in the labour doc is deferred (see §11). Timeline = 2026–2028,\n", + "contract start Jan 2026.*" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "83ba2b80", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:42.281815Z", + "iopub.status.busy": "2026-07-07T12:22:42.281637Z", + "iopub.status.idle": "2026-07-07T12:22:42.521532Z", + "shell.execute_reply": "2026-07-07T12:22:42.520768Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "tokencalc loaded — 9 sites, 2,088 named users (contracted: 2,088)\n" + ] + } + ], + "source": [ + "# ── Setup ──────────────────────────────────────────────────────────\n", + "import sys, pathlib\n", + "_ROOT = pathlib.Path.cwd()\n", + "if not (_ROOT / \"tokencalc\").exists(): # notebook lives in notebooks/\n", + " _ROOT = _ROOT.parent\n", + "sys.path.insert(0, str(_ROOT))\n", + "\n", + "import dataclasses\n", + "import datetime as dt\n", + "import math\n", + "\n", + "import pandas as pd\n", + "import plotly.graph_objects as go\n", + "\n", + "from tokencalc import *\n", + "\n", + "pd.options.display.float_format = \"{:,.0f}\".format\n", + "\n", + "YEARS = [2026, 2027, 2028] # model years 1..3, contract start Jan 2026\n", + "YEAR_INDEX = {2026: 1, 2027: 2, 2028: 3}\n", + "\n", + "# ── Chart chrome (dataviz reference palette, light surface) ─────────\n", + "INK, INK2, MUTED = \"#0b0b0b\", \"#52514e\", \"#898781\"\n", + "SURFACE, GRID, BASELINE = \"#fcfcfb\", \"#e1e0d9\", \"#c3c2b7\"\n", + "CUMULATIVE, CONTEXT = \"#52514e\", \"#c3c2b7\" # neutral line; de-emphasized context series\n", + "FONT_STACK = 'system-ui, -apple-system, \"Segoe UI\", sans-serif'\n", + "\n", + "# Fixed capability colors — color follows the entity across every figure.\n", + "CAP_COLOR = {\n", + " \"Agent Copilot\": \"#2a78d6\", # blue\n", + " \"WFM\": \"#1baf7a\", # aqua\n", + " \"Email\": \"#eda100\", # yellow (sub-3:1 → relief via total labels + tables)\n", + " \"STA\": \"#008300\", # green\n", + " \"Predictive Routing\": \"#4a3aa7\", # violet\n", + " \"Supervisor Copilot\": \"#e34948\", # red\n", + "}\n", + "COST_COLOR = {\n", + " \"CCaaS platform licences (ramp-adjusted)\": \"#2a78d6\",\n", + " \"Base professional services + training\": \"#1baf7a\",\n", + " \"Existing platform (term-contract run-off)\": \"#eda100\",\n", + " \"AI token consumption\": \"#008300\",\n", + " \"AI implementation + KB readiness\": \"#4a3aa7\",\n", + " \"AI steady-state tuning\": \"#e34948\",\n", + "}\n", + "SEQ_BLUES = [\"#cde2fb\", \"#b7d3f6\", \"#9ec5f4\", \"#86b6ef\", \"#6da7ec\", \"#5598e7\",\n", + " \"#3987e5\", \"#2a78d6\", \"#256abf\", \"#1c5cab\", \"#184f95\", \"#104281\", \"#0d366b\"]\n", + "\n", + "\n", + "def tei_layout(fig, title, subtitle=None, height=460):\n", + " t = f\"{title}\"\n", + " if subtitle:\n", + " t += f\"
{subtitle}\"\n", + " fig.update_layout(\n", + " title=dict(text=t, font=dict(size=16, color=INK), x=0.02, xanchor=\"left\"),\n", + " paper_bgcolor=SURFACE, plot_bgcolor=SURFACE,\n", + " font=dict(family=FONT_STACK, size=12, color=INK2),\n", + " legend=dict(orientation=\"h\", yanchor=\"top\", y=-0.10, x=0,\n", + " font=dict(size=11, color=INK2)),\n", + " xaxis=dict(type=\"category\", showgrid=False, linecolor=BASELINE,\n", + " tickfont=dict(color=MUTED)),\n", + " yaxis=dict(gridcolor=GRID, zerolinecolor=BASELINE, zerolinewidth=1.5,\n", + " tickformat=\"$~s\", tickfont=dict(color=MUTED)),\n", + " hovermode=\"x unified\", bargap=0.45, height=height,\n", + " margin=dict(t=70, r=30, b=80, l=70),\n", + " )\n", + " return fig\n", + "\n", + "\n", + "def bar(x, y, name, color):\n", + " return go.Bar(x=x, y=y, name=name,\n", + " marker=dict(color=color, line=dict(width=2, color=SURFACE)),\n", + " hovertemplate=\"%{fullData.name}: %{y:$,.0f}\")\n", + "\n", + "\n", + "def cum_line(x, y, name, color=CUMULATIVE, dash=None):\n", + " return go.Scatter(x=x, y=y, name=name, mode=\"lines+markers\",\n", + " line=dict(color=color, width=2, dash=dash),\n", + " marker=dict(size=8, line=dict(width=2, color=SURFACE)),\n", + " hovertemplate=\"%{fullData.name}: %{y:$,.0f}\")\n", + "\n", + "\n", + "def money(v):\n", + " sign, a = (\"-\" if v < 0 else \"\"), abs(v)\n", + " return f\"{sign}${a/1e6:,.1f}M\" if a >= 1e6 else f\"{sign}${a/1e3:,.0f}K\"\n", + "\n", + "\n", + "def html_money(v):\n", + " # Plotly text with two or more bare \"$\" triggers MathJax math mode —\n", + " # annotations holding several amounts must use the HTML entity instead.\n", + " return money(v).replace(\"$\", \"$\")\n", + "\n", + "\n", + "print(f\"tokencalc loaded — {len(CTM_DEFAULT_SITES)} sites, \"\n", + " f\"{sum(s.named_users for s in CTM_DEFAULT_SITES):,} named users \"\n", + " f\"(contracted: {CONTRACTED_NAMED_USERS:,})\")" + ] + }, + { + "cell_type": "markdown", + "id": "9139f3c0", + "metadata": {}, + "source": [ + "## §1 · Current state & contract inputs — data collection\n", + "\n", + "Verbatim anchors (Appendix 4, slide 5–6): current solution costs **$7.3M/yr globally**\n", + "(ANZ, APAC, EMEA, NA — $22M over 3 years); CCaaS is **$4.3M/yr** with **$2.4M professional\n", + "services + $167K training in year 1**.\n", + "\n", + "Three contract facts the deck's cost case ignores, collected here as editable inputs:\n", + "\n", + "1. **Per-region current cost** — seeded as $7.3M × (region agents ÷ total agents). 🟡 Allocation\n", + " estimate; replace with real regional contract values as they arrive.\n", + "2. **Contract termination dates** — the current platforms are **term contracts**; CTM pays until\n", + " termination (default **31 Dec 2027**) regardless of when Genesys goes live → double-billing.\n", + "3. **Genesys ramp programme** — `RAMP_MONTHS = 12`: licences are free for the first 12 months,\n", + " so the $4.3M/yr commit bills from month 13." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "7d0c26bd", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:42.523771Z", + "iopub.status.busy": "2026-07-07T12:22:42.523541Z", + "iopub.status.idle": "2026-07-07T12:22:42.546359Z", + "shell.execute_reply": "2026-07-07T12:22:42.545634Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "current 3-yr = $21.9M (deck: $22.0M) — deck rounding\n", + "deck CCaaS 3-yr (no ramp) = $15.5M (deck: $15.4M)\n", + "ramp-adjusted licences by year: {2026: '$0K', 2027: '$4.3M', 2028: '$4.3M'}\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
agentsshareannual_costcontract_terminationconfidence
region
NA89003,348,9692027-12-31🟡 agent-share allocation of the verbatim $7.3M
ANZ1800677,3202027-12-31🟡 agent-share allocation of the verbatim $7.3M
EMEA32001,204,1242027-12-31🟡 agent-share allocation of the verbatim $7.3M
ASIA55002,069,5882027-12-31🟡 agent-share allocation of the verbatim $7.3M
\n", + "
" + ], + "text/plain": [ + " agents share annual_cost contract_termination \\\n", + "region \n", + "NA 890 0 3,348,969 2027-12-31 \n", + "ANZ 180 0 677,320 2027-12-31 \n", + "EMEA 320 0 1,204,124 2027-12-31 \n", + "ASIA 550 0 2,069,588 2027-12-31 \n", + "\n", + " confidence \n", + "region \n", + "NA 🟡 agent-share allocation of the verbatim $7.3M \n", + "ANZ 🟡 agent-share allocation of the verbatim $7.3M \n", + "EMEA 🟡 agent-share allocation of the verbatim $7.3M \n", + "ASIA 🟡 agent-share allocation of the verbatim $7.3M " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
data itemstatus
0Per-region current-platform contract values🟡 seeded by agent share above
1Actual termination / renewal dates per region🟡 defaulted to 31 Dec 2027
2Genesys ramp period in the actual order form🟡 12 months assumed
3Contracted token rate (vs $1.00 US list)🔴 not sourced — list rate used
4Non-NAM site volumes & AHTs🟡 tokencalc placeholders (defaults.py warning)
5Early-termination / co-term options on NICE IE...🔴 unknown
\n", + "
" + ], + "text/plain": [ + " data item \\\n", + "0 Per-region current-platform contract values \n", + "1 Actual termination / renewal dates per region \n", + "2 Genesys ramp period in the actual order form \n", + "3 Contracted token rate (vs $1.00 US list) \n", + "4 Non-NAM site volumes & AHTs \n", + "5 Early-termination / co-term options on NICE IE... \n", + "\n", + " status \n", + "0 🟡 seeded by agent share above \n", + "1 🟡 defaulted to 31 Dec 2027 \n", + "2 🟡 12 months assumed \n", + "3 🔴 not sourced — list rate used \n", + "4 🟡 tokencalc placeholders (defaults.py warning) \n", + "5 🔴 unknown " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── Verbatim TCO anchors (Appendix 4, slides 5-6) ────────────────────\n", + "TCO_VERBATIM = {\n", + " \"current_annual\": 7_300_000, # current global spend / yr\n", + " \"current_3yr\": 22_000_000,\n", + " \"ccaas_annual\": 4_300_000, # licence run-rate / yr\n", + " \"ccaas_3yr\": 15_400_000, # deck's 3-yr CCaaS investment (no ramp, no AI costs)\n", + " \"prof_services_y1\": 2_400_000,\n", + " \"training_y1\": 167_000,\n", + " \"npv_discount_rate\": 0.135, # deck's benefit-NPV rate\n", + "}\n", + "RAMP_MONTHS = 12 # Genesys ramp programme: licence-free months from contract start\n", + "DISCOUNT_RATE = TCO_VERBATIM[\"npv_discount_rate\"] # 0.08 = CTM treasury alternative\n", + "\n", + "# ── Region ⇄ site mapping (tokencalc sites → deck regions) ───────────\n", + "SITE_REGION = {\"NAM\": \"NA\", \"AUZ\": \"ANZ\", \"EMEA\": \"EMEA\"} # APAC * → ASIA\n", + "REGIONS = [\"NA\", \"ANZ\", \"EMEA\", \"ASIA\"]\n", + "sites = list(CTM_DEFAULT_SITES)\n", + "ALL_SITES = [s.site_name for s in sites]\n", + "ASIA_SITES = [n for n in ALL_SITES if n.startswith(\"APAC\")]\n", + "REGION_SITES = {r: [n for n in ALL_SITES if SITE_REGION.get(n, \"ASIA\") == r] for r in REGIONS}\n", + "agents_by_region = {\n", + " r: sum(s.agents for s in sites if s.site_name in REGION_SITES[r]) for r in REGIONS\n", + "}\n", + "TOTAL_AGENTS = sum(agents_by_region.values())\n", + "\n", + "# ── Per-region current-state inputs (EDIT HERE as real data arrives) ─\n", + "current_state = pd.DataFrame([\n", + " {\n", + " \"region\": r,\n", + " \"agents\": agents_by_region[r],\n", + " \"share\": agents_by_region[r] / TOTAL_AGENTS,\n", + " \"annual_cost\": TCO_VERBATIM[\"current_annual\"] * agents_by_region[r] / TOTAL_AGENTS,\n", + " \"contract_termination\": dt.date(2027, 12, 31),\n", + " \"confidence\": \"🟡 agent-share allocation of the verbatim $7.3M\",\n", + " }\n", + " for r in REGIONS\n", + "]).set_index(\"region\")\n", + "\n", + "\n", + "def current_months_in_year(termination: dt.date, cal_year: int) -> int:\n", + " # Term contract bills through its termination month, then stops.\n", + " if cal_year < termination.year:\n", + " return 12\n", + " if cal_year > termination.year:\n", + " return 0\n", + " return termination.month\n", + "\n", + "\n", + "current_by_year = {\n", + " y: float(sum(row[\"annual_cost\"] * current_months_in_year(row[\"contract_termination\"], y) / 12\n", + " for _, row in current_state.iterrows()))\n", + " for y in YEARS\n", + "}\n", + "\n", + "\n", + "def licence_months_in_year(year_index: int, ramp_months: int) -> int:\n", + " # Ramp programme: billing starts in calendar month ramp_months + 1.\n", + " start, end = 12 * (year_index - 1) + 1, 12 * year_index\n", + " return max(0, end - max(start, ramp_months + 1) + 1)\n", + "\n", + "\n", + "licence_by_year = {\n", + " y: TCO_VERBATIM[\"ccaas_annual\"] * licence_months_in_year(YEAR_INDEX[y], RAMP_MONTHS) / 12\n", + " for y in YEARS\n", + "}\n", + "\n", + "assert abs(current_state[\"annual_cost\"].sum() - TCO_VERBATIM[\"current_annual\"]) < 1\n", + "print(f\"current 3-yr = {money(3 * TCO_VERBATIM['current_annual'])} \"\n", + " f\"(deck: {money(TCO_VERBATIM['current_3yr'])}) — deck rounding\")\n", + "print(f\"deck CCaaS 3-yr (no ramp) = \"\n", + " f\"{money(3 * TCO_VERBATIM['ccaas_annual'] + TCO_VERBATIM['prof_services_y1'] + TCO_VERBATIM['training_y1'])} \"\n", + " f\"(deck: {money(TCO_VERBATIM['ccaas_3yr'])})\")\n", + "print(f\"ramp-adjusted licences by year: \"\n", + " f\"{ {y: money(v) for y, v in licence_by_year.items()} }\")\n", + "display(current_state)\n", + "\n", + "# ── Remaining data-collection checklist ──────────────────────────────\n", + "display(pd.DataFrame([\n", + " (\"Per-region current-platform contract values\", \"🟡 seeded by agent share above\"),\n", + " (\"Actual termination / renewal dates per region\", \"🟡 defaulted to 31 Dec 2027\"),\n", + " (\"Genesys ramp period in the actual order form\", \"🟡 12 months assumed\"),\n", + " (\"Contracted token rate (vs $1.00 US list)\", \"🔴 not sourced — list rate used\"),\n", + " (\"Non-NAM site volumes & AHTs\", \"🟡 tokencalc placeholders (defaults.py warning)\"),\n", + " (\"Early-termination / co-term options on NICE IEX et al.\", \"🔴 unknown\"),\n", + "], columns=[\"data item\", \"status\"]))" + ] + }, + { + "cell_type": "markdown", + "id": "8af2a53b", + "metadata": {}, + "source": [ + "## §2 · Verbatim Genesys benefits & deployment schedule\n", + "\n", + "Benefits are taken **verbatim** from Appendix 4 slides 12–15 (per-region × capability, annual and\n", + "3-yr values). The deck gives no per-year split, so each cell's 3-yr value is **phased by Genesys's\n", + "own deployment schedule** (slides 17–21): implementation months NA=18, ANZ=21, EMEA=24, ASIA=27,\n", + "and *\"business benefits start 3 months after implementation\"* — realization Sep 2027 / Dec 2027 /\n", + "Mar 2028 / Jun 2028. Exception: the NA Gantt shows **Email realizing Apr 2027** (implemented month 13).\n", + "\n", + "**Month convention:** `RolloutPlan.go_live_month = m` means active from calendar month *m+1*; the\n", + "deck's labels are inclusive (NA \"realizes Sep 2027\" ⇒ September counts), so rollout keys are set to\n", + "*label − 1*. Deck-implied realization windows differ from its own schedule by ±1 month in places —\n", + "the per-cell scaling below absorbs that; don't \"fix\" it.\n", + "\n", + "The result: **benefits in 2026 are $0 — by Genesys's own schedule.**" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "da68f997", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:42.547907Z", + "iopub.status.busy": "2026-07-07T12:22:42.547785Z", + "iopub.status.idle": "2026-07-07T12:22:42.577658Z", + "shell.execute_reply": "2026-07-07T12:22:42.576992Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "cross-foot OK — cell grain sums to $15.0M 3-yr / $13.7M annual (deck headline: $15.0M / $13.6M — deck rounding)\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
regionANZASIAEMEANATOTAL
capability
Agent Copilot390000006400034000007364000
WFM140000091400068700003001000
Email1430009300023500025000002971000
STA10500072000131000506000814000
Predictive Routing302000510006000167000526000
Supervisor Copilot27000049000291000367000
TOTAL587700011300001172000686400015043000
\n", + "
" + ], + "text/plain": [ + "region ANZ ASIA EMEA NA TOTAL\n", + "capability \n", + "Agent Copilot 3900000 0 64000 3400000 7364000\n", + "WFM 1400000 914000 687000 0 3001000\n", + "Email 143000 93000 235000 2500000 2971000\n", + "STA 105000 72000 131000 506000 814000\n", + "Predictive Routing 302000 51000 6000 167000 526000\n", + "Supervisor Copilot 27000 0 49000 291000 367000\n", + "TOTAL 5877000 1130000 1172000 6864000 15043000" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── VERBATIM Appendix 4 slides 12-15: (annual, 3-yr) per region × capability ──\n", + "CAPABILITIES = [\"Agent Copilot\", \"WFM\", \"Email\", \"STA\", \"Predictive Routing\", \"Supervisor Copilot\"]\n", + "VERBATIM_BENEFITS = { # (annual_value, three_yr_value) — verbatim, do not edit\n", + " (\"NA\", \"Agent Copilot\"): (2_400_000, 3_400_000),\n", + " (\"NA\", \"Email\"): (1_900_000, 2_500_000),\n", + " (\"NA\", \"STA\"): (294_000, 506_000),\n", + " (\"NA\", \"Supervisor Copilot\"): (218_000, 291_000),\n", + " (\"NA\", \"Predictive Routing\"): (97_000, 167_000),\n", + " (\"NA\", \"WFM\"): (0, 0), # NA excluded — has similar feature\n", + " (\"ANZ\", \"Agent Copilot\"): (3_600_000, 3_900_000),\n", + " (\"ANZ\", \"WFM\"): (1_300_000, 1_400_000),\n", + " (\"ANZ\", \"Predictive Routing\"): (279_000, 302_000),\n", + " (\"ANZ\", \"Email\"): (132_000, 143_000),\n", + " (\"ANZ\", \"STA\"): (97_000, 105_000),\n", + " (\"ANZ\", \"Supervisor Copilot\"): (25_000, 27_000),\n", + " (\"ASIA\", \"WFM\"): (1_600_000, 914_000),\n", + " (\"ASIA\", \"Email\"): (160_000, 93_000),\n", + " (\"ASIA\", \"STA\"): (124_000, 72_000),\n", + " (\"ASIA\", \"Predictive Routing\"): (87_000, 51_000),\n", + " (\"ASIA\", \"Agent Copilot\"): (0, 0),\n", + " (\"ASIA\", \"Supervisor Copilot\"): (0, 0),\n", + " (\"EMEA\", \"WFM\"): (824_000, 687_000),\n", + " (\"EMEA\", \"Email\"): (282_000, 235_000),\n", + " (\"EMEA\", \"STA\"): (157_000, 131_000),\n", + " (\"EMEA\", \"Agent Copilot\"): (77_000, 64_000),\n", + " (\"EMEA\", \"Supervisor Copilot\"): (59_000, 49_000),\n", + " (\"EMEA\", \"Predictive Routing\"): (7_000, 6_000),\n", + "}\n", + "SLIDE_TOTALS = { # deck's own (rounded) summary rows — slides 8-9\n", + " \"regional_3yr\": {\"NA\": 6_900_000, \"ANZ\": 5_900_000, \"ASIA\": 1_100_000, \"EMEA\": 1_200_000},\n", + " \"capability_3yr\": {\"Agent Copilot\": 7_400_000, \"WFM\": 3_000_000, \"Email\": 2_900_000,\n", + " \"STA\": 814_000, \"Predictive Routing\": 526_000,\n", + " \"Supervisor Copilot\": 367_000},\n", + " \"total_3yr\": 15_000_000,\n", + " \"total_annual\": 13_600_000,\n", + "}\n", + "\n", + "verbatim = pd.DataFrame(\n", + " [{\"region\": r, \"capability\": c, \"annual\": a, \"three_yr\": t}\n", + " for (r, c), (a, t) in VERBATIM_BENEFITS.items()]\n", + ")\n", + "\n", + "\n", + "def _tol(v): # deck rounds to $0.1M — its own tables cross-foot ±$50-120K\n", + " return max(100_000, 0.015 * v)\n", + "\n", + "\n", + "for r, expect in SLIDE_TOTALS[\"regional_3yr\"].items():\n", + " got = verbatim.loc[verbatim.region == r, \"three_yr\"].sum()\n", + " assert abs(got - expect) <= _tol(expect), (r, got, expect)\n", + "for c, expect in SLIDE_TOTALS[\"capability_3yr\"].items():\n", + " got = verbatim.loc[verbatim.capability == c, \"three_yr\"].sum()\n", + " assert abs(got - expect) <= _tol(expect), (c, got, expect)\n", + "assert abs(verbatim[\"three_yr\"].sum() - SLIDE_TOTALS[\"total_3yr\"]) <= _tol(15e6)\n", + "assert abs(verbatim[\"annual\"].sum() - SLIDE_TOTALS[\"total_annual\"]) <= _tol(13.6e6)\n", + "print(f\"cross-foot OK — cell grain sums to {money(verbatim['three_yr'].sum())} 3-yr / \"\n", + " f\"{money(verbatim['annual'].sum())} annual (deck headline: $15.0M / $13.6M — deck rounding)\")\n", + "\n", + "display(verbatim.pivot_table(index=\"capability\", columns=\"region\", values=\"three_yr\",\n", + " aggfunc=\"sum\", margins=True, margins_name=\"TOTAL\")\n", + " .reindex([*CAPABILITIES, \"TOTAL\"]))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "67842fd4", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:42.579539Z", + "iopub.status.busy": "2026-07-07T12:22:42.579402Z", + "iopub.status.idle": "2026-07-07T12:22:42.590942Z", + "shell.execute_reply": "2026-07-07T12:22:42.590120Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "NA Email exception: implemented Jan 2027, realizes Apr 2027\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
implementedbenefits realizetoken months 2026token months 2027token months 2028benefit months 2026benefit months 2027benefit months 2028
region
NAJun 2027 (m18)Sep 2027 (m21)07120412
ANZSep 2027 (m21)Dec 2027 (m24)04120112
EMEADec 2027 (m24)Mar 2028 (m27)01120010
ASIAMar 2028 (m27)Jun 2028 (m30)0010007
\n", + "
" + ], + "text/plain": [ + " implemented benefits realize token months 2026 token months 2027 \\\n", + "region \n", + "NA Jun 2027 (m18) Sep 2027 (m21) 0 7 \n", + "ANZ Sep 2027 (m21) Dec 2027 (m24) 0 4 \n", + "EMEA Dec 2027 (m24) Mar 2028 (m27) 0 1 \n", + "ASIA Mar 2028 (m27) Jun 2028 (m30) 0 0 \n", + "\n", + " token months 2028 benefit months 2026 benefit months 2027 \\\n", + "region \n", + "NA 12 0 4 \n", + "ANZ 12 0 1 \n", + "EMEA 12 0 0 \n", + "ASIA 10 0 0 \n", + "\n", + " benefit months 2028 \n", + "region \n", + "NA 12 \n", + "ANZ 12 \n", + "EMEA 10 \n", + "ASIA 7 " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── Genesys/Broadreach deployment schedule (slides 17-21) ────────────\n", + "IMPL_MONTH = {\"NA\": 18, \"ANZ\": 21, \"EMEA\": 24, \"ASIA\": 27} # months from Jan 2026, inclusive\n", + "BENEFIT_LAG_MONTHS = 3\n", + "REALIZE_MONTH = {r: m + BENEFIT_LAG_MONTHS for r, m in IMPL_MONTH.items()}\n", + "NA_EMAIL_EARLY = True # NA Gantt exception: Email realizes Apr 2027\n", + "NA_EMAIL_IMPL_MONTH = 13 # ⇒ implemented Jan 2027, realizes month 16\n", + "\n", + "_MONTHS = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\",\n", + " \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\n", + "\n", + "\n", + "def month_label(m): # m is 1-indexed from Jan 2026\n", + " return f\"{_MONTHS[(m - 1) % 12]} {2026 + (m - 1) // 12}\"\n", + "\n", + "\n", + "# go_live_month = label − 1 so the labelled month is included (see §2 note).\n", + "TOKEN_ROLLOUT = RolloutPlan(\n", + " contract_start=\"2026-01\", build_months=27, ramp_months=RAMP_MONTHS,\n", + " first_year_platform_discount=0.0, # licences handled verbatim in §1, not by this plan\n", + " go_live_month={\"NAM\": IMPL_MONTH[\"NA\"] - 1, \"AUZ\": IMPL_MONTH[\"ANZ\"] - 1,\n", + " \"EMEA\": IMPL_MONTH[\"EMEA\"] - 1,\n", + " **{n: IMPL_MONTH[\"ASIA\"] - 1 for n in ASIA_SITES}},\n", + ")\n", + "EMAIL_TOKEN_ROLLOUT = dataclasses.replace( # Auto-Respond only: NA implements early\n", + " TOKEN_ROLLOUT,\n", + " go_live_month={**TOKEN_ROLLOUT.go_live_month,\n", + " \"NAM\": (NA_EMAIL_IMPL_MONTH - 1) if NA_EMAIL_EARLY else IMPL_MONTH[\"NA\"] - 1},\n", + ")\n", + "BENEFIT_ROLLOUT = RolloutPlan( # region-keyed; NA_EMAIL covers the (NA, Email) cell only\n", + " first_year_platform_discount=0.0,\n", + " go_live_month={**{r: REALIZE_MONTH[r] - 1 for r in REGIONS},\n", + " \"NA_EMAIL\": (NA_EMAIL_IMPL_MONTH + BENEFIT_LAG_MONTHS - 1)\n", + " if NA_EMAIL_EARLY else REALIZE_MONTH[\"NA\"] - 1},\n", + ")\n", + "\n", + "schedule = pd.DataFrame([\n", + " {\n", + " \"region\": r,\n", + " \"implemented\": f\"{month_label(IMPL_MONTH[r])} (m{IMPL_MONTH[r]})\",\n", + " \"benefits realize\": f\"{month_label(REALIZE_MONTH[r])} (m{REALIZE_MONTH[r]})\",\n", + " **{f\"token months {y}\": TOKEN_ROLLOUT.live_months_in_year(REGION_SITES[r][0], YEAR_INDEX[y])\n", + " for y in YEARS},\n", + " **{f\"benefit months {y}\": BENEFIT_ROLLOUT.live_months_in_year(r, YEAR_INDEX[y])\n", + " for y in YEARS},\n", + " }\n", + " for r in REGIONS\n", + "]).set_index(\"region\")\n", + "if NA_EMAIL_EARLY:\n", + " print(f\"NA Email exception: implemented {month_label(NA_EMAIL_IMPL_MONTH)}, \"\n", + " f\"realizes {month_label(NA_EMAIL_IMPL_MONTH + BENEFIT_LAG_MONTHS)}\")\n", + "display(schedule)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "618205b4", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:42.593342Z", + "iopub.status.busy": "2026-07-07T12:22:42.593199Z", + "iopub.status.idle": "2026-07-07T12:22:42.650436Z", + "shell.execute_reply": "2026-07-07T12:22:42.649806Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "benefits by year: {2026: '$0K', 2027: '$2.6M', 2028: '$12.4M'}\n", + "⚠ 2026 benefits are $0 — the honest consequence of the deck's own deployment schedule.\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
year202620272028TOTAL
capability
Agent Copilot01,150,0006,214,0007,364,000
WFM0107,6922,893,3083,001,000
Email01,082,4291,888,5712,971,000
STA0134,577679,423814,000
Predictive Routing064,981461,019526,000
Supervisor Copilot074,827292,173367,000
TOTAL02,614,50512,428,49515,043,000
\n", + "
" + ], + "text/plain": [ + "year 2026 2027 2028 TOTAL\n", + "capability \n", + "Agent Copilot 0 1,150,000 6,214,000 7,364,000\n", + "WFM 0 107,692 2,893,308 3,001,000\n", + "Email 0 1,082,429 1,888,571 2,971,000\n", + "STA 0 134,577 679,423 814,000\n", + "Predictive Routing 0 64,981 461,019 526,000\n", + "Supervisor Copilot 0 74,827 292,173 367,000\n", + "TOTAL 0 2,614,505 12,428,495 15,043,000" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── Phase each verbatim 3-yr value by its region's realization window ─\n", + "rows = []\n", + "for (region, cap), (annual, three_yr) in VERBATIM_BENEFITS.items():\n", + " key = \"NA_EMAIL\" if (region == \"NA\" and cap == \"Email\" and NA_EMAIL_EARLY) else region\n", + " live = [BENEFIT_ROLLOUT.live_months_in_year(key, YEAR_INDEX[y]) for y in YEARS]\n", + " total_live = sum(live)\n", + " for y, m in zip(YEARS, live):\n", + " rows.append({\"region\": region, \"capability\": cap, \"year\": y,\n", + " \"benefit\": three_yr * m / total_live if total_live else 0.0})\n", + "benefits_long = pd.DataFrame(rows)\n", + "benefit_total_by_year = benefits_long.groupby(\"year\")[\"benefit\"].sum().to_dict()\n", + "\n", + "# Scaling is at the finest grain, so every verbatim total is reproduced exactly.\n", + "assert benefit_total_by_year[2026] == 0.0, \"2026 must be $0 under Genesys's own schedule\"\n", + "for (region, cap), (_, three_yr) in VERBATIM_BENEFITS.items():\n", + " got = benefits_long.query(\"region == @region and capability == @cap\")[\"benefit\"].sum()\n", + " assert abs(got - three_yr) < 1e-6, (region, cap)\n", + "\n", + "print(f\"benefits by year: { {y: money(v) for y, v in benefit_total_by_year.items()} }\")\n", + "print(\"⚠ 2026 benefits are $0 — the honest consequence of the deck's own deployment schedule.\")\n", + "display(benefits_long.pivot_table(index=\"capability\", columns=\"year\", values=\"benefit\",\n", + " aggfunc=\"sum\", margins=True, margins_name=\"TOTAL\")\n", + " .reindex([*CAPABILITIES, \"TOTAL\"]))" + ] + }, + { + "cell_type": "markdown", + "id": "f47fdc0f", + "metadata": {}, + "source": [ + "## §3 · Feature enablement → token consumption (missing cost #1)\n", + "\n", + "Every deck capability maps to Genesys AI Experience token meters (all 🟢 published rates unless\n", + "noted), consumed from each region's **implementation month** — three months *before* benefits:\n", + "\n", + "| Deck capability | Token meter(s) | Treatment |\n", + "|---|---|---|\n", + "| Agent Copilot | Agent Copilot **[named]** — 40 tokens/user/mo | Per V2 correction #1, this **includes email/chat Auto-Suggest** and interaction summarization. Scoped to NA/ANZ/EMEA by default (ASIA claims $0 Copilot benefit — toggle below). |\n", + "| Email | Email AI (**Auto-Respond**) — per message | 🔴→🟡 rate not published; working assumption below (anchored to Genesys Cloud Copilot's 20 AI actions/token), deck's 25.5% auto-respond rate. Sensitivity in §9. |\n", + "| STA | Speech & Text Analytics **[named]** — 30 tokens/user/mo | All sites. |\n", + "| Predictive Routing | Predictive Routing — 17 routed interactions/token | All sites; `PR_ELIGIBILITY` scopes to PR-enabled queue share. |\n", + "| Supervisor Copilot | AI Summary & Insights (**$0 by Rule 1** — Copilot's rate already covers summarization) + AI Translate as small-volume proxy | Kept visible to show the coverage rule. |\n", + "| WFM | **No token meter** — WEM is licence-included | $0 row; impl effort assumed inside the base $2.4M PS (no V2 line). |\n", + "\n", + "Consumption uses a **claim-level scenario** (no maturity haircut — Genesys claims none) and bills\n", + "with Genesys's monthly `ceil` token rounding, per-region pricing at $1.00/token US list (🔴 contracted\n", + "rates not sourced)." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "99e510d0", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:42.652747Z", + "iopub.status.busy": "2026-07-07T12:22:42.652584Z", + "iopub.status.idle": "2026-07-07T12:22:42.656004Z", + "shell.execute_reply": "2026-07-07T12:22:42.655407Z" + } + }, + "outputs": [], + "source": [ + "# ── CONFIG — token model knobs ───────────────────────────────────────\n", + "COPILOT_INCLUDES_ASIA = False # deck claims $0 Copilot benefit in ASIA → excluded by default\n", + "EMAIL_AUTO_RESPOND_RATE = 0.255 # deck: \"Reduced Email Interactions with Auto-Respond 25.5%\"\n", + "EMAIL_AUTORESPOND_TOKENS_PER_MSG = 0.05 # 🟡 ≈ one AI action per generated response (20/token)\n", + "PR_ELIGIBILITY = 1.0 # share of voice volume on PR-enabled queues\n", + "AI_TRANSLATE_ELIGIBILITY = 0.01 # 🟡 supervisor-evaluation slice of interactions\n", + "USE_CONTRACTED_RATES = False # no contracted token rate sourced yet" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "3ea04102", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:42.657915Z", + "iopub.status.busy": "2026-07-07T12:22:42.657753Z", + "iopub.status.idle": "2026-07-07T12:22:42.671127Z", + "shell.execute_reply": "2026-07-07T12:22:42.670305Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
featuremeter_typeunits_per_tokentokens_per_unitconfidencenotes
0Agent Copilot [named]per_user_per_monthNaN40🟢 confirmed40 tokens per named user per month. Includes i...
1Speech & Text Analytics [named]per_user_per_monthNaN30🟢 confirmedSTA named licence; 30 tokens per named user pe...
2Predictive Routingper_interaction170🟢 confirmedPredictive routing; 17 routes per token.
3AI Summary & Insightsper_summary500🟢 confirmedSupervisor standalone summarization; 50 summar...
4AI Translateper_interaction20🟢 confirmedAI translation; 2 translations per token.
5Email AI (Auto-Respond)per_message200🟡 estimatedWORKING ASSUMPTION — rate unpublished; ≈1 AI a...
\n", + "
" + ], + "text/plain": [ + " feature meter_type units_per_token \\\n", + "0 Agent Copilot [named] per_user_per_month NaN \n", + "1 Speech & Text Analytics [named] per_user_per_month NaN \n", + "2 Predictive Routing per_interaction 17 \n", + "3 AI Summary & Insights per_summary 50 \n", + "4 AI Translate per_interaction 2 \n", + "5 Email AI (Auto-Respond) per_message 20 \n", + "\n", + " tokens_per_unit confidence \\\n", + "0 40 🟢 confirmed \n", + "1 30 🟢 confirmed \n", + "2 0 🟢 confirmed \n", + "3 0 🟢 confirmed \n", + "4 0 🟢 confirmed \n", + "5 0 🟡 estimated \n", + "\n", + " notes \n", + "0 40 tokens per named user per month. Includes i... \n", + "1 STA named licence; 30 tokens per named user pe... \n", + "2 Predictive routing; 17 routes per token. \n", + "3 Supervisor standalone summarization; 50 summar... \n", + "4 AI translation; 2 translations per token. \n", + "5 WORKING ASSUMPTION — rate unpublished; ≈1 AI a... " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── Claim-level scenario: deck parameters, no consumption ramp ───────\n", + "GENESYS_CLAIM = Scenario(\n", + " name=\"genesys-claim\",\n", + " voice_bot_deflection=0.0, voice_bot_avg_minutes=0.0, agentic_va_deflection=0.0,\n", + " voice_summarization_eligibility=0.0, voice_knowledge_eligibility=0.0, # unused by this scope set\n", + " email_auto_respond_rate=EMAIL_AUTO_RESPOND_RATE,\n", + " email_auto_suggest_acceptance=0.0, # Auto-Suggest is inside Copilot's per-user rate (V2 #1)\n", + " consumption_cost_realization={1: 1.0, 2: 1.0, 3: 1.0}, # Genesys claims no maturity ramp\n", + ")\n", + "\n", + "AUTORESPOND_METER = dataclasses.replace(\n", + " DEFAULT_METERS[\"Email AI (Auto-Respond)\"],\n", + " units_per_token=1.0 / EMAIL_AUTORESPOND_TOKENS_PER_MSG,\n", + " tokens_per_unit=EMAIL_AUTORESPOND_TOKENS_PER_MSG,\n", + " confidence=Confidence.ESTIMATED,\n", + " notes=\"WORKING ASSUMPTION — rate unpublished; ≈1 AI action per generated response \"\n", + " \"(Genesys Cloud Copilot meters 20 AI actions/token). Sensitivity in §9.\",\n", + ")\n", + "METERS = {**DEFAULT_METERS, \"Email AI (Auto-Respond)\": AUTORESPOND_METER}\n", + "\n", + "COPILOT_SITES = [\"NAM\", \"AUZ\", \"EMEA\"] + (ASIA_SITES if COPILOT_INCLUDES_ASIA else [])\n", + "# NOTE: no adoption_curve on any scope — a curve would silently override the\n", + "# claim scenario's flat consumption realization in calculate_consumption_ai_cost.\n", + "CORE_SCOPES = [\n", + " FeatureScope(\"Agent Copilot [named]\", COPILOT_SITES, phase=1),\n", + " FeatureScope(\"Speech & Text Analytics [named]\", ALL_SITES, phase=1),\n", + " FeatureScope(\"Predictive Routing\", ALL_SITES, phase=1, eligibility_pct=PR_ELIGIBILITY),\n", + " FeatureScope(\"AI Summary & Insights\", COPILOT_SITES, phase=1), # $0 by Rule 1 — kept visible\n", + " FeatureScope(\"AI Translate\", ASIA_SITES + [\"EMEA\"], phase=1,\n", + " eligibility_pct=AI_TRANSLATE_ELIGIBILITY),\n", + "]\n", + "EMAIL_SCOPES = [FeatureScope(\"Email AI (Auto-Respond)\", ALL_SITES, phase=1)]\n", + "\n", + "USED_FEATURES = [sc.feature for sc in CORE_SCOPES + EMAIL_SCOPES]\n", + "display(meters_dataframe({f: METERS[f] for f in USED_FEATURES})\n", + " .drop(columns=[\"source\"]))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "7727fbb0", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:42.672905Z", + "iopub.status.busy": "2026-07-07T12:22:42.672732Z", + "iopub.status.idle": "2026-07-07T12:22:42.717246Z", + "shell.execute_reply": "2026-07-07T12:22:42.716200Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "AI token consumption (missing cost #1): {2026: '$0K', 2027: '$1.2M', 2028: '$3.3M'} → 3-yr $4.5M\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
yearconf2026202720283-yr
cost_line
Predictive Routing🟢0583,5611,764,8702,348,431
Agent Copilot [named]🟢0311,000715,2001,026,200
Speech & Text Analytics [named]🟢0233,250715,800949,050
Email AI (Auto-Respond)🟡046,27287,136133,408
AI Translate🟢02,10062,15064,250
AI Summary & Insights🟢0000
WFM (no token meter — licence-included)🟢0000
TOTAL01,176,1833,345,1564,521,339
\n", + "
" + ], + "text/plain": [ + "year conf 2026 2027 2028 \\\n", + "cost_line \n", + "Predictive Routing 🟢 0 583,561 1,764,870 \n", + "Agent Copilot [named] 🟢 0 311,000 715,200 \n", + "Speech & Text Analytics [named] 🟢 0 233,250 715,800 \n", + "Email AI (Auto-Respond) 🟡 0 46,272 87,136 \n", + "AI Translate 🟢 0 2,100 62,150 \n", + "AI Summary & Insights 🟢 0 0 0 \n", + "WFM (no token meter — licence-included) 🟢 0 0 0 \n", + "TOTAL 0 1,176,183 3,345,156 \n", + "\n", + "year 3-yr \n", + "cost_line \n", + "Predictive Routing 2,348,431 \n", + "Agent Copilot [named] 1,026,200 \n", + "Speech & Text Analytics [named] 949,050 \n", + "Email AI (Auto-Respond) 133,408 \n", + "AI Translate 64,250 \n", + "AI Summary & Insights 0 \n", + "WFM (no token meter — licence-included) 0 \n", + "TOTAL 4,521,339 " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── Token cost by year — engine call, rollout-gated ──────────────────\n", + "frames = []\n", + "for y in YEARS:\n", + " core = calculate_total_cost(sites, CORE_SCOPES, METERS, DEFAULT_PRICING,\n", + " GENESYS_CLAIM, YEAR_INDEX[y], include_platform=False,\n", + " use_contracted=USE_CONTRACTED_RATES, rollout=TOKEN_ROLLOUT)\n", + " email = calculate_total_cost(sites, EMAIL_SCOPES, METERS, DEFAULT_PRICING,\n", + " GENESYS_CLAIM, YEAR_INDEX[y], include_platform=False,\n", + " use_contracted=USE_CONTRACTED_RATES, rollout=EMAIL_TOKEN_ROLLOUT)\n", + " part = pd.concat([core, email], ignore_index=True)\n", + " part[\"year\"] = y\n", + " frames.append(part)\n", + "tokens_long = pd.concat(frames, ignore_index=True)\n", + "\n", + "tokens_pivot = tokens_long.pivot_table(index=\"cost_line\", columns=\"year\",\n", + " values=\"annual_cost\", aggfunc=\"sum\")\n", + "tokens_pivot.loc[\"WFM (no token meter — licence-included)\"] = [0.0, 0.0, 0.0]\n", + "tokens_pivot[\"3-yr\"] = tokens_pivot.sum(axis=1)\n", + "tokens_pivot = tokens_pivot.sort_values(\"3-yr\", ascending=False)\n", + "tokens_pivot.loc[\"TOTAL\"] = tokens_pivot.sum()\n", + "token_total_by_year = {y: float(tokens_pivot.loc[\"TOTAL\", y]) for y in YEARS}\n", + "\n", + "conf = {f: METERS[f].confidence.icon for f in USED_FEATURES}\n", + "tokens_pivot.insert(0, \"conf\", [conf.get(i, \"🟢\") for i in tokens_pivot.index[:-1]] + [\"\"])\n", + "print(f\"AI token consumption (missing cost #1): \"\n", + " f\"{ {y: money(v) for y, v in token_total_by_year.items()} } \"\n", + " f\"→ 3-yr {money(sum(token_total_by_year.values()))}\")\n", + "display(tokens_pivot)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "ee088f69", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:42.720863Z", + "iopub.status.busy": "2026-07-07T12:22:42.720525Z", + "iopub.status.idle": "2026-07-07T12:22:43.918533Z", + "shell.execute_reply": "2026-07-07T12:22:43.917721Z" + } + }, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#2a78d6", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Agent Copilot [named]", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0.0, + 311000.0, + 715200.0 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#008300", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Speech & Text Analytics [named]", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0.0, + 233250.0, + 715800.0 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#4a3aa7", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Predictive Routing", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0.0, + 583561.0, + 1764870.0 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#eda100", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Email AI (Auto-Respond)", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0.0, + 46272.0, + 87136.0 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#e34948", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "AI Translate", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0.0, + 2100.0, + 62150.0 + ] + } + ], + "layout": { + "annotations": [ + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$0K", + "x": 0, + "y": 0.0, + "yshift": 12 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$1.2M", + "x": 1, + "y": 1176183.0, + "yshift": 12 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$3.3M", + "x": 2, + "y": 3345156.0, + "yshift": 12 + } + ], + "bargap": 0.45, + "barmode": "stack", + "font": { + "color": "#52514e", + "family": "system-ui, -apple-system, \"Segoe UI\", sans-serif", + "size": 12 + }, + "height": 460, + "hovermode": "x unified", + "legend": { + "font": { + "color": "#52514e", + "size": 11 + }, + "orientation": "h", + "x": 0, + "y": -0.1, + "yanchor": "top" + }, + "margin": { + "b": 80, + "l": 70, + "r": 30, + "t": 70 + }, + "paper_bgcolor": "#fcfcfb", + "plot_bgcolor": "#fcfcfb", + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermap": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermap" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "title": { + "font": { + "color": "#0b0b0b", + "size": 16 + }, + "text": "AI Experience token consumption — absent from the Genesys cost case
Claim-level usage · published meter rates · $1.00/token US list · consumption starts at each region's implementation month", + "x": 0.02, + "xanchor": "left" + }, + "xaxis": { + "linecolor": "#c3c2b7", + "showgrid": false, + "tickfont": { + "color": "#898781" + }, + "type": "category" + }, + "yaxis": { + "gridcolor": "#e1e0d9", + "tickfont": { + "color": "#898781" + }, + "tickformat": "$~s", + "zerolinecolor": "#c3c2b7", + "zerolinewidth": 1.5 + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── Chart: token cost by meter per year (stacked) ────────────────────\n", + "TOKEN_LINE_COLOR = { # entity colors consistent with the capability palette\n", + " \"Agent Copilot [named]\": CAP_COLOR[\"Agent Copilot\"],\n", + " \"Speech & Text Analytics [named]\": CAP_COLOR[\"STA\"],\n", + " \"Predictive Routing\": CAP_COLOR[\"Predictive Routing\"],\n", + " \"Email AI (Auto-Respond)\": CAP_COLOR[\"Email\"],\n", + " \"AI Translate\": CAP_COLOR[\"Supervisor Copilot\"],\n", + " \"AI Summary & Insights\": \"#e87ba4\",\n", + "}\n", + "X = [str(y) for y in YEARS]\n", + "fig = go.Figure()\n", + "for line, color in TOKEN_LINE_COLOR.items():\n", + " vals = [tokens_long.query(\"cost_line == @line and year == @y\")[\"annual_cost\"].sum()\n", + " for y in YEARS]\n", + " if sum(vals) > 0:\n", + " fig.add_trace(bar(X, vals, line, color))\n", + "for i, y in enumerate(YEARS): # annotations anchor by category index, not label\n", + " total = token_total_by_year[y]\n", + " fig.add_annotation(x=i, y=total, text=f\"{money(total)}\", showarrow=False,\n", + " yshift=12, font=dict(size=12, color=INK))\n", + "fig.update_layout(barmode=\"stack\")\n", + "tei_layout(fig, \"AI Experience token consumption — absent from the Genesys cost case\",\n", + " \"Claim-level usage · published meter rates · $1.00/token US list · \"\n", + " \"consumption starts at each region's implementation month\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "7d197edc", + "metadata": {}, + "source": [ + "## §4 · AI implementation effort (missing cost #2)\n", + "\n", + "The deck's $2.4M professional services covers **base platform implementation only** — none of the\n", + "five AI capabilities are turn-key. Hours below are the **V2 corrected LoE**\n", + "(`docs/ctm_ai_labour_estimate_V2.md`; V1 was vendor-inflated ~4×): configuration, tuning cycles and\n", + "enablement at CTM scale, not custom development. Per V2 correction #1, Agent Copilot's line\n", + "includes email/chat Auto-Suggest — one implementation.\n", + "\n", + "**Model (deliberately simple — the activity-level engine is deferred):**\n", + "hours(feature) × blended rate, allocated to each feature's scoped regions by agent share, spread\n", + "uniformly from contract start to that region's implementation month; **KB readiness** is a\n", + "separately-flagged prerequisite project; **steady-state** is an absolute 500–900 h/yr\n", + "(tuning, KB refresh, drift correction) booked program-level in 2027–2028." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "4a7bd418", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:43.923450Z", + "iopub.status.busy": "2026-07-07T12:22:43.923326Z", + "iopub.status.idle": "2026-07-07T12:22:43.927251Z", + "shell.execute_reply": "2026-07-07T12:22:43.926625Z" + } + }, + "outputs": [], + "source": [ + "# ── CONFIG — AI implementation (V2 LoE) ──────────────────────────────\n", + "AI_IMPL_HOURS = { # (low, high) Y1 hours — ctm_ai_labour_estimate_V2.md\n", + " \"Agent Copilot\": (1_200, 1_800), # voice + digital incl. email Auto-Suggest, all languages\n", + " \"Email Auto-Respond\": (800, 1_400), # separate flow; needs system-of-record integration\n", + " \"STA\": (800, 1_200), # topics, programs, tuning × 7 languages\n", + " \"Supervisor Copilot\": (200, 400),\n", + " \"Predictive Routing\": (400, 700),\n", + " \"Cross-cutting\": (1_000, 1_800), # governance, PM, test environment, integration coordination\n", + "}\n", + "KB_READINESS_HOURS = (500, 1_500) # prerequisite project — flagged separately\n", + "STEADY_STATE_HOURS = (500, 900) # absolute h/yr, 2027-2028\n", + "HOURS_MODE = \"mid\" # \"low\" | \"mid\" | \"high\"\n", + "BLENDED_RATE = 225 # $/h — 175 offshore-heavy | 225 typical | 275 onshore\n", + "INCLUDE_KB_READINESS = True\n", + "\n", + "IMPL_FEATURE_REGIONS = { # which regions each impl workstream serves\n", + " \"Agent Copilot\": [\"NA\", \"ANZ\", \"EMEA\"] + ([\"ASIA\"] if COPILOT_INCLUDES_ASIA else []),\n", + " \"Email Auto-Respond\": REGIONS,\n", + " \"STA\": REGIONS,\n", + " \"Supervisor Copilot\": [\"NA\", \"ANZ\", \"EMEA\"], # deck claims $0 SupCopilot benefit in ASIA\n", + " \"Predictive Routing\": REGIONS,\n", + " \"Cross-cutting\": REGIONS,\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "afdcda65", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:43.929433Z", + "iopub.status.busy": "2026-07-07T12:22:43.929332Z", + "iopub.status.idle": "2026-07-07T12:22:43.944980Z", + "shell.execute_reply": "2026-07-07T12:22:43.944243Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "AI implementation (mid, $225/h): Y1 LoE $1.3M + KB readiness $225K + steady-state $315K (2027-28)\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
hourscost202620272028
workstream
Agent Copilot1,500337,500207,888129,6120
Cross-cutting1,400315,000178,711126,3669,923
Email Auto-Respond1,100247,500169,53070,1747,796
KB readiness (prerequisite)1,000225,000127,65190,2617,088
Predictive Routing550123,75070,20849,6443,898
STA1,000225,000127,65190,2617,088
Supervisor Copilot30067,50041,57825,9220
Steady-state tuning (2027-28)1,400315,0000157,500157,500
TOTAL8,2501,856,250923,217739,741193,293
\n", + "
" + ], + "text/plain": [ + " hours cost 2026 2027 2028\n", + "workstream \n", + "Agent Copilot 1,500 337,500 207,888 129,612 0\n", + "Cross-cutting 1,400 315,000 178,711 126,366 9,923\n", + "Email Auto-Respond 1,100 247,500 169,530 70,174 7,796\n", + "KB readiness (prerequisite) 1,000 225,000 127,651 90,261 7,088\n", + "Predictive Routing 550 123,750 70,208 49,644 3,898\n", + "STA 1,000 225,000 127,651 90,261 7,088\n", + "Supervisor Copilot 300 67,500 41,578 25,922 0\n", + "Steady-state tuning (2027-28) 1,400 315,000 0 157,500 157,500\n", + "TOTAL 8,250 1,856,250 923,217 739,741 193,293" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── V2 hours-range × rate model (swap point for the future LoE engine) ─\n", + "def hours_pick(rng, mode):\n", + " low, high = rng\n", + " return {\"low\": low, \"mid\": (low + high) / 2, \"high\": high}[mode]\n", + "\n", + "\n", + "def impl_year_fractions(impl_month):\n", + " # Spend spreads uniformly from contract start (month 0) to the impl month.\n", + " prev, fracs = 0, []\n", + " for yi in (1, 2, 3):\n", + " cur = min(12 * yi, impl_month)\n", + " fracs.append((cur - prev) / impl_month)\n", + " prev = cur\n", + " return fracs\n", + "\n", + "\n", + "def region_impl_month(feature, region):\n", + " if feature == \"Email Auto-Respond\" and region == \"NA\" and NA_EMAIL_EARLY:\n", + " return NA_EMAIL_IMPL_MONTH\n", + " return IMPL_MONTH[region]\n", + "\n", + "\n", + "def build_impl_costs(mode, rate, include_kb):\n", + " rows = []\n", + " workstreams = dict(AI_IMPL_HOURS)\n", + " if include_kb:\n", + " workstreams[\"KB readiness (prerequisite)\"] = KB_READINESS_HOURS\n", + " for feature, rng in workstreams.items():\n", + " regions = IMPL_FEATURE_REGIONS.get(feature, REGIONS)\n", + " scope_agents = sum(agents_by_region[r] for r in regions)\n", + " for r in regions:\n", + " hours = hours_pick(rng, mode) * agents_by_region[r] / scope_agents\n", + " fracs = impl_year_fractions(region_impl_month(feature, r))\n", + " rows.append({\"workstream\": feature, \"region\": r, \"hours\": hours,\n", + " \"cost\": hours * rate,\n", + " **{y: hours * rate * f for y, f in zip(YEARS, fracs)}})\n", + " df = pd.DataFrame(rows)\n", + " is_kb = df[\"workstream\"].str.startswith(\"KB\")\n", + " impl_y = {y: float(df.loc[~is_kb, y].sum()) for y in YEARS}\n", + " kb_y = {y: float(df.loc[is_kb, y].sum()) for y in YEARS}\n", + " steady = hours_pick(STEADY_STATE_HOURS, mode) * rate\n", + " steady_y = {2026: 0.0, 2027: steady, 2028: steady}\n", + " return df, impl_y, kb_y, steady_y\n", + "\n", + "\n", + "impl_detail, impl_by_year, kb_by_year, steady_by_year = build_impl_costs(\n", + " HOURS_MODE, BLENDED_RATE, INCLUDE_KB_READINESS)\n", + "\n", + "impl_summary = impl_detail.groupby(\"workstream\")[[\"hours\", \"cost\", *YEARS]].sum()\n", + "impl_summary.loc[\"Steady-state tuning (2027-28)\"] = [\n", + " hours_pick(STEADY_STATE_HOURS, HOURS_MODE) * 2,\n", + " sum(steady_by_year.values()), *[steady_by_year[y] for y in YEARS]]\n", + "impl_summary.loc[\"TOTAL\"] = impl_summary.sum()\n", + "print(f\"AI implementation ({HOURS_MODE}, ${BLENDED_RATE}/h): \"\n", + " f\"Y1 LoE {money(sum(impl_by_year.values()))}\"\n", + " + (f\" + KB readiness {money(sum(kb_by_year.values()))}\" if INCLUDE_KB_READINESS else \"\")\n", + " + f\" + steady-state {money(sum(steady_by_year.values()))} (2027-28)\")\n", + "display(impl_summary)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "b0614dec", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:43.946728Z", + "iopub.status.busy": "2026-07-07T12:22:43.946619Z", + "iopub.status.idle": "2026-07-07T12:22:43.950400Z", + "shell.execute_reply": "2026-07-07T12:22:43.949590Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "AI implementation $1.3M ÷ benefit claim $15.0M = 8.8% → FLAG — under-modelled by the industry benchmark\n", + "Industry benchmark: 20-40% of the Y1 benefit claim goes to implementation.\n", + "Reading: V2 deliberately strips vendor-services inflation from V1 (which sat at ~31%).\n", + "If the corrected case still looks good at V2 hours, it is robust to the higher estimate;\n", + "§9 sweeps hours × rate to show exactly how much the conclusion depends on this line.\n" + ] + } + ], + "source": [ + "# ── Smell test (V1 doc rule): AI impl should be ≥15% of the AI benefit claim ──\n", + "_impl_total = sum(impl_by_year.values())\n", + "_ratio = _impl_total / SLIDE_TOTALS[\"total_3yr\"]\n", + "_verdict = \"PASS\" if _ratio >= 0.15 else \"FLAG — under-modelled by the industry benchmark\"\n", + "print(f\"AI implementation {money(_impl_total)} ÷ benefit claim \"\n", + " f\"{money(SLIDE_TOTALS['total_3yr'])} = {_ratio:.1%} → {_verdict}\")\n", + "print(\"Industry benchmark: 20-40% of the Y1 benefit claim goes to implementation.\")\n", + "print(\"Reading: V2 deliberately strips vendor-services inflation from V1 (which sat at ~31%).\")\n", + "print(\"If the corrected case still looks good at V2 hours, it is robust to the higher estimate;\")\n", + "print(\"§9 sweeps hours × rate to show exactly how much the conclusion depends on this line.\")" + ] + }, + { + "cell_type": "markdown", + "id": "e98e70db", + "metadata": {}, + "source": [ + "## §5 · Corrected cost stack\n", + "\n", + "The pitched case vs the same case with the three missing costs added — and one *credit* the deck\n", + "also missed: the 12-month ramp means licences don't actually bill in 2026. Double-billing is\n", + "visible directly: in 2026–27 CTM pays the existing platforms **and** the Genesys programme." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "30948da4", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:43.952985Z", + "iopub.status.busy": "2026-07-07T12:22:43.952792Z", + "iopub.status.idle": "2026-07-07T12:22:43.970944Z", + "shell.execute_reply": "2026-07-07T12:22:43.970160Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Corrected programme cost: {2026: '$10.8M', 2027: '$13.5M', 2028: '$7.8M'} → 3-yr $32.1M\n", + "As pitched (deck): {2026: '$6.9M', 2027: '$4.3M', 2028: '$4.3M'} → 3-yr $15.5M (deck says $15.4M — rounding)\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
2026202720283-yr
CCaaS platform licences (ramp-adjusted)04,300,0004,300,0008,600,000
Base professional services + training2,567,000002,567,000
Existing platform (term-contract run-off)7,300,0007,300,000014,600,000
AI token consumption01,176,1833,345,1564,521,339
AI implementation + KB readiness923,217582,24135,7931,541,250
AI steady-state tuning0157,500157,500315,000
TOTAL — corrected10,790,21713,515,9247,838,44932,144,589
\n", + "
" + ], + "text/plain": [ + " 2026 2027 2028 \\\n", + "CCaaS platform licences (ramp-adjusted) 0 4,300,000 4,300,000 \n", + "Base professional services + training 2,567,000 0 0 \n", + "Existing platform (term-contract run-off) 7,300,000 7,300,000 0 \n", + "AI token consumption 0 1,176,183 3,345,156 \n", + "AI implementation + KB readiness 923,217 582,241 35,793 \n", + "AI steady-state tuning 0 157,500 157,500 \n", + "TOTAL — corrected 10,790,217 13,515,924 7,838,449 \n", + "\n", + " 3-yr \n", + "CCaaS platform licences (ramp-adjusted) 8,600,000 \n", + "Base professional services + training 2,567,000 \n", + "Existing platform (term-contract run-off) 14,600,000 \n", + "AI token consumption 4,521,339 \n", + "AI implementation + KB readiness 1,541,250 \n", + "AI steady-state tuning 315,000 \n", + "TOTAL — corrected 32,144,589 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
2026202720283-yr
CCaaS platform licences (as pitched, no ramp)4,300,0004,300,0004,300,00012,900,000
Base professional services + training2,567,000002,567,000
TOTAL — as pitched6,867,0004,300,0004,300,00015,467,000
\n", + "
" + ], + "text/plain": [ + " 2026 2027 2028 \\\n", + "CCaaS platform licences (as pitched, no ramp) 4,300,000 4,300,000 4,300,000 \n", + "Base professional services + training 2,567,000 0 0 \n", + "TOTAL — as pitched 6,867,000 4,300,000 4,300,000 \n", + "\n", + " 3-yr \n", + "CCaaS platform licences (as pitched, no ramp) 12,900,000 \n", + "Base professional services + training 2,567,000 \n", + "TOTAL — as pitched 15,467,000 " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ps_by_year = {2026: TCO_VERBATIM[\"prof_services_y1\"] + TCO_VERBATIM[\"training_y1\"],\n", + " 2027: 0.0, 2028: 0.0}\n", + "impl_kb_by_year = {y: impl_by_year[y] + kb_by_year[y] for y in YEARS}\n", + "\n", + "corrected_costs = pd.DataFrame({\n", + " \"CCaaS platform licences (ramp-adjusted)\": licence_by_year,\n", + " \"Base professional services + training\": ps_by_year,\n", + " \"Existing platform (term-contract run-off)\": current_by_year,\n", + " \"AI token consumption\": token_total_by_year,\n", + " \"AI implementation + KB readiness\": impl_kb_by_year,\n", + " \"AI steady-state tuning\": steady_by_year,\n", + "}).T[YEARS]\n", + "corrected_costs[\"3-yr\"] = corrected_costs.sum(axis=1)\n", + "corrected_total_by_year = {y: float(corrected_costs[y].sum()) for y in YEARS}\n", + "\n", + "pitched_costs = pd.DataFrame({\n", + " \"CCaaS platform licences (as pitched, no ramp)\": {y: TCO_VERBATIM[\"ccaas_annual\"] for y in YEARS},\n", + " \"Base professional services + training\": ps_by_year,\n", + "}).T[YEARS]\n", + "pitched_costs[\"3-yr\"] = pitched_costs.sum(axis=1)\n", + "pitched_total_by_year = {y: float(pitched_costs[y].sum()) for y in YEARS}\n", + "\n", + "kb_note = \"\" if INCLUDE_KB_READINESS else \" (KB readiness excluded)\"\n", + "print(f\"Corrected programme cost{kb_note}: \"\n", + " f\"{ {y: money(v) for y, v in corrected_total_by_year.items()} } \"\n", + " f\"→ 3-yr {money(sum(corrected_total_by_year.values()))}\")\n", + "print(f\"As pitched (deck): { {y: money(v) for y, v in pitched_total_by_year.items()} } \"\n", + " f\"→ 3-yr {money(sum(pitched_total_by_year.values()))} (deck says $15.4M — rounding)\")\n", + "display(pd.concat([corrected_costs,\n", + " pd.DataFrame(corrected_costs.sum()).T.set_axis([\"TOTAL — corrected\"])]))\n", + "display(pd.concat([pitched_costs,\n", + " pd.DataFrame(pitched_costs.sum()).T.set_axis([\"TOTAL — as pitched\"])]))" + ] + }, + { + "cell_type": "markdown", + "id": "1d37d385", + "metadata": {}, + "source": [ + "## §6 · Figure 1 — Benefits over 3 years (verbatim Genesys)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "897493af", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:43.973216Z", + "iopub.status.busy": "2026-07-07T12:22:43.973035Z", + "iopub.status.idle": "2026-07-07T12:22:44.069313Z", + "shell.execute_reply": "2026-07-07T12:22:44.068537Z" + } + }, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#2a78d6", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Agent Copilot", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0.0, + 1150000.0, + 6214000.0 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#1baf7a", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "WFM", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0.0, + 107692.30769230769, + 2893307.692307692 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#eda100", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Email", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0.0, + 1082428.5714285714, + 1888571.4285714286 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#008300", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "STA", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0.0, + 134576.92307692306, + 679423.076923077 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#4a3aa7", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Predictive Routing", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0.0, + 64980.769230769234, + 461019.23076923075 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#e34948", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Supervisor Copilot", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0.0, + 74826.92307692308, + 292173.07692307694 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "line": { + "color": "#52514e", + "width": 2 + }, + "marker": { + "line": { + "color": "#fcfcfb", + "width": 2 + }, + "size": 8 + }, + "mode": "lines+markers", + "name": "Cumulative benefits", + "type": "scatter", + "x": [ + "2026", + "2027", + "2028" + ], + "y": { + "bdata": "AAAAAAAAAAC/9Eu/dPJDQQAAAAA3sWxB", + "dtype": "f8" + } + } + ], + "layout": { + "annotations": [ + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$0K", + "x": 0, + "y": 0.0, + "yshift": 12 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$2.6M", + "x": 1, + "y": 2614505.4945054944, + "yshift": 12 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$12.4M", + "x": 2, + "y": 12428494.505494505, + "yshift": 12 + }, + { + "font": { + "color": "#52514e", + "size": 12 + }, + "showarrow": false, + "text": "3-yr total $15.0M (verbatim)", + "x": 2, + "xshift": -40, + "y": 15043000.0, + "yshift": 16 + } + ], + "bargap": 0.45, + "barmode": "stack", + "font": { + "color": "#52514e", + "family": "system-ui, -apple-system, \"Segoe UI\", sans-serif", + "size": 12 + }, + "height": 460, + "hovermode": "x unified", + "legend": { + "font": { + "color": "#52514e", + "size": 11 + }, + "orientation": "h", + "x": 0, + "y": -0.1, + "yanchor": "top" + }, + "margin": { + "b": 80, + "l": 70, + "r": 30, + "t": 70 + }, + "paper_bgcolor": "#fcfcfb", + "plot_bgcolor": "#fcfcfb", + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermap": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermap" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "title": { + "font": { + "color": "#0b0b0b", + "size": 16 + }, + "text": "Benefits over 3 years — verbatim Genesys (Appendix 4)
Phased by Genesys's own deployment schedule: benefits realize +3 months after regional go-live — $0 in 2026 · WFM = Workforce Forecast & Scheduling", + "x": 0.02, + "xanchor": "left" + }, + "xaxis": { + "linecolor": "#c3c2b7", + "showgrid": false, + "tickfont": { + "color": "#898781" + }, + "type": "category" + }, + "yaxis": { + "gridcolor": "#e1e0d9", + "tickfont": { + "color": "#898781" + }, + "tickformat": "$~s", + "zerolinecolor": "#c3c2b7", + "zerolinewidth": 1.5 + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig = go.Figure()\n", + "for cap in CAPABILITIES:\n", + " vals = [benefits_long.query(\"capability == @cap and year == @y\")[\"benefit\"].sum()\n", + " for y in YEARS]\n", + " fig.add_trace(bar(X, vals, cap, CAP_COLOR[cap]))\n", + "cum = pd.Series([benefit_total_by_year[y] for y in YEARS]).cumsum()\n", + "fig.add_trace(cum_line(X, cum, \"Cumulative benefits\"))\n", + "for i, y in enumerate(YEARS):\n", + " fig.add_annotation(x=i, y=benefit_total_by_year[y],\n", + " text=f\"{money(benefit_total_by_year[y])}\",\n", + " showarrow=False, yshift=12, font=dict(size=12, color=INK))\n", + "fig.add_annotation(x=len(YEARS) - 1, y=float(cum.iloc[-1]),\n", + " text=f\"3-yr total {money(float(cum.iloc[-1]))} (verbatim)\",\n", + " showarrow=False, yshift=16, xshift=-40, font=dict(size=12, color=INK2))\n", + "fig.update_layout(barmode=\"stack\")\n", + "tei_layout(fig, \"Benefits over 3 years — verbatim Genesys (Appendix 4)\",\n", + " \"Phased by Genesys's own deployment schedule: benefits realize +3 months after \"\n", + " \"regional go-live — $0 in 2026 · WFM = Workforce Forecast & Scheduling\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d62fd178", + "metadata": {}, + "source": [ + "## §7 · Figure 2 — Costs over 3 years, corrected\n", + "\n", + "The stack is the full programme cost. The dashed grey line is the deck's cumulative cost case\n", + "($15.4M) for reference — the gap between the lines is what the pitch left out (net of the ramp\n", + "credit it also left out)." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "f12111e3", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:44.072051Z", + "iopub.status.busy": "2026-07-07T12:22:44.071804Z", + "iopub.status.idle": "2026-07-07T12:22:44.104691Z", + "shell.execute_reply": "2026-07-07T12:22:44.103839Z" + } + }, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#2a78d6", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "CCaaS platform licences (ramp-adjusted)", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0.0, + 4300000.0, + 4300000.0 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#1baf7a", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Base professional services + training", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 2567000.0, + 0.0, + 0.0 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#eda100", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Existing platform (term-contract run-off)", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 7300000.0, + 7300000.0, + 0.0 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#008300", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "AI token consumption", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0.0, + 1176183.0, + 3345156.0 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#4a3aa7", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "AI implementation + KB readiness", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 923216.703288553, + 582240.770938251, + 35792.52577319588 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#e34948", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "AI steady-state tuning", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0.0, + 157500.0, + 157500.0 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "line": { + "color": "#52514e", + "width": 2 + }, + "marker": { + "line": { + "color": "#fcfcfb", + "width": 2 + }, + "size": 8 + }, + "mode": "lines+markers", + "name": "Cumulative — corrected", + "type": "scatter", + "x": [ + "2026", + "2027", + "2028" + ], + "y": { + "bdata": "/1aBFqmUZEHYbpbHHS53QQAAANDMp35B", + "dtype": "f8" + } + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "line": { + "color": "#c3c2b7", + "dash": "dash", + "width": 2 + }, + "marker": { + "line": { + "color": "#fcfcfb", + "width": 2 + }, + "size": 8 + }, + "mode": "lines+markers", + "name": "Cumulative — as pitched", + "type": "scatter", + "x": [ + "2026", + "2027", + "2028" + ], + "y": { + "bdata": "AAAAAA4yWkEAAAAAo0xlQQAAAAA/gG1B", + "dtype": "f8" + } + } + ], + "layout": { + "annotations": [ + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$10.8M", + "x": 0, + "y": 10790216.703288553, + "yshift": 12 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$13.5M", + "x": 1, + "y": 13515923.770938251, + "yshift": 12 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$7.8M", + "x": 2, + "y": 7838448.525773196, + "yshift": 12 + }, + { + "font": { + "color": "#52514e", + "size": 12 + }, + "showarrow": false, + "text": "3-yr $32.1M — $16.7M above the pitch", + "x": 2, + "xshift": -70, + "y": 32144589.0, + "yshift": 18 + } + ], + "bargap": 0.45, + "barmode": "stack", + "font": { + "color": "#52514e", + "family": "system-ui, -apple-system, \"Segoe UI\", sans-serif", + "size": 12 + }, + "height": 500, + "hovermode": "x unified", + "legend": { + "font": { + "color": "#52514e", + "size": 11 + }, + "orientation": "h", + "x": 0, + "y": -0.1, + "yanchor": "top" + }, + "margin": { + "b": 80, + "l": 70, + "r": 30, + "t": 70 + }, + "paper_bgcolor": "#fcfcfb", + "plot_bgcolor": "#fcfcfb", + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermap": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermap" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "title": { + "font": { + "color": "#0b0b0b", + "size": 16 + }, + "text": "Programme cost over 3 years — with the missed costs
Existing platforms bill until 31 Dec 2027 term-contract end (double-billing) · licences ramp-free for 12 months · tokens + AI implementation added", + "x": 0.02, + "xanchor": "left" + }, + "xaxis": { + "linecolor": "#c3c2b7", + "showgrid": false, + "tickfont": { + "color": "#898781" + }, + "type": "category" + }, + "yaxis": { + "gridcolor": "#e1e0d9", + "tickfont": { + "color": "#898781" + }, + "tickformat": "$~s", + "zerolinecolor": "#c3c2b7", + "zerolinewidth": 1.5 + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig = go.Figure()\n", + "for line in corrected_costs.index:\n", + " fig.add_trace(bar(X, [corrected_costs.loc[line, y] for y in YEARS], line, COST_COLOR[line]))\n", + "cum_corrected = pd.Series([corrected_total_by_year[y] for y in YEARS]).cumsum()\n", + "cum_pitched = pd.Series([pitched_total_by_year[y] for y in YEARS]).cumsum()\n", + "fig.add_trace(cum_line(X, cum_corrected, \"Cumulative — corrected\"))\n", + "fig.add_trace(cum_line(X, cum_pitched, \"Cumulative — as pitched\", color=CONTEXT, dash=\"dash\"))\n", + "for i, y in enumerate(YEARS):\n", + " fig.add_annotation(x=i, y=corrected_total_by_year[y],\n", + " text=f\"{money(corrected_total_by_year[y])}\",\n", + " showarrow=False, yshift=12, font=dict(size=12, color=INK))\n", + "delta = float(cum_corrected.iloc[-1] - cum_pitched.iloc[-1])\n", + "fig.add_annotation(x=len(YEARS) - 1, y=float(cum_corrected.iloc[-1]),\n", + " text=f\"3-yr {html_money(float(cum_corrected.iloc[-1]))} — \"\n", + " f\"{html_money(delta)} above the pitch\",\n", + " showarrow=False, yshift=18, xshift=-70, font=dict(size=12, color=INK2))\n", + "fig.update_layout(barmode=\"stack\")\n", + "tei_layout(fig, \"Programme cost over 3 years — with the missed costs\",\n", + " \"Existing platforms bill until 31 Dec 2027 term-contract end (double-billing) · \"\n", + " \"licences ramp-free for 12 months · tokens + AI implementation added\", height=500)\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "31448686", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:44.106991Z", + "iopub.status.busy": "2026-07-07T12:22:44.106665Z", + "iopub.status.idle": "2026-07-07T12:22:44.136989Z", + "shell.execute_reply": "2026-07-07T12:22:44.136239Z" + } + }, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#c3c2b7", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "As pitched (deck)", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 6867000.0, + 4300000.0, + 4300000.0 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#2a78d6", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Corrected", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 10790216.703288553, + 13515923.770938251, + 7838448.525773196 + ] + } + ], + "layout": { + "annotations": [ + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "+$3.9M", + "x": 0, + "xshift": 16, + "y": 10790216.703288553, + "yshift": 12 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "+$9.2M", + "x": 1, + "xshift": 16, + "y": 13515923.770938251, + "yshift": 12 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "+$3.5M", + "x": 2, + "xshift": 16, + "y": 7838448.525773196, + "yshift": 12 + } + ], + "bargap": 0.45, + "bargroupgap": 0.15, + "barmode": "group", + "font": { + "color": "#52514e", + "family": "system-ui, -apple-system, \"Segoe UI\", sans-serif", + "size": 12 + }, + "height": 400, + "hovermode": "x unified", + "legend": { + "font": { + "color": "#52514e", + "size": 11 + }, + "orientation": "h", + "x": 0, + "y": -0.1, + "yanchor": "top" + }, + "margin": { + "b": 80, + "l": 70, + "r": 30, + "t": 70 + }, + "paper_bgcolor": "#fcfcfb", + "plot_bgcolor": "#fcfcfb", + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermap": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermap" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "title": { + "font": { + "color": "#0b0b0b", + "size": 16 + }, + "text": "Cost case: as pitched vs corrected, by year
Delta labels = what each year's pitch understates", + "x": 0.02, + "xanchor": "left" + }, + "xaxis": { + "linecolor": "#c3c2b7", + "showgrid": false, + "tickfont": { + "color": "#898781" + }, + "type": "category" + }, + "yaxis": { + "gridcolor": "#e1e0d9", + "tickfont": { + "color": "#898781" + }, + "tickformat": "$~s", + "zerolinecolor": "#c3c2b7", + "zerolinewidth": 1.5 + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig = go.Figure()\n", + "fig.add_trace(bar(X, [pitched_total_by_year[y] for y in YEARS], \"As pitched (deck)\", CONTEXT))\n", + "fig.add_trace(bar(X, [corrected_total_by_year[y] for y in YEARS], \"Corrected\", \"#2a78d6\"))\n", + "for i, y in enumerate(YEARS):\n", + " d = corrected_total_by_year[y] - pitched_total_by_year[y]\n", + " fig.add_annotation(x=i, y=corrected_total_by_year[y], xshift=16,\n", + " text=f\"{'+' if d >= 0 else '−'}{money(abs(d))}\",\n", + " showarrow=False, yshift=12, font=dict(size=12, color=INK))\n", + "fig.update_layout(barmode=\"group\", bargap=0.35, bargroupgap=0.15)\n", + "tei_layout(fig, \"Cost case: as pitched vs corrected, by year\",\n", + " \"Delta labels = what each year's pitch understates\", height=400)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "b178185b", + "metadata": {}, + "source": [ + "## §8 · Figure 3 — Overall business case / ROI\n", + "\n", + "**Frame:** baseline-relative, against *do nothing* (keep paying $7.3M/yr).\n", + "Incremental cost = programme cost − $7.3M baseline; net = verbatim benefits − incremental cost.\n", + "This single frame captures both the 2026–27 **double-billing penalty** and the 2028\n", + "**cost-avoidance credit** once the old contracts terminate — no separate \"savings\" line needed.\n", + "The deck's implicit frame is the same, minus the three missing costs.\n", + "\n", + "*Not modelled: any early-termination fees, and migration costs beyond the PS/impl lines (§11).*" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "4fdcfe24", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:44.139720Z", + "iopub.status.busy": "2026-07-07T12:22:44.139505Z", + "iopub.status.idle": "2026-07-07T12:22:44.145487Z", + "shell.execute_reply": "2026-07-07T12:22:44.144835Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "corrected: net by year {2026: '-$3.5M', 2027: '-$3.6M', 2028: '$11.9M'}\n", + "pitched: net by year {2026: '$433K', 2027: '$5.6M', 2028: '$15.4M'}\n" + ] + } + ], + "source": [ + "BASELINE_ANNUAL = TCO_VERBATIM[\"current_annual\"]\n", + "\n", + "\n", + "def case_flows(total_cost_by_year):\n", + " inc = {y: total_cost_by_year[y] - BASELINE_ANNUAL for y in YEARS}\n", + " net = {y: benefit_total_by_year[y] - inc[y] for y in YEARS}\n", + " return inc, net\n", + "\n", + "\n", + "def case_kpis(inc, net):\n", + " net_list = [net[y] for y in YEARS]\n", + " inc_total = sum(inc.values())\n", + " pb = payback_years(net_list)\n", + " if pb is None:\n", + " pb_label = \"beyond 2028\"\n", + " elif pb == 0:\n", + " pb_label = \"immediate\"\n", + " else:\n", + " m = math.ceil(pb * 12)\n", + " pb_label = f\"{m} months (~{_MONTHS[(m - 1) % 12]} {2026 + (m - 1) // 12})\"\n", + " return {\n", + " \"3-yr benefits\": sum(benefit_total_by_year.values()),\n", + " \"3-yr incremental cost\": inc_total,\n", + " \"3-yr net\": sum(net_list),\n", + " \"ROI (net ÷ incremental cost)\":\n", + " f\"{sum(net_list) / inc_total:.0%}\" if inc_total > 0 else \"n/a — net cost saving\",\n", + " f\"NPV @ {DISCOUNT_RATE:.1%} (deck rate)\": npv(net_list, DISCOUNT_RATE),\n", + " \"NPV @ 8.0% (CTM treasury)\": npv(net_list, 0.08),\n", + " \"Payback\": pb_label,\n", + " }\n", + "\n", + "\n", + "inc_corrected, net_corrected = case_flows(corrected_total_by_year)\n", + "inc_pitched, net_pitched = case_flows(pitched_total_by_year)\n", + "kpi_corrected = case_kpis(inc_corrected, net_corrected)\n", + "kpi_pitched = case_kpis(inc_pitched, net_pitched)\n", + "print(f\"corrected: net by year { {y: money(v) for y, v in net_corrected.items()} }\")\n", + "print(f\"pitched: net by year { {y: money(v) for y, v in net_pitched.items()} }\")" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "c43d1e56", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:44.147385Z", + "iopub.status.busy": "2026-07-07T12:22:44.147219Z", + "iopub.status.idle": "2026-07-07T12:22:44.177837Z", + "shell.execute_reply": "2026-07-07T12:22:44.176873Z" + } + }, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#2a78d6", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Benefits (verbatim Genesys)", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0.0, + 2614505.4945054944, + 12428494.505494505 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#e34948", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Incremental cost vs $7.3M/yr baseline", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + -3490216.7032885533, + -6215923.770938251, + -538448.5257731955 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "line": { + "color": "#52514e", + "width": 2 + }, + "marker": { + "line": { + "color": "#fcfcfb", + "width": 2 + }, + "size": 8 + }, + "mode": "lines+markers", + "name": "Cumulative net", + "type": "scatter", + "x": [ + "2026", + "2027", + "2028" + ], + "y": { + "bdata": "/FsFWtSgSsECwbO+bA1bwQAAAMDyTVJB", + "dtype": "f8" + } + } + ], + "layout": { + "annotations": [ + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "-$3.5M", + "x": 0, + "y": -3490216.7032885533, + "yshift": -14 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "-$7.1M", + "x": 1, + "y": -7091634.97972131, + "yshift": -14 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$4.8M", + "x": 2, + "y": 4798411.0, + "yshift": 14 + }, + { + "align": "left", + "bgcolor": "#fcfcfb", + "bordercolor": "#e1e0d9", + "borderwidth": 1, + "font": { + "color": "#52514e", + "size": 12 + }, + "showarrow": false, + "text": "3-yr net $4.8M · ROI 47%
NPV@13.5% $2.3M · payback 32 months (~Aug 2028)", + "x": 0.01, + "xref": "paper", + "y": 0.98, + "yref": "paper" + } + ], + "bargap": 0.45, + "barmode": "relative", + "font": { + "color": "#52514e", + "family": "system-ui, -apple-system, \"Segoe UI\", sans-serif", + "size": 12 + }, + "height": 500, + "hovermode": "x unified", + "legend": { + "font": { + "color": "#52514e", + "size": 11 + }, + "orientation": "h", + "x": 0, + "y": -0.1, + "yanchor": "top" + }, + "margin": { + "b": 80, + "l": 70, + "r": 30, + "t": 70 + }, + "paper_bgcolor": "#fcfcfb", + "plot_bgcolor": "#fcfcfb", + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermap": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermap" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "title": { + "font": { + "color": "#0b0b0b", + "size": 16 + }, + "text": "Corrected business case — benefits vs incremental cost
Baseline = keep paying $7.3M/yr · double-billing hits 2026-27, cost avoidance and benefits land 2028", + "x": 0.02, + "xanchor": "left" + }, + "xaxis": { + "linecolor": "#c3c2b7", + "showgrid": false, + "tickfont": { + "color": "#898781" + }, + "type": "category" + }, + "yaxis": { + "gridcolor": "#e1e0d9", + "tickfont": { + "color": "#898781" + }, + "tickformat": "$~s", + "zerolinecolor": "#c3c2b7", + "zerolinewidth": 1.5 + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig = go.Figure()\n", + "fig.add_trace(bar(X, [benefit_total_by_year[y] for y in YEARS],\n", + " \"Benefits (verbatim Genesys)\", \"#2a78d6\"))\n", + "fig.add_trace(bar(X, [-inc_corrected[y] for y in YEARS],\n", + " \"Incremental cost vs $7.3M/yr baseline\", \"#e34948\"))\n", + "cum_net = pd.Series([net_corrected[y] for y in YEARS]).cumsum()\n", + "fig.add_trace(cum_line(X, cum_net, \"Cumulative net\"))\n", + "for i, c in enumerate(cum_net):\n", + " fig.add_annotation(x=i, y=float(c), text=f\"{money(float(c))}\", showarrow=False,\n", + " yshift=14 if c >= 0 else -14, font=dict(size=12, color=INK))\n", + "fig.update_layout(barmode=\"relative\")\n", + "fig.add_annotation(\n", + " xref=\"paper\", yref=\"paper\", x=0.01, y=0.98, align=\"left\", showarrow=False,\n", + " font=dict(size=12, color=INK2), bgcolor=SURFACE, bordercolor=GRID, borderwidth=1,\n", + " text=(f\"3-yr net {html_money(kpi_corrected['3-yr net'])} · \"\n", + " f\"ROI {kpi_corrected['ROI (net ÷ incremental cost)']}
\"\n", + " f\"NPV@{DISCOUNT_RATE:.1%} {html_money(kpi_corrected[f'NPV @ {DISCOUNT_RATE:.1%} (deck rate)'])} · \"\n", + " f\"payback {kpi_corrected['Payback']}\"))\n", + "tei_layout(fig, \"Corrected business case — benefits vs incremental cost\",\n", + " \"Baseline = keep paying $7.3M/yr · double-billing hits 2026-27, \"\n", + " \"cost avoidance and benefits land 2028\", height=500)\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "f504f2dc", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:44.180248Z", + "iopub.status.busy": "2026-07-07T12:22:44.180036Z", + "iopub.status.idle": "2026-07-07T12:22:44.206953Z", + "shell.execute_reply": "2026-07-07T12:22:44.204790Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The missed costs move the 3-yr case by $16.7M ($21.5M pitched → $4.8M corrected).\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
As pitched (deck)Corrected
3-yr benefits$15.0M$15.0M
3-yr incremental cost-$6.4M$10.2M
3-yr net$21.5M$4.8M
ROI (net ÷ incremental cost)n/a — net cost saving47%
NPV @ 13.5% (deck rate)$15.3M$2.3M
NPV @ 8.0% (CTM treasury)$17.5M$3.1M
Paybackimmediate32 months (~Aug 2028)
\n", + "
" + ], + "text/plain": [ + " As pitched (deck) Corrected\n", + "3-yr benefits $15.0M $15.0M\n", + "3-yr incremental cost -$6.4M $10.2M\n", + "3-yr net $21.5M $4.8M\n", + "ROI (net ÷ incremental cost) n/a — net cost saving 47%\n", + "NPV @ 13.5% (deck rate) $15.3M $2.3M\n", + "NPV @ 8.0% (CTM treasury) $17.5M $3.1M\n", + "Payback immediate 32 months (~Aug 2028)" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "kpis = pd.DataFrame({\"As pitched (deck)\": kpi_pitched, \"Corrected\": kpi_corrected})\n", + "kpis_fmt = kpis.map(lambda v: money(v) if isinstance(v, (int, float)) else v)\n", + "delta_net = kpi_pitched[\"3-yr net\"] - kpi_corrected[\"3-yr net\"]\n", + "print(f\"The missed costs move the 3-yr case by {money(delta_net)} \"\n", + " f\"({money(kpi_pitched['3-yr net'])} pitched → {money(kpi_corrected['3-yr net'])} corrected).\")\n", + "display(kpis_fmt)" + ] + }, + { + "cell_type": "markdown", + "id": "ab730edc", + "metadata": {}, + "source": [ + "## §9 · Sensitivity\n", + "\n", + "The two weakest inputs, swept: the **Email Auto-Respond token rate** (🔴 unpublished — the working\n", + "assumption is the only estimated meter in the token stack) and the **AI implementation LoE**\n", + "(V1 vs V2 disagreed by ~4×)." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "80c2a060", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:44.210569Z", + "iopub.status.busy": "2026-07-07T12:22:44.210085Z", + "iopub.status.idle": "2026-07-07T12:22:44.477936Z", + "shell.execute_reply": "2026-07-07T12:22:44.476537Z" + } + }, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "colorscale": [ + [ + 0.0, + "#cde2fb" + ], + [ + 0.08333333333333333, + "#b7d3f6" + ], + [ + 0.16666666666666666, + "#9ec5f4" + ], + [ + 0.25, + "#86b6ef" + ], + [ + 0.3333333333333333, + "#6da7ec" + ], + [ + 0.4166666666666667, + "#5598e7" + ], + [ + 0.5, + "#3987e5" + ], + [ + 0.5833333333333334, + "#2a78d6" + ], + [ + 0.6666666666666666, + "#256abf" + ], + [ + 0.75, + "#1c5cab" + ], + [ + 0.8333333333333334, + "#184f95" + ], + [ + 0.9166666666666666, + "#104281" + ], + [ + 1.0, + "#0d366b" + ] + ], + "hovertemplate": "%{y} × %{x}: %{z:$,.0f}", + "showscale": false, + "type": "heatmap", + "x": [ + "0.02 tokens/msg", + "0.05 tokens/msg", + "0.1 tokens/msg" + ], + "xgap": 2, + "y": [ + "10.0% auto-respond", + "25.5% auto-respond", + "50.0% auto-respond" + ], + "ygap": 2, + "z": [ + [ + 20930.0, + 52301.0, + 104602.0 + ], + [ + 53389.0, + 133408.0, + 266749.0 + ], + [ + 104602.0, + 261505.0, + 523010.0 + ] + ] + } + ], + "layout": { + "annotations": [ + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$21K", + "x": 0, + "y": 0 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$52K", + "x": 1, + "y": 0 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$105K", + "x": 2, + "y": 0 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$53K", + "x": 0, + "y": 1 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$133K", + "x": 1, + "y": 1 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$267K", + "x": 2, + "y": 1 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$105K", + "x": 0, + "y": 2 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$262K", + "x": 1, + "y": 2 + }, + { + "font": { + "color": "#ffffff", + "size": 12 + }, + "showarrow": false, + "text": "$523K", + "x": 2, + "y": 2 + } + ], + "bargap": 0.45, + "font": { + "color": "#52514e", + "family": "system-ui, -apple-system, \"Segoe UI\", sans-serif", + "size": 12 + }, + "height": 380, + "hovermode": "closest", + "legend": { + "font": { + "color": "#52514e", + "size": 11 + }, + "orientation": "h", + "x": 0, + "y": -0.1, + "yanchor": "top" + }, + "margin": { + "b": 80, + "l": 70, + "r": 30, + "t": 70 + }, + "paper_bgcolor": "#fcfcfb", + "plot_bgcolor": "#fcfcfb", + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermap": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermap" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "title": { + "font": { + "color": "#0b0b0b", + "size": 16 + }, + "text": "Email Auto-Respond tokens — 3-yr cost under the unpublished rate
Deck claims a $2.9M 3-yr Email benefit; even the worst corner stays well below it", + "x": 0.02, + "xanchor": "left" + }, + "xaxis": { + "linecolor": "#c3c2b7", + "showgrid": false, + "tickfont": { + "color": "#898781" + }, + "type": "category" + }, + "yaxis": { + "gridcolor": "#e1e0d9", + "showgrid": false, + "tickfont": { + "color": "#898781" + }, + "zerolinecolor": "#c3c2b7", + "zerolinewidth": 1.5 + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── 3-yr Email Auto-Respond token cost: tokens/msg × auto-respond rate ─\n", + "TOK_GRID = [0.02, 0.05, 0.10]\n", + "RATE_GRID = [0.10, 0.255, 0.50]\n", + "\n", + "\n", + "def email_token_cost_3yr(tokens_per_msg, respond_rate):\n", + " meter = dataclasses.replace(AUTORESPOND_METER, tokens_per_unit=tokens_per_msg,\n", + " units_per_token=1.0 / tokens_per_msg)\n", + " sc = dataclasses.replace(GENESYS_CLAIM, email_auto_respond_rate=respond_rate)\n", + " m = {**METERS, \"Email AI (Auto-Respond)\": meter}\n", + " return sum(\n", + " calculate_total_cost(sites, EMAIL_SCOPES, m, DEFAULT_PRICING, sc, YEAR_INDEX[y],\n", + " include_platform=False, rollout=EMAIL_TOKEN_ROLLOUT)\n", + " [\"annual_cost\"].sum()\n", + " for y in YEARS)\n", + "\n", + "\n", + "grid = [[email_token_cost_3yr(t, r) for t in TOK_GRID] for r in RATE_GRID]\n", + "zmax = max(max(row) for row in grid)\n", + "fig = go.Figure(go.Heatmap(\n", + " z=grid, x=[f\"{t} tokens/msg\" for t in TOK_GRID],\n", + " y=[f\"{r:.1%} auto-respond\" for r in RATE_GRID],\n", + " colorscale=[[i / (len(SEQ_BLUES) - 1), c] for i, c in enumerate(SEQ_BLUES)],\n", + " showscale=False, xgap=2, ygap=2,\n", + " hovertemplate=\"%{y} × %{x}: %{z:$,.0f}\"))\n", + "for i, r in enumerate(RATE_GRID):\n", + " for j, t in enumerate(TOK_GRID):\n", + " v = grid[i][j]\n", + " fig.add_annotation(x=j, y=i, text=f\"{money(v)}\", showarrow=False,\n", + " font=dict(size=12, color=\"#ffffff\" if v > 0.55 * zmax else INK))\n", + "tei_layout(fig, \"Email Auto-Respond tokens — 3-yr cost under the unpublished rate\",\n", + " \"Deck claims a $2.9M 3-yr Email benefit; even the worst corner stays well below it\",\n", + " height=380)\n", + "fig.update_layout(xaxis=dict(showgrid=False), yaxis=dict(showgrid=False, tickformat=None),\n", + " hovermode=\"closest\")\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "abd816ea", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:44.480803Z", + "iopub.status.busy": "2026-07-07T12:22:44.480601Z", + "iopub.status.idle": "2026-07-07T12:22:44.510856Z", + "shell.execute_reply": "2026-07-07T12:22:44.509842Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3-yr corrected net under implementation-cost assumptions (V2 LoE range × blended rate):\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
$175/h$225/h$275/h
low hours5,622,1615,327,1615,032,161
mid hours5,210,9114,798,4114,385,911
high hours4,799,6614,269,6613,739,661
\n", + "
" + ], + "text/plain": [ + " $175/h $225/h $275/h\n", + "low hours 5,622,161 5,327,161 5,032,161\n", + "mid hours 5,210,911 4,798,411 4,385,911\n", + "high hours 4,799,661 4,269,661 3,739,661" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Spread across the whole grid: $1.9M — the conclusion is not hostage to the implementation estimate; tokens and double-billing dominate.\n" + ] + } + ], + "source": [ + "# ── 3-yr corrected net across impl hours mode × blended rate ─────────\n", + "def corrected_net_3yr(mode, rate):\n", + " _, iby, kby, ssby = build_impl_costs(mode, rate, INCLUDE_KB_READINESS)\n", + " total = {y: licence_by_year[y] + ps_by_year[y] + current_by_year[y]\n", + " + token_total_by_year[y] + iby[y] + kby[y] + ssby[y] for y in YEARS}\n", + " _, net = case_flows(total)\n", + " return sum(net.values())\n", + "\n", + "\n", + "impl_sens = pd.DataFrame(\n", + " {f\"${r}/h\": {m: corrected_net_3yr(m, r) for m in (\"low\", \"mid\", \"high\")}\n", + " for r in (175, 225, 275)})\n", + "impl_sens.index = [f\"{m} hours\" for m in impl_sens.index]\n", + "print(\"3-yr corrected net under implementation-cost assumptions \"\n", + " \"(V2 LoE range × blended rate):\")\n", + "display(impl_sens)\n", + "spread = float(impl_sens.max().max() - impl_sens.min().min())\n", + "print(f\"Spread across the whole grid: {money(spread)} — the conclusion is not \"\n", + " f\"hostage to the implementation estimate; tokens and double-billing dominate.\")" + ] + }, + { + "cell_type": "markdown", + "id": "cb6ad27e", + "metadata": {}, + "source": [ + "## §10 · Verification & assertions\n", + "\n", + "The next cell re-derives key numbers independently and **raises on any failure**, so\n", + "`jupyter nbconvert --execute` acts as a regression gate for this notebook. Config-dependent\n", + "checks are guarded so the cell stays valid after you edit the CONFIG cells." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "7e241b27", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-07T12:22:44.513962Z", + "iopub.status.busy": "2026-07-07T12:22:44.513773Z", + "iopub.status.idle": "2026-07-07T12:22:44.539348Z", + "shell.execute_reply": "2026-07-07T12:22:44.537406Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "All assertions passed.\n", + " benefits 3-yr $15.0M · tokens 3-yr $4.5M · corrected net 3-yr $4.8M\n" + ] + } + ], + "source": [ + "def _approx(got, want, tol=0.5):\n", + " assert abs(got - want) <= tol, f\"got {got:,.2f}, want {want:,.2f}\"\n", + "\n", + "\n", + "# §1 — contract mechanics\n", + "_approx(current_state[\"annual_cost\"].sum(), 7_300_000)\n", + "if RAMP_MONTHS == 12:\n", + " _approx(licence_by_year[2026], 0)\n", + " _approx(licence_by_year[2027], 4_300_000)\n", + "if all(row[\"contract_termination\"] == dt.date(2027, 12, 31) for _, row in current_state.iterrows()):\n", + " _approx(current_by_year[2026], 7_300_000)\n", + " _approx(current_by_year[2028], 0)\n", + "\n", + "# §2 — verbatim benefits reproduce the deck\n", + "_approx(benefit_total_by_year[2026], 0)\n", + "_approx(sum(benefit_total_by_year.values()), verbatim[\"three_yr\"].sum())\n", + "assert abs(sum(benefit_total_by_year.values()) - SLIDE_TOTALS[\"total_3yr\"]) <= _tol(15e6)\n", + "for r in REGIONS:\n", + " _approx(benefits_long.query(\"region == @r\")[\"benefit\"].sum(),\n", + " verbatim.query(\"region == @r\")[\"three_yr\"].sum())\n", + "\n", + "# §3 — token hand-checks (independent derivations)\n", + "sta = tokens_long.query(\"cost_line == 'Speech & Text Analytics [named]'\")\n", + "_named = {s.site_name: s.named_users for s in sites}\n", + "_sta_2028 = sum(_named[n] * 30 * TOKEN_ROLLOUT.live_months_in_year(n, 3) for n in ALL_SITES)\n", + "_approx(sta.query(\"year == 2028\")[\"annual_cost\"].sum(), _sta_2028)\n", + "if not COPILOT_INCLUDES_ASIA:\n", + " _approx(_sta_2028, 715_800) # NAM/AUZ/EMEA ×12mo + ASIA ×10mo, by hand\n", + " cp = tokens_long.query(\"cost_line == 'Agent Copilot [named]' and year == 2028\")\n", + " _approx(cp[\"annual_cost\"].sum(), 1_490 * 40 * 12) # = $715,200\n", + "assert (tokens_long.query(\"cost_line == 'AI Summary & Insights'\")[\"annual_cost\"] == 0).all(), \\\n", + " \"Rule 1: Copilot must cover AI Summary at Copilot sites\"\n", + "assert math.ceil(1_214_358 * DEFAULT_METERS[\"Predictive Routing\"].tokens_per_unit) == 71_433\n", + "_approx(tokens_long.query(\"year == 2026\")[\"annual_cost\"].sum(), 0) # nothing live in 2026\n", + "\n", + "# §4 — impl model reconciles with the V2 doc\n", + "if HOURS_MODE == \"mid\" and BLENDED_RATE == 225:\n", + " _approx(sum(impl_by_year.values()), 5_850 * 225) # $1,316,250 — V2's \"$1.3M\"\n", + " _approx(sum(steady_by_year.values()), 700 * 225 * 2)\n", + " if INCLUDE_KB_READINESS:\n", + " _approx(sum(kb_by_year.values()), 1_000 * 225)\n", + "\n", + "# §5 — cost stacks\n", + "_approx(sum(pitched_total_by_year.values()),\n", + " 3 * TCO_VERBATIM[\"ccaas_annual\"] + ps_by_year[2026]) # ≈ deck's $15.4M\n", + "_approx(sum(corrected_total_by_year.values()),\n", + " sum(licence_by_year.values()) + ps_by_year[2026] + sum(current_by_year.values())\n", + " + sum(token_total_by_year.values()) + sum(impl_kb_by_year.values())\n", + " + sum(steady_by_year.values()))\n", + "\n", + "# §8 — flows tie out\n", + "for y in YEARS:\n", + " _approx(net_corrected[y],\n", + " benefit_total_by_year[y] - (corrected_total_by_year[y] - BASELINE_ANNUAL))\n", + "\n", + "print(\"All assertions passed.\")\n", + "print(f\" benefits 3-yr {money(sum(benefit_total_by_year.values()))} · \"\n", + " f\"tokens 3-yr {money(sum(token_total_by_year.values()))} · \"\n", + " f\"corrected net 3-yr {money(kpi_corrected['3-yr net'])}\")" + ] + }, + { + "cell_type": "markdown", + "id": "8c43881a", + "metadata": {}, + "source": [ + "## §11 · Risks, gaps & next steps\n", + "\n", + "**Findings to lead with**\n", + "- **Predictive Routing is net-negative standalone at claimed scope:** ~$1.8M/yr in tokens at 100%\n", + " eligibility vs a $470K/yr claimed benefit. `PR_ELIGIBILITY` is the honest knob — Genesys likely\n", + " intends PR on a queue subset; ask which queues.\n", + "- **2026 is a pure cost year** — $0 benefits under Genesys's own schedule, while CTM double-pays\n", + " existing platforms plus PS and early AI implementation.\n", + "- The ramp programme (12 licence-free months) is a real credit the deck also failed to model —\n", + " the correction is not one-directional.\n", + "\n", + "**Known gaps / data wanted**\n", + "- Non-NAM site volumes & AHTs are `tokencalc` placeholders (🟡) — all non-NA token figures inherit\n", + " that. Regional current-cost split is an agent-share allocation pending real contract data.\n", + "- Email Auto-Respond token rate is unpublished (🔴) — §9 bounds it; even the worst corner is small\n", + " relative to the Email benefit claim.\n", + "- Early-termination fees, co-term options, and migration costs beyond PS/impl are not modelled.\n", + "- Contract termination default (31 Dec 2027) zeroes current costs in 2028; if any region's term\n", + " runs longer, 2028 worsens — edit the per-region dates in §1.\n", + "- The smell test (§4) flags V2 implementation hours as below the 20–40% industry band — V2\n", + " deliberately strips vendor inflation; §9 shows the conclusion is robust across the whole\n", + " V2 range × rate grid.\n", + "- Deck internal rounding: cell-level benefits cross-foot to $15.04M vs the $15.0M headline\n", + " (and $13.72M vs $13.6M annual); tolerated, not \"fixed\".\n", + "\n", + "**Next steps**\n", + "- Replace §4's hours-range model with the activity-level `ImplementationEffort` engine sketched in\n", + " `docs/ctm_ai_labour_estimate.md` (dataclasses + `tokencalc/implementation.py` + tests).\n", + "- Confirm contracted token rates and regional pricing (EU/AU/APAC flagged TBD at $1.00 list).\n", + "- Feed real per-region current-platform contract values and termination dates into §1." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/studies/202512_GenesysCX/ctm-token-calculator/tests/test_cost_model.py b/studies/202512_GenesysCX/ctm-token-calculator/tests/test_cost_model.py index e6c5f2c..ebd2d83 100644 --- a/studies/202512_GenesysCX/ctm-token-calculator/tests/test_cost_model.py +++ b/studies/202512_GenesysCX/ctm-token-calculator/tests/test_cost_model.py @@ -108,6 +108,50 @@ def test_consumption_tokens_rounded_up_monthly(): assert df.iloc[0]["annual_cost"] == pytest.approx(3 * 12 * 1.0) +def test_predictive_routing_consumption(): + """1,700 calls/mo ÷ 17 per token = 100 tokens/mo → $1,200/yr (year 2, no ramp).""" + site = SiteInput( + "Tiny", "US", agents=5, supervisors=0, + voice_volume_monthly=1_700, email_volume_monthly=0, + chat_volume_monthly=0, sms_volume_monthly=0, + voice_aht_seconds=300, email_aht_seconds=600, + chat_aht_seconds=480, voice_acw_seconds=60, + fully_loaded_agent_cost_annual=65_000, + fully_loaded_supervisor_cost_annual=95_000, + ) + df = calculate_consumption_ai_cost( + [site], FeatureScope("Predictive Routing", ["Tiny"]), + DEFAULT_METERS["Predictive Routing"], "realistic", DEFAULT_PRICING, year=2, + ) + assert df.iloc[0]["tokens_monthly"] == 100 + assert df.iloc[0]["annual_cost"] == pytest.approx(1_200) + + +def test_predictive_routing_eligibility_and_total_cost(): + """eligibility_pct halves the routed volume; total_cost handles the scope.""" + site = SiteInput( + "Tiny", "US", agents=5, supervisors=0, + voice_volume_monthly=1_700, email_volume_monthly=0, + chat_volume_monthly=0, sms_volume_monthly=0, + voice_aht_seconds=300, email_aht_seconds=600, + chat_aht_seconds=480, voice_acw_seconds=60, + fully_loaded_agent_cost_annual=65_000, + fully_loaded_supervisor_cost_annual=95_000, + ) + scope = FeatureScope("Predictive Routing", ["Tiny"], eligibility_pct=0.5) + df = calculate_consumption_ai_cost( + [site], scope, DEFAULT_METERS["Predictive Routing"], "realistic", + DEFAULT_PRICING, year=2, + ) + assert df.iloc[0]["tokens_monthly"] == 50 + total = calculate_total_cost( + [site], [scope], DEFAULT_METERS, DEFAULT_PRICING, "realistic", 2, + include_platform=False, + ) + pr_row = total[total["cost_line"] == "Predictive Routing"].iloc[0] + assert pr_row["annual_cost"] == pytest.approx(50 * 12 * 1.0) + + def test_regional_pricing_not_hardcoded(): pricing = dict(DEFAULT_PRICING) from tokencalc.meters import TokenPricing diff --git a/studies/202512_GenesysCX/ctm-token-calculator/tokencalc/cost_model.py b/studies/202512_GenesysCX/ctm-token-calculator/tokencalc/cost_model.py index 12f9868..a24e1d0 100644 --- a/studies/202512_GenesysCX/ctm-token-calculator/tokencalc/cost_model.py +++ b/studies/202512_GenesysCX/ctm-token-calculator/tokencalc/cost_model.py @@ -170,6 +170,11 @@ def _monthly_units(site: SiteInput, feature: str, scope: FeatureScope, # can be used to scope to a subset of interactions (e.g. non-English only). eligibility = scope.eligibility_pct if scope.eligibility_pct is not None else 1.0 return site.voice_volume_monthly * eligibility # translations + if feature == "Predictive Routing": + # Every predictively-routed voice interaction consumes the PR meter; + # eligibility_pct scopes to the share of volume on PR-enabled queues. + eligibility = scope.eligibility_pct if scope.eligibility_pct is not None else 1.0 + return site.voice_volume_monthly * eligibility # routed interactions raise KeyError(f"No consumption-volume mapping for feature {feature!r}")