v1 · 18 min

The Universal Margin

Christopher White Christopher White Consulting · Melbourne, Australia

Abstract

This paper describes a financial calculation engine built from the realisation that the math of profitability is identical for every type of business. A seven-stage deterministic pipeline resolves tax, computes order-weighted margins, applies a cash-flow-correct refund model with per-product recoverability, and derives break-even boundaries; the same 335 lines of code for e-commerce, services, subscriptions, and lead generation. A vocabulary layer swaps the terminology without touching the math. Products, campaigns, and organic traffic sources collapse into a unified entity abstraction with per-channel elasticity physics. A goal solver reverse-engineers the exact adjustment to every available lever (pricing, costs, budget, conversion) needed to hit a profit target, ranked by effort score. A constitutional test suite runs 10,000 randomised scenarios per invariant, cross-references a Python oracle, and enforces named axioms: Conservation of Mass proves refund handling is correct; Service Identity proves the engine is truly universal. 117,893 lines of TypeScript. Nine revenue channels. Four business models. One engine. This paper reports on the architecture, the six failed versions that preceded it, and the discovery that started as a spreadsheet at an agency where nobody not the strategists, not the clients, not (in one case) the client's accountant could answer the most basic question in business: how much money do you actually make per sale?

Keywords: financial modelling,unit economics,deterministic systems,business intelligence,precision arithmetic,scenario simulation,entity abstraction,goal solver,refund recoverability

Every margin calculator on the internet is wrong.

Not approximately wrong. Not rounded wrong. Structurally wrong. They apply refund rates to revenue. They treat shipping costs as refundable. They use floating-point arithmetic for money. They model one business type and pretend it’s universal, or they model nothing at all and call it a template.

I know because I built six of them before I got it right.

The first version was a spreadsheet. I was working at an agency, and I wanted to understand why none of us, not the strategists, not the account managers, not even the clients, could answer the most basic question in business: how much money do you actually make per order?

Not revenue. Not gross profit. The real number. After the payment processor takes 2.9% plus thirty cents. After the 3PL charges for pick and pack. After the customer returns the product and you eat the shipping label. After the ad spend. After the OPEX. The actual number.

Nobody knew. Not the founders. Not their bookkeepers. Not, in one memorable case, their accountant.

That spreadsheet became a calculator. The calculator became a system. The system became something I didn’t expect: a proof that the mathematics of profitability is identical for every type of business. An e-commerce brand selling protein powder and a consultant selling strategy sessions and a SaaS company selling monthly subscriptions all resolve to the same seven-stage pipeline. The only thing that changes is what you call things.

This paper describes how that system works, what broke along the way, and why the hardest problems in financial software have nothing to do with finance.

The waterfall

Every business, regardless of what it sells, converts money through the same sequence. This is the full pipeline, in formal notation:

Stage 1: Tax Resolution

P_ex = P_inc / (1 + t)          if tax-registered, inclusive pricing
P_ex = P_inc                     if tax-registered, exclusive pricing
P_ex = P_inc                     if not registered

Where P_inc is the retail price as entered, t is the tax rate (e.g. 0.10 for GST), and P_ex is the price the business actually keeps.

Stage 2: Average Order Value

AOV = P_ex × Q × (1 - d)

Where Q is items per order and d is the average discount rate.

Stage 3: Cost of Goods Sold

COGS_unit = C_supplier + C_freight + C_packaging + C_other
COGS_order = COGS_unit × Q

Every cost component is additive. No hidden interactions. The sum of parts equals the total.

Stage 4: Variable Selling Costs

VSC splits into two fundamentally different types:

VSC_percent = AOV × (r_payment + r_marketplace + r_bnpl + r_additional)
VSC_fixed   = f_payment + f_pickpack + f_shipping + f_other + f_additional
VSC_total   = VSC_percent + VSC_fixed

This decomposition matters because percentage-based costs (payment processing at 2.9%, marketplace fees at 15%) scale with price, while fixed costs (a $0.30 payment fee, an $8.00 shipping label) don’t. When you raise your price by 10%, your margin doesn’t improve by the full 10%. The percentage fees eat into it. But the fixed costs become a smaller fraction of revenue. This is unit operating leverage and it’s the reason most margin calculators get scenario modelling wrong.

Stage 5: Contribution Margin (before refunds)

CM = AOV - COGS_order - VSC_total

This is the margin per order before accounting for returns, cancellations, or churn. It’s the number most calculators stop at. It’s not the real number.

Stage 6: Refund-Adjusted Net CM (the hard part)

Most calculators treat refunds as a revenue reduction: Revenue × (1 - R). That’s wrong. When a customer returns a product, three things happen, and they follow different rules:

Revenue: gone. You refund the customer. COGS: partially recoverable. If the product comes back in sellable condition, you get the inventory back. If it’s destroyed, you eat the cost. VSC: always sunk. FedEx doesn’t refund the shipping label. Stripe doesn’t refund the processing fee.

The correct model requires two variables: R (refund rate) and ρ (recovery rate, i.e. what percentage of returned goods can be resold).

Net_Revenue = AOV × (1 - R)

COGS_chargeable = (1 - R) + R × (1 - ρ)
                = 1 - R × ρ

Net_COGS = COGS_order × COGS_chargeable

Net_VSC = VSC_total                        (always sunk)

Net_CM = Net_Revenue - Net_COGS - Net_VSC

The COGS_chargeable rate is the key formula. You pay COGS for two categories of items: those kept by the customer (1 - R), and those returned but unrecoverable R × (1 - ρ). Items that are returned and recovered go back into inventory. You don’t pay for them twice.

Critically, ρ is a per-product setting, not a global rate. A supplement brand sets ρ = 0 for opened bottles (it goes in the bin). A fashion retailer sets ρ = 0.85 for clothing (most returns go back on the shelf). An electronics company sets ρ = 0.6 (some units are refurbished, some are dead). A SaaS product doesn’t use this field at all; the Lens hides it and churn replaces it.

This per-product granularity matters because aggregate refund rates lie. A business with 10% returns and 90% recoverability is in a completely different position than one with 10% returns and 0% recoverability. The first loses ~1% of margin to returns. The second loses ~10%. Most calculators can’t distinguish between them.

Boundary cases prove the formula:

R=0 (no refunds):     COGS_chargeable = 1       → full COGS charged. Correct.
R=1, ρ=1 (all returned, all recovered):  = 0    → zero COGS. Product came back. Correct.
R=1, ρ=0 (all returned, all destroyed):  = 1    → full COGS. Product is gone. Correct.
R=0.05, ρ=0.5 (5% returned, half recoverable): = 0.975  → almost full COGS, small recovery.

The code expresses this directly:

// STRICT CASH FLOW MODEL
const retainedByCustomer = new Decimal(1).minus(refundRatePercent);
const lostInReturn = refundRatePercent.times(
    new Decimal(1).minus(refundRecoveryPercent)
);
const cogsChargeableRate = retainedByCustomer.plus(lostInReturn);
const netCogs = orderCogs.times(cogsChargeableRate);
const netVsc = totalVscPerOrder;  // Always sunk
const netCmPerOrder = netRevenue.minus(netCogs).minus(netVsc);

The refundRecoveryPercent parameter answers: “What percentage of returned products can you actually resell?” For a candle company, maybe 0% (the wax melted in transit). For an electronics retailer, maybe 90% (repackage and resell as refurbished). For a service business, the question becomes: “What percentage of cancelled bookings can you rebook?” Different vocabulary. Same formula.

Stage 7: Ad Performance and Break-Even

Given a cost-per-click CPC and conversion rate CVR:

CPA = CPC / CVR                              (cost to acquire one customer)
Clicks = Budget / CPC
Orders = Clicks × CVR = Budget / CPA
Total_CM = Orders × Net_CM
Post_Ad_Profit = Total_CM - Budget

The break-even condition is when Post_Ad_Profit = 0:

Break_Even_CPA = Net_CM                      (you can spend up to your margin)
Break_Even_ROAS = AOV / Break_Even_CPA        (required return on ad spend)
Break_Even_CPC = Net_CM × CVR                (maximum you can bid per click)

These aren’t targets. They’re boundaries. If your CPA exceeds your Net CM, every additional order loses money. If your actual ROAS is below Break-Even ROAS, you’re buying revenue at a loss. The formulas are simple. The consequences of ignoring them are not.

This is the ProfitCalculator. Seven stages. 335 lines of pure static methods operating on Decimal inputs. No state, no side effects, no awareness of which business type is running. It just does math.

The full trace from raw input to dashboard output:

graph TD
    subgraph Input["User Input"]
        A["Retail Price (inc/ex tax)"] --> B["Tax Resolution"]
        C["Items per Order"] --> D
        E["Discount %"] --> D
    end

    subgraph Revenue["Stage 1-2: Revenue"]
        B --> |"P_ex = P_inc / (1+t)"| D["AOV Calculation"]
        D --> |"AOV = P_ex × Q × (1-d)"| F["AOV ex-tax"]
    end

    subgraph Costs["Stage 3-4: Costs"]
        G["Supplier + Freight + Packaging"] --> |"COGS = Σ costs × Q"| H["COGS per Order"]
        I["Payment % + Marketplace % + BNPL %"] --> |"VSC_pct = AOV × Σ rates"| J["VSC Percentage"]
        K["Fixed Fee + Shipping + Pick/Pack"] --> |"VSC_fix = Σ fixed costs"| L["VSC Fixed"]
        J --> M["VSC Total"]
        L --> M
    end

    subgraph Margin["Stage 5-6: Margin"]
        F --> N["CM = AOV - COGS - VSC"]
        H --> N
        M --> N
        N --> O{"Refund Model"}
        P["R (refund rate)"] --> O
        Q["ρ (recovery rate, per product)"] --> O
        O --> |"Net_CM = AOV(1-R) - COGS(1-Rρ) - VSC"| R["Net CM per Order"]
    end

    subgraph AdPerf["Stage 7: Ad Performance"]
        S["CPC"] --> T["CPA = CPC / CVR"]
        U["CVR"] --> T
        V["Budget"] --> W["Orders = Budget / CPA"]
        T --> W
        W --> X["Total CM = Orders × Net_CM"]
        R --> X
        X --> Y["Post-Ad Profit = CM - Budget"]
        V --> Y
    end

    subgraph Entity["Entity Collapse"]
        Y --> Z{"Entity Type?"}
        Z --> |"Ungrouped"| AA["SKU Entity"]
        Z --> |"In Campaign"| AB["Campaign Entity (weighted)"]
        Z --> |"Organic"| AC["Organic Entity (per-channel physics)"]
        AA --> AD["Dashboard Entity List"]
        AB --> AD
        AC --> AD
    end

    subgraph Tools["Downstream Tools"]
        AD --> AE["Scenario Planner"]
        AD --> AF["Goal Solver"]
        AD --> AG["Insights Engine"]
        AD --> AH["Dashboard"]
    end

    style Input fill:#1e293b,stroke:#475569,color:#e2e8f0
    style Revenue fill:#0f172a,stroke:#3b82f6,color:#93c5fd
    style Costs fill:#0f172a,stroke:#f97316,color:#fdba74
    style Margin fill:#0f172a,stroke:#22c55e,color:#86efac
    style AdPerf fill:#0f172a,stroke:#a855f7,color:#d8b4fe
    style Entity fill:#0f172a,stroke:#eab308,color:#fde68a
    style Tools fill:#0f172a,stroke:#06b6d4,color:#67e8f9

The precision problem

JavaScript does not understand money.

0.1 + 0.2 === 0.3  // false. It's 0.30000000000000004.

This is IEEE 754 floating-point arithmetic. It’s fine for physics simulations. It’s catastrophic for financial calculations, because errors compound. A 0.0000000000000004 rounding error on one order becomes a $400 discrepancy across a million orders.

ProfitOS uses Decimal.js for every financial value. Every price, every cost, every percentage, every intermediate calculation. This creates a type-level guarantee: if you see Decimal in a function signature, the value is precise. If you see number, it’s a display artefact or a configuration flag.

But Decimal.js creates its own problem: persistence.

The application uses Zustand for state management with localStorage persistence. When you JSON.stringify a Zustand store that contains Decimal instances, the Decimals get silently destroyed. new Decimal("5.99") serialises to… nothing useful. On rehydration, your store is full of undefined where your prices used to be. Every financial value in the system is now NaN. The dashboard shows nothing. The user’s data is gone.

The fix is a custom serialiser that walks every object recursively, wrapping Decimals on write and reviving them on read:

Store → Serialize: Decimal("5.99") → { _isDecimal: true, value: "5.99" }
→ localStorage
→ Deserialize: { _isDecimal: true, value: "5.99" } → new Decimal("5.99")

This is invisible infrastructure. It’s never visible to the user. But without it, the application doesn’t work. The hardest bug in financial software isn’t in the formulas. It’s at the boundaries, the moment a number crosses from one type system to another.

The same boundary problem appears in the database layer. Supabase stores numbers as PostgreSQL numeric. The application uses Decimal. Every entity exists in two forms: a DbSku with number fields in snake_case, and an Sku with Decimal fields in camelCase. The conversion layer between them is ~300 lines of boilerplate. It is the most boring code in the system and the most important.

One engine, four businesses

The calculation engine doesn’t know what kind of business it’s modelling. It only knows about prices, costs, margins, and orders. I didn’t design it that way. I discovered it.

The first version was built for e-commerce. Products, shipping, COGS, pick-and-pack. I understood this world because I’d spent years buying media for brands that sold physical things.

Then I tried to add services. A consultant charges $150/hour for a 10-hour engagement. That’s a “price” of $1,500. The “COGS” is their labour cost. The “VSC” is payment processing. There’s no shipping. There’s no packaging. There’s no pick-and-pack.

But the math is the same. Revenue minus costs minus fees equals margin.

Then subscriptions. A SaaS product charges $49/month. The “COGS” is server infrastructure. The “refund rate” is churn. The “order” is a new subscriber. Same math.

Then lead generation. A high-ticket offering worth $50,000. The “CVR” is a two-stage funnel (lead to qualified, qualified to closed). The “refund rate” is deal fall-through. Same math.

The engine stayed identical. What changed was the vocabulary. The system now has a Lens - a configuration object that controls what you see without affecting what’s calculated:

ConceptE-CommerceServiceSubscriptionLead Gen
ItemProductServicePlanOffering
OrderPurchaseBookingSubscriberDeal Closed
COGSSupplier + FreightLabour + TravelInfrastructureFulfilment
RefundReturnCancellationChurnFall-through
CVRConversion RateBooking RateSignup RateLead Rate

Each Lens also controls field visibility. The e-commerce lens shows “Items per Order”, “Inbound Freight”, “Packaging”, “BNPL Fees”, and “Marketplace Fees”. The service lens hides all of them, because a consultant doesn’t have inbound freight. The subscription lens surfaces “Monthly Churn Rate” and “Billing Cycle”. The lead-gen lens surfaces “Sales Cycle Days” and a two-stage conversion funnel.

But underneath, the Sku type is the same. The ProfitCalculator is the same. The seven-stage waterfall is the same.

I started with language. I assumed that services, subscriptions, and products needed different engines because the industry was different. The first few attempts at a multi-model system involved separate calculators, separate types, separate data models. They were expensive to build, impossible to test, and constantly out of sync.

The breakthrough was realising that the industry was irrelevant. The math doesn’t care what you call it. Price minus cost equals margin. Always. The Lens is a 173-line file that makes one engine serve four markets. This is a sufficiency claim, not a completeness claim. The engine handles four business models because the math admits them, not because I’ve proven no fifth model exists that would break it.

The entity collapse

The dashboard doesn’t render products, or campaigns, or organic traffic sources. It renders entities.

An entity is a unified abstraction that collapses three different data types into a single interface:

type DashboardEntityType = 'sku' | 'campaign' | 'organic-source';

The collapse works like this:

  1. If a product is assigned to a campaign group, it stops appearing individually. The campaign becomes the entity. Its metrics are the order-weighted aggregate of all its constituent products.
  2. If a product isn’t assigned to any campaign, it appears as its own entity.
  3. Each organic revenue source (SEO, email, marketplace, affiliate, social, loyalty, direct) becomes its own entity, tied to a product but independent of campaigns.

When multiple products collapse into a campaign entity, the aggregation is order-weighted, not averaged:

Campaign_CM_per_order = Σ(CM_i × Orders_i) / Σ(Orders_i)
Campaign_AOV         = Σ(AOV_i × Orders_i) / Σ(Orders_i)

A product generating 80% of a campaign’s orders dominates the blended metrics. A product generating 2% of orders barely registers. This is the only mathematically honest way to aggregate unit economics across a portfolio.

Every entity shares the same interface: orders, revenue, contribution margin, ad spend, post-ad profit. The dashboard, the scenario planner, the goal solver, and the insights engine all operate on the same flat list. They don’t need to know whether they’re looking at a single product, a campaign containing five products, or an SEO traffic source.

This didn’t start as a design decision. It started as a performance problem.

The scenario engine applies pricing adjustments, cost adjustments, growth multipliers, and LTV projections to every revenue source in the system. Without the entity collapse, the engine would need to calculate each product’s contribution to each campaign, then each campaign’s contribution to the dashboard, then each organic source’s contribution separately. The combinatorial explosion was real. A user with 20 products, 5 campaigns, and 8 organic channels would generate 140+ individual calculations per scenario adjustment. And the adjustments compose: pricing changes affect demand (via elasticity), which affects orders, which affects revenue, which affects contribution.

The entity collapse reduces this to a flat list of N entities, each with pre-computed baseline metrics. The scenario engine doesn’t need to know the internal structure. It just adjusts the baseline.

But the real benefit was conceptual. Once everything is an entity, you can compare them. A campaign spending $5,000/month on Meta ads and an SEO channel generating 200 organic orders/month are the same kind of thing. They both have a contribution margin. They both have a cost (explicit ad spend vs. implicit content investment). They both respond to price changes, with different elasticity.

Which brings us to physics.

Channel physics

Not all revenue sources respond to the world the same way.

If you raise your price by 10%, your SEO traffic will feel it immediately. Search users are comparison-shopping. Elasticity is high (-1.5: a 1% price increase causes a 1.5% demand drop). But your loyalty customers won’t care. They’re already committed. Elasticity is zero.

If you change your supplier costs, your marketplace orders won’t be affected. Amazon sets the price. You eat the margin change. But your direct website orders will see the full impact.

The demand model uses a price-elasticity function:

Δ_demand = Δ_price% × ε                     (where ε is the elasticity coefficient)
Demand_multiplier = 1 + Δ_demand / 100
Demand_clamped = clamp(Demand_multiplier, 0.1, 2.0)

The clamp at [0.1, 2.0] is a safety boundary. Without it, a user modelling a 50% price increase with ε = -3.0 would get a raw multiplier of -0.5, or negative orders. Physics doesn’t allow negative demand.

The system models this with per-channel physics:

const DEFAULT_PHYSICS_BY_CHANNEL = {
    organic:     { ε: -0.5,  allowPriceChange: true,  applyCostChanges: true  },
    seo:         { ε: -1.5,  allowPriceChange: true,  applyCostChanges: true  },
    loyalty:     { ε:  0,    allowPriceChange: true,  applyVolumeMultiplier: false },
    marketplace: { ε:  0,    allowPriceChange: false, applyCostChanges: false },
    email:       { ε: -0.3,  allowPriceChange: true,  applyCostChanges: true  },
    affiliate:   { ε: -1.0,  allowPriceChange: true,  applyCostChanges: true  },
    direct:      { ε: -0.5,  allowPriceChange: true,  applyCostChanges: true  },
    social:      { ε: -1.0,  allowPriceChange: true,  applyCostChanges: true  },
    other:       { ε: -0.5,  allowPriceChange: true,  applyCostChanges: true  },
};

These defaults encode business judgment as coefficients. Loyalty traffic has zero elasticity because loyal customers have already decided. Marketplace traffic has zero elasticity because the marketplace sets the price. SEO has the highest magnitude because search users are actively comparing.

These defaults can be overridden at three levels: globally per channel, per individual source, or left at the hardcoded defaults. The override hierarchy uses spread:

const resolved = {
    ...DEFAULT_PHYSICS_BY_CHANNEL[source.channel],
    ...globalPhysicsOverrides?.[source.channel],
    ...source.physicsOverrides,
};

The full entity projection under a scenario combines four adjustment types. For each entity:

// 1. PRICING: adjust revenue per order, apply elasticity to demand
Price_multiplier = 1 + Δ_price / 100
Orders_adjusted = Orders_base × Demand_clamped

// 2. COSTS: adjust contribution margin
Δ_CM% = -(Δ_supplier × w_cogs + Δ_shipping × w_ship + Δ_fees × w_fee)

// 3. GROWTH: scale budget and conversion
Orders_growth = Orders_adjusted × (Budget_multiplier / 100)

// 4. CONTRIBUTION: price change flows to margin, net of percentage fees
New_CM_per_order = CM_base + Δ_price_amount × (1 - fee_rate)
Projected_CM = Projected_Orders × New_CM_per_order × (1 + Δ_CM%)

// 5. PROFIT
Projected_Profit = Projected_CM - Projected_Ad_Spend

The cost adjustment uses dynamic weights derived from the entity’s actual cost structure, not static assumptions:

w_cogs = COGS_order / (COGS_order + VSC_order)
w_ship = VSC_fixed / (COGS_order + VSC_order)
w_fee  = 1 - w_cogs - w_ship

This means a product with high COGS (a piece of furniture) responds differently to supplier cost changes than a product with high VSC (a digital download with high marketplace fees). The weights are computed from the entity’s own economics, not from a global assumption.

The goal solver

The scenario planner answers: “What happens if I change X?” The goal solver inverts the question: “What do I need to change to hit Y?”

Given a target profit and the current baseline, the solver reverse-engineers every available lever and calculates the exact adjustment needed. The algebra is simple. The insight is that every lever has a different effort cost.

Price lever:

Δ_price% = (Target - Current) / (Total_Orders × Avg_Price) × 100

If you need $500 extra profit from 100 orders at $50 average price, you need a 10% price increase. The formula is a single division.

Cost lever:

Δ_cost% = (Target - Current) / (Total_Orders × Avg_COGS) × 100

Same structure, different denominator. $500 needed, 100 orders, $20 average COGS = 25% supplier cost reduction. Harder than a 10% price increase? That depends on your negotiating position.

Budget lever (only if scaling is profitable):

Extra_Orders = (Target - Current) / Net_CM_per_order
Extra_Budget = Extra_Orders × CPA

This lever has a profitability gate. If CPA > Net CM, scaling makes things worse, not better. The solver detects this and doesn’t suggest budget increases when they’d be negative-ROI.

CVR lever:

Target_Orders = Current_Orders + (Target - Current) / Net_CM_per_order
Δ_CVR = (Target_Orders / Current_Orders - 1) × Current_CVR

The solver then ranks every suggestion by an effort score that combines implementation difficulty with business risk:

Effort = Base_Weight × Feasibility_Multiplier × log(1 + |Δ|)

CVR improvements (landing page optimisation, UX fixes) have the lowest base weight (1.0). Budget increases have the highest (3.0). The logarithmic scaling penalises extreme changes: a 5% price increase is manageable; a 50% price increase is a different business.

The result is a ranked list of pathways to the target, from “improve CVR by 0.3pp (easy)” to “raise prices by 25% (challenging)” to “increase ad budget by 400% (unfeasible)”. Each pathway can be applied directly to the scenario planner with one click, letting the user compose multiple small changes instead of making one large bet.

The solver also generates combined suggestions, stacking multiple small-effort levers to reach the target without requiring any single dramatic change:

Per_Lever_Target = Profit_Gap / N_levers
Scaled_Change_i = Individual_Change_i × (Per_Lever_Target / Individual_Impact_i)

“Raise prices 3%, reduce shipping costs 8%, and improve CVR by 0.2pp” is almost always more practical than any single lever at full stretch. The solver does the algebra so the user can focus on which levers they actually control.

The constitutional test suite

Financial software has a trust problem. If the numbers are wrong, nobody finds out until the accountant does. By then, you’ve made decisions based on lies.

ProfitOS addresses this with three layers of testing, each more paranoid than the last.

Layer 1: Golden Master Tests. A Python oracle (scripts/oracle.py) reimplements the calculation pipeline using only the financial model specification, without ever reading the TypeScript code. The TypeScript tests compare their output to the oracle’s output. If the values differ, either the TypeScript has a bug, the Python has a bug, or the spec is ambiguous. This is dual-implementation verification.

Layer 2: Property-Based Tests. Using fast-check, the test suite generates 10,000 random scenarios per invariant and verifies that mathematical properties hold for all of them:

  • Revenue is never negative
  • VSC is never negative
  • CM = AOV - COGS - VSC (before refunds)
  • Orders = Budget / (CPC / CVR)
  • PostAdProfit = Contribution - Budget
  • Reducing COGS always increases CM
  • Zero budget always means zero orders

These properties are more powerful than example-based tests because they test the shape of correctness, not specific values. They find edge cases humans never think of. They cannot be “taught” to pass specific test cases.

Layer 3: Profit Axioms. Named constitutional tests that verify the immutable laws of the system:

Axiom 1: Conservation of Mass. A fully refunded order with full product recovery MUST have a net CM of exactly -VSC. Because the revenue is gone, but the product came back (COGS recovered), and only the sunk costs (shipping, payment fees) are lost. This axiom catches the most common error in margin calculators: double-counting COGS on recovered returns.

Axiom 2: Unit Operating Leverage. Doubling the price must exactly double the percentage-based VSC (payment fees scale with price) while leaving fixed VSC unchanged (shipping costs don’t care how much the product costs). This axiom verifies that the system correctly separates percentage-based and fixed costs.

Axiom 3: Service Identity. A “Service” product must produce identical unit economics to a “Physical” product with $0 shipping and $0 pick-and-pack. This axiom proves the universality claim. The Lens changes the vocabulary. It doesn’t change the math. If this test fails, the four business models are not running on the same engine.

What broke

Everything.

The first five versions used number everywhere. The rounding errors were invisible until they weren’t. A client’s margin report showed 14.7% when the real number was 14.2%. Half a point doesn’t sound like much until you’re making ad spend decisions based on it.

The Zustand serialisation bug killed a weekend. The app worked perfectly in memory. On refresh, every price was NaN. The custom serialiser fixed it, but the deeper lesson was that JavaScript’s type system gives you no protection at persistence boundaries. You can pass a Decimal to JSON.stringify and it will happily produce garbage. No error. No warning.

The entity model went through three rewrites. The first version mixed ad-driven and organic orders in the same entity, which meant a campaign’s ROAS calculation included orders that cost nothing to acquire. The numbers looked great. They were wrong. The second version separated them but couldn’t handle weighted aggregation across multi-SKU campaigns. The third version, the current one, treats organic sources as first-class entities with their own physics.

The refund model broke every calculator I looked at before building this. Most of them apply refund rate to revenue: “5% of revenue is lost to refunds.” But that’s not how refunds work. You lose the sale (revenue goes to zero for that order) but you might get the product back (COGS is recoverable) and you definitely don’t get the shipping label back (VSC is sunk). The difference between “revenue × (1 - refund rate)” and the correct cash-flow model can be 2-3 percentage points of margin. On a $10M/year business, that’s $200-300K of phantom profit.

The multi-model attempt, building separate engines for services, subscriptions, and lead-gen, lasted two weeks before I realised the math was the same. The code was tripling, the tests were diverging, and every bug fix had to be applied in three places. The Lens system replaced all of it with 173 lines.

Intelligence without AI

ProfitOS has an AI layer. Users can bring their own API key (OpenAI, Anthropic, Google, Groq) and use it for CSV import, ad copy generation, and a chat assistant.

But the core intelligence is deliberately not AI.

The insights engine generates actionable business intelligence using pure deterministic math: CPA headroom analysis, scaling opportunity detection, portfolio concentration risk (Herfindahl-Hirschman Index), OPEX coverage analysis, and marginal ROAS per entity (“Next Best Dollar” allocation).

This is a conscious choice. The insights that matter most in business are precise. “Your CPA is $12.40 and your break-even CPA is $14.80, giving you $2.40 of headroom to scale” is not a question for an LLM. It’s arithmetic. And arithmetic doesn’t hallucinate.

The AI features handle ambiguous tasks: parsing messy CSV exports from Shopify, generating ad copy that matches a brand voice, answering natural-language questions about the dashboard. The boundary between “use AI” and “use math” is the boundary between ambiguity and precision. I’m not sure that boundary is in the right place yet. But I’m sure it exists.

Limitations

This is N=1 engineering. The universality claim, that the math of profitability is identical across business types, is demonstrated by one system modelling four business types. It has not been validated against real-world financial data from each type. The Lens system works because I chose four business models that resolve to similar structures. A marketplace business with two-sided economics (buyer fees + seller fees) would likely break the model.

The channel physics are based on heuristic defaults, not empirical measurement. An SEO elasticity of -1.5 is a reasonable assumption, not a measured coefficient. Users can override these values, but most won’t.

The entity collapse simplifies the math at the cost of debuggability. When a number looks wrong on the dashboard, you can’t just check a single record. You have to trace through the collapse: which products were weighted, which budget mode was active, which organic sources contributed.

The test suite, despite its depth, tests the engine in isolation. There are no end-to-end tests that verify the complete flow from user input through the store, through the entity builder, through the dashboard, to the rendered number on screen. The space between “the calculation is correct” and “the user sees the right number” is where most bugs live.

What I learned

The math of profitability is universal. Only the vocabulary changes.

This sounds obvious now. It wasn’t obvious when I was building separate engines for separate industries, convinced that services and products and subscriptions were fundamentally different problems. They’re not. Price minus cost equals margin. The cost might be a supplier invoice or a labour rate or a server bill. The price might be a retail price or a booking fee or a monthly subscription. The margin is the margin.

The hardest problems in financial software are not financial. They’re about types (Decimal vs. number), boundaries (serialisation, persistence, currency conversion), and abstraction (how do you collapse 50 products across 8 channels into something a human can reason about?). The actual math, the seven-stage waterfall, fits in 335 lines.

The most valuable intelligence in the system is deterministic. Not because AI is bad. Because precision is non-negotiable when people make money decisions based on your numbers.

And the most important test in the suite is Axiom 3: Service Identity. It doesn’t test a value. It tests a claim. It proves, on every run, that the vocabulary layer is pure indirection, that switching from “Product” to “Service” doesn’t change the output by a single cent. If that test ever fails, the thesis of this paper is wrong.

It hasn’t failed yet.