%% docs/cpq-subsystem.mmd
%%
%% Novel Systems — inside the CPQ engine, as merged.
%%
%% WHY THERE IS A SECOND DIAGRAM.
%%
%% docs/architecture.mmd draws the whole platform and gives CPQ four boxes:
%% the sandbox, the browser engine, the proxy route, the server engine. That is
%% the right resolution for "how does a request get from a browser to Postgres"
%% and the wrong resolution for the question a reviewer actually asked, which
%% was how the pricing subsystem is laid out internally. A review in August 2026
%% listed "CPQ subsystem layout" as a missing artifact while standing on a page
%% that served the request-flow render. It was not wrong. Zooming in on a
%% diagram drawn for a different scale does not answer a different question.
%%
%% So this file is the zoom. It draws one call to calculateQuote() and nothing
%% else: what is read, in what order the arithmetic happens, where each rounding
%% mode applies, and which of the two engines each box belongs to.
%%
%% WHAT IT IS A DRAWING OF.
%%
%% backend/src/services/cpq-engine.ts and lib/cpq-engine.ts, both at the commit
%% this was rendered from. Every constant on a node is declared in one of those
%% two files and checked by one of the three parity scripts drawn at the bottom.
%% Nothing here is aspirational and nothing here is a status — see the header of
%% architecture.mmd and scripts/check-diagram-current.mjs for why that rule
%% exists and what it cost to learn.
%%
%% THE STRUCTURAL CLAIM WORTH DRAWING.
%%
%% Two engines compute the same prices and cannot import from each other. One is
%% bundled into the browser so the marketing calculator can respond without a
%% network round trip; the other must never reach a browser, because it reads
%% rate cards and a rate card is the customer's commercial position. The
%% duplication is forced. What holds them together is the row of checks along the
%% bottom — a fixture neither engine imports, held against both. See D-002 and
%% D-010.
%%
%% Render: double-click render-diagram.command in the repository root. It renders
%% every docs/*.mmd through the Chrome already installed on the machine.
%% mermaid-cli is not used; it needs a Chromium that does not exist for
%% arm64 Linux, which is what this repository is built on.
flowchart TB
%% ── Callers ────────────────────────────────────────────────────────────
subgraph callers["Two callers, two engines, one arithmetic"]
direction LR
sandbox["CPQ sandbox
components/cpq — marketing page
no network, no rate card"]
apicall["POST /api/v1/quotes/calculate
Bearer JWT · ADMIN · SALES"]
end
browserEngine["lib/cpq-engine.ts
bundled to the browser
list prices only, published in the module"]
serverEngine["services/cpq-engine.ts — calculateQuote()
never bundled to a browser"]
%% ── What the server reads before it can price anything ─────────────────
subgraph inputs["Resolved before pricing, inside withTenant()"]
direction LR
skus[("inventory_skus
costPerUnitCents · yieldFactor · type")]
tiers[("labor_tiers
hourlyRateCents · multiplier")]
end
%% ── The gate ───────────────────────────────────────────────────────────
%% Drawn before the arithmetic because that is where it runs. A margin that
%% cannot be honoured is refused before a single line item is costed.
gate{{"Margin gate — before any price exists
appliedMargin > 0.95 → 400
appliedMargin < 0.50 without an approved override → 400, not a clamp
a quote that silently differs from the request is worse than an error"}}
%% ── Per line item ──────────────────────────────────────────────────────
subgraph line["calculateLineItem() — once per line"]
direction TB
validate["assertDimensions · assertSkuType
6" floor · 144" width · 180" drop · 500 per line
fabric is FABRIC, motor is MOTOR — types are checked, not assumed"]
area["openingSquareFeet = W × D ÷ 144
the divisor is written out, not pre-computed"]
subgraph components["Component costs — each rounded half-up to the cent"]
direction TB
fabric["fabric · sq ft
opening × yieldFactor × 1.15 × qty
waste applied on top of yield, not folded into it,
so a contractor can argue with either"]
hardware["hardware · each
yieldFactor × qty"]
motor["motor · each (optional)
yieldFactor × qty"]
trim["trim · linear in (optional)
width × yieldFactor × qty"]
labour["labour · hours
0.75/unit + 0.05/sq ft + 0.5 if motorised
rate = hourlyRateCents × tier multiplier
the multiplier scales the rate, never the hours —
a costly market pays more, it does not take longer"]
end
cost["costCents = Σ already-rounded components"]
price["priceCents = roundCentsUp( cost ÷ (1 − margin) )
costs are measurements and round to the nearest;
this is a constraint and rounds away from the violation"]
end
%% ── Cut list, deliberately off to the side ─────────────────────────────
cutlist["calculateCutList( W, D )
tube = W − 1.25 · hembar = W − 1.00
fabric = (W − 1.50) × (D + 4.00 wrap)
derived from the finished opening, never from a costed intermediate"]
cutnote["A work order needs cut sizes and has no business
asking for a price to get them. Coupling a shop-floor fact
to a commercial one is how the two come apart."]
%% ── Totals and the assertion ───────────────────────────────────────────
totals["totals = Σ line items
the sum of rounded lines, not a re-rounding of the unrounded sum —
the lines on the printed proposal have to add up to the total under them"]
assertion{{"realisedMargin(total) < 0.50 → 400
should be unreachable
which is why it is here: it catches a future change to
the rounding direction, the line pricing or the totalling"}}
result["QuoteCalculation
lineItems · components · cutList · totals · assumptions
marginFloor and appliedMargin are both in the response"]
%% ── Downstream ─────────────────────────────────────────────────────────
persist["POST /api/v1/quotes → withTenant()
one transaction, one declared tenant"]
approved(["quote.approved
after commit"])
downstream["webhook dispatcher · integration fan-out
drawn in full at /architecture.png"]
%% ── Flows ──────────────────────────────────────────────────────────────
sandbox --> browserEngine
apicall --> serverEngine
skus --> serverEngine
tiers --> serverEngine
serverEngine --> gate
gate -->|"margin accepted"| validate
validate --> area
area --> fabric
area --> labour
validate --> hardware
validate --> motor
validate --> trim
fabric --> cost
hardware --> cost
motor --> cost
trim --> cost
labour --> cost
cost ==> price
validate ==>|"from W and D alone"| cutlist
cutlist -.- cutnote
price --> totals
cutlist --> result
totals --> assertion
assertion -->|"holds"| result
result --> persist
persist --> approved
approved --> downstream
%% ── What holds the two engines together ────────────────────────────────
subgraph parity["The two engines cannot import each other, so this is what pins them"]
direction LR
fixture[("fixtures/cut-list-parity.json
read by both checks, imported by neither engine
pins the values, not the constants")]
cutcheck["check:cutlist-parity
both files declare the same four allowances"]
valuecheck["check:cpq-parity + backend/test/cpq-engine.test.ts
each engine reproduces the fixture's numbers"]
margincheck["check:margin-policy
both files declare MARGIN_FLOOR 0.50"]
end
paritynote["Identical constants still produce different cut sizes if one engine
transforms its input before subtracting from it. That is not hypothetical:
the browser engine snapped to the quarter inch and cut a 30.1" opening
to 29.00" where the API cut 28.85" — from constants that agreed.
So the fixture pins the answers."]
browserEngine -.- cutcheck
serverEngine -.- cutcheck
browserEngine -.- valuecheck
serverEngine -.- valuecheck
browserEngine -.- margincheck
serverEngine -.- margincheck
cutcheck -.- fixture
valuecheck -.- fixture
fixture -.- paritynote
%% ── Styling ────────────────────────────────────────────────────────────
classDef engine fill:#eef2ff,stroke:#4338ca,color:#312e81
classDef boundary fill:#fee2e2,stroke:#b91c1c,stroke-width:2px,color:#7f1d1d
classDef store fill:#f0fdf4,stroke:#15803d,color:#14532d
classDef annotation fill:#f8fafc,stroke:#94a3b8,stroke-dasharray:3 3,color:#475569
classDef arith fill:#fffbeb,stroke:#b45309,color:#78350f
class browserEngine,serverEngine engine
class gate,assertion,price,persist boundary
class skus,tiers,fixture store
class cutnote,paritynote annotation
class fabric,hardware,motor,trim,labour,area,cost,totals,cutlist arith