# Calculator Pattern v1.0.0 Standardizes **Calculators** β€” Palladium masters that price a vendor's product from that vendor's own **published rate card**: a verbatim rate-card anchor, a client overlay in tagged cells, and a re-anchoring protocol for when the vendor republishes. ## 🐾 Red Panda Approvalβ„’ This pattern follows Red Panda Approval standards (see `CLAUDE.md` for the rubric). **Audience note:** written to be loaded whole as context by an LLM agent building or modifying a Calculator. Rules are imperative (MUST/SHOULD/NEVER), each with a one-line *why*. It **specializes** [`Mercury_Notebook_Pattern_V1-00.md`](Mercury_Notebook_Pattern_V1-00.md) β€” read that first; the shared mechanics (reactivity, widget-pairs, gate, staging, appendix, packaging) are **not** restated here. `CLAUDE.md` is the always-on contract and takes precedence where documents disagree. Reference implementation: [`calculators/Genesys_Token_Calculator/`](../calculators/Genesys_Token_Calculator/). --- ## What a Calculator is β€” and why it's a third kind | | Study | Assessment | **Calculator** | |---|---|---|---| | Anchored to | a **dated publication** | β€” (it *is* the instrument) | a **living rate card** | | Naming | `YYYYMM_…` dated | `Instrument_Name` undated | `Vendor_Subject_Calculator` undated | | Anchor lifetime | immutable **forever** | n/a | immutable **until the vendor republishes** | | Its "numbers" | NPV / ROI / payback | status & progress | **run-rate, $/unit, per-feature cost** | | Lives in | `studies/` | `assessments/` | `calculators/` | The distinguishing property is the **anchor's lifetime**. A Study's anchor is frozen forever β€” the December 2025 Forrester PDF will never change, so a dated directory name is honest. A Calculator's anchor is frozen *until the vendor republishes*, at which point updating it is a deliberate, test-breaking, dated act. A `YYYYMM_` prefix would start lying the moment the vendor updated their page, so **Calculators are undated**; the publication date lives inside the anchor (`RATE_CARD_SOURCE_DATE`), pinned by a test and shown on stage. > **Note.** Mercury Pattern *Variant 3 β€” Exploratory calculator* describes the > pre-master form of this: a what-if surface inside a study. This pattern > promotes it to a first-class master type. A Variant-3 notebook that outlives > its study should graduate into `calculators/`. --- ## Required Structure & Contracts Everything the Mercury pattern requires, **plus** the four below. ### 1 Β· The rate card is a verbatim anchor, and it lives in `.py` `CLAUDE.md` says content never lives in `.py`. A published vendor rate card is **not content**, and the boundary is who authors and edits the thing: > **In `.py` (the engine):** the vendor's published rate card β€” verbatim, with > a source URL and a source date. A third-party record nobody in this repo > authors. **In tagged notebook cells:** everything the consultant authors or > the client supplies β€” volumes, headcount, licence model, the **negotiated** > rate, feature enablement, adoption assumptions, scenario definitions, and > the narrative. *Why:* the content rule exists because *"the notebook is the document the consultant edits"*. Nobody edits the vendor's rate card β€” editing it is **forbidden**, and changing it requires a republication plus the re-anchoring protocol below. Putting an immutable anchor on the surface designated as editable invites exactly the casual edit the anchor rule prevents, and makes the most-likely-wrong numbers checkable only via a notebook parse. Precedent: `studies/202602_TEI_Amazon_Connect/teicalc/anchor.py` holds Forrester's published tables the same way. **Corollary that has teeth:** the *contracted overlay* is **client data** and belongs in the `engagement-data` cell, never in `.py`. A Calculator's engine MUST ship **no client volumes at all** β€” no `defaults.py` full of real sites. (`studies/202607_CTM_GenesysCX/tokencalc/defaults.py` does exactly that; it is the anti-precedent, not the model.) The anchor module MUST carry: ```python RATE_CARD_SOURCE: str # the URL RATE_CARD_SOURCE_DATE: str # the publication's own "last updated" date METERS_VERBATIM: tuple[Meter, ...] # feature wording AND rate string, exact ``` Each meter keeps the vendor's **exact wording** (`feature`) and **exact rate string** (`published_rate`) beside the parsed number. *Why:* the string is what makes a transcription slip catchable β€” a test pins the strings, and a second test proves the float still agrees with the string beside it. ### 2 Β· The `topic-bank` cell holds the catalogue, not the rates Since the rates are in the engine, the required `topic-bank` cell holds what a consultant genuinely authors: the **feature catalogue** β€” per capability, a client-facing title, a one-line description, and the **sizing question to ask**. Each `key` MUST match a published meter key, pinned by a test. *Why:* this keeps the tag taxonomy honest rather than tag-stuffed, and the cross-layer pin catches a rename that would otherwise silently unprice a feature. ### 3 Β· Costs the vendor bills outside the primary meter are a separate line A vendor usually bills something adjacent on a different basis (here: Enhanced TTS, per character rather than per token). Such a line MUST have its **own source URL and date**, its own confidence flag, and its own subtotal β€” and the grand total MUST show the components labelled, never one blended figure. *Why:* the headline is understated without it and wrong with it silently folded in; a reader must be able to see which meter produced which dollar. ### 4 Β· Every published rule that changes the arithmetic is modelled and reported Vendors bury billing rules in prose. Each one that moves the number MUST be enforced in the engine, pinned with hand-checked arithmetic, and **surfaced in the result** (a `warnings` tuple), not applied silently. *Why:* a total that shrank for a published reason nobody can see is indistinguishable from a bug. In the reference implementation these are the per-call round-up, the free monthly allowance, the highest-tier rule, and the Copilot exclusion. --- ## The Re-anchoring Protocol The section unique to this kind. A Calculator tracks a *living* rate card, so updating the anchor is expected β€” but never casual. Re-anchoring is **Show-first** (`CLAUDE.md` Β§ Risk tier), unlike editing a Study anchor, which is forbidden outright. When the vendor republishes: 1. **Diff** the published table against the anchor module. 2. **Update the meters AND bump `RATE_CARD_SOURCE_DATE` in the same edit.** 3. **Update the test pins in the same commit.** 4. **Re-execute** the notebook; re-pin the gate's live-state numbers if the defaults moved. 5. **Report the cost moves honestly** β€” say what got more expensive. 6. **Show the user the rate diff** and the KPI move before it lands. > Never partially re-anchor. Never bump the date without the pins. A date that > claims a rate card the numbers don't match is worse than a stale one, because > it is stale *and* it lies about it. --- ## Standard Choice Values Beyond the Mercury pattern's confidence legend (🟒 confirmed / 🟑 estimated / πŸ”΄ unknown), Calculators standardize: ```python class Confidence(Enum): # 🟒 requires source_url AND source_date β€” enforced CONFIRMED = "confirmed" ESTIMATED = "estimated" UNKNOWN = "unknown" class LicenceModel(Enum): # vendors price per-seat features by licence model NAMED = "named" CONCURRENT = "concurrent" ``` A 🟒 meter that carries no source URL and date MUST fail to construct. *Why:* it is the one invariant that stops an unsourced number wearing a green tick on a client-facing stage. --- ## Recommended Practices - **The list β†’ negotiated walk.** Carry `list_cost` beside the effective cost and show both. The client sees MSRP β†’ their deal explicitly (the Mercury pattern's deck-frame column, applied to pricing). - **Published zeroes are findings.** A feature the vendor publishes as free renders as an explicit `$0` line with the quoted wording β€” not an omission. - **Adoption as a range.** Adoption is the largest uncertainty in any consumption estimate; ship conservative / base / aggressive rather than a single point. - **A `$/unit` figure.** Cost per interaction is the number a client remembers and the one that survives a change of volume assumptions. --- ## Anti-Patterns - ❌ **Don't build a business case in a Calculator.** NPV, payback, ROI, a benefit model, a P&L β€” any of these means the work has become a **Study**. Move it, don't grow it here. - ❌ **Don't edit `RATE_CARD_VERBATIM` to reflect a negotiated rate.** That is the overlay, and it belongs in `engagement-data`. The anchor is what the vendor published, not what the client pays. - ❌ **Don't bump the source date without updating the pins** (see the protocol). - ❌ **Don't ship a rate without a source URL and a confidence flag** β€” the schema should make this impossible. - ❌ **Don't put engagement volumes in the engine.** Masters stay client-clean; an engine with real client sites in it cannot be shared. - ❌ **Don't fold a differently-metered cost into the headline meter's subtotal** β€” separate line, separate source, labelled components. - ❌ **Don't model a per-unit round-up by averaging first.** Round per billing event, then sum; averaging first understates short events, sometimes by multiples. --- ## Testing The Mercury pattern's layers 0–4, plus **layer 1a β€” the rate-card tripwire**, which is the highest-value test in a Calculator: ```python def test_published_meter_table_verbatim(): """Exact feature wording AND exact rate string, every row. A transcription slip or an un-noticed republication breaks the build rather than quietly moving a client-facing number. """ assert {m.feature: m.published_rate for m in METERS_VERBATIM} == PUBLISHED def test_source_pinned(): assert RATE_CARD_SOURCE_DATE == "2026-07-12" def test_numeric_rates_match_their_published_strings(): """Catches a float drifting away from the wording beside it.""" def test_every_confirmed_meter_carries_source_url_and_date(): ... ``` Then **hand-check the arithmetic before pinning it** β€” compute each published rule's effect by hand, write the working into the test's docstring, and pin the result: ```python def test_voice_bot_15_second_roundup(): """1,000 calls at a 40s average. ceil(40/15) = 3 increments = 45s = 0.75 min/call β†’ 750.0 min/month. The naive 40/60 Γ— 1,000 = 666.67 understates by 12.5%. """ assert voice_bot_billable_minutes(VoiceBotUsage(1_000, 40)) == pytest.approx(750.0) ``` The in-notebook **gate** additionally asserts *structural ties that hold at any widget setting* β€” the allowance identity, effective ≀ list, components summing to the grand total, and each published exclusion β€” so a stakeholder moving a slider cannot produce an incoherent number. --- ## Adding a Calculator 1. Copy `calculators/Genesys_Token_Calculator/` and rename it `Vendor_Subject_Calculator` (underscores β€” directories are Python packages). 2. Rename the engine package; keep `staging.py` byte-for-byte. 3. Replace the anchor module with the new vendor's published table β€” **verbatim**, with its own source URL and date β€” and rewrite the rate-card tripwire against it. 4. Rewrite the `topic-bank` catalogue and reset `engagement-data` to placeholders. 5. Register the notebook in [`tests/nbcheck.py`](../tests/nbcheck.py) (`NOTEBOOK_FIRST`) β€” the completeness test fails until you do. 6. Run the full verification: `pytest`, `mypy`, headless `nbconvert --execute`, `python scripts/export_report.py`, a stage simulation, and an actual `mercury --working-dir .` render.