Use for ANY change to pricing values, brackets, fees, or discounts — pricing is DB-authoritative, so editing constants.js alone silently ships nothing.
Install
npx skillscat add wavespestcontrolfl/waves-customer-portal/pricing-config Install via the SkillsCat registry.
Pricing changes are DB-authoritative
server/services/pricing-engine/db-bridge.js (syncConstantsFromDB) loadspricing_config rows OVER the in-code constants at runtime. In any
environment that carries the row (prod does), editingserver/services/pricing-engine/constants.js alone changes nothing.
Checklist for a pricing change
constants.js — update the in-code default (keeps fresh envs correct).
Migration — update the
pricing_configrow. Read-modify-write so
admin edits to other keys in the same row survive; insert apricing_config_auditrow with reason + changelog identity. Exemplar:server/models/migrations/20260611000003_pest_footprint_1750_bracket.js.Admin seed — if the value is admin-editable, make sure the admin
seed/panel (client/src/components/admin/PricingLogicPanel.jsx) reflects
the new shape.Client mirrors — search for static copies in the client estimators and
update them manually; they do NOT read pricing_config. Known offender
class:client/src/lib/estimateEngine.jskeeps static price mirrors
(e.g.oneTimeMosquitoLadderPrice).
Use ripgrep (recursivegrepis banned in this monorepo):rg -n "<the old value or constant name>" client/src server/servicesVerify after deploy — confirm the migration ran and the live
estimator (/pest-control-calculator/flow or admin estimate builder)
shows the new value.
Migration / engine traps
pricing_changelog.version_from/tois varchar(10) and holds ENGINE
versions only;categoryhas a CHECK constraint
(bug/leak/rule/cost/architecture/documentation/infrastructure).- Engine input is FLAT —
generateEstimate({ homeSqFt, ... }), never{ property: { ... } }; the sqft adapter key isattachedGarage
(nothasAttachedGarage). - A pricing migration's
down()keys off its OWN audit row, never a
blanket revert. Direct prodpricing_configUPDATEs are blocked —
always ship via migration. - Regression baselines regenerate via
cd server && CAPTURE_BASELINE=1 npx jest tests/pricing-engine.regression.test.js
(the suite lives in theserverworkspace), then hand-apply the delta
topricing-engine.baseline.json. - Known client-mirror sites:
client/src/lib/estimateEngine.js(mosquito ×2 blocks),EstimateToolViewV2.jsx(approx preview),PortalPage,EstimatePage.jsx.
Mosquito price changes must sync ALL of them in the SAME PR, and the
pre-push suite set must includeclient-estimate-engine-pricing-driftmosquito-estimator-adapter.
Standing owner rulings (dated — don't relitigate)
- No pricing floors (2026-07-17): all floors are DISARMED; margins are
report-only. Never re-add a floor or flag "below floor". - Lawn IS WaveGuard-tier-discountable (2026-07-28): the old
"lawn non-discountable" rule is DEAD — never re-flagdiscount-engine.jslawn eligibility. - Lawn 12x/app never exceeds 9x/app: runtime cap
min(prev, floor(enh × 12/9))(LAWN_PRICING_V2_LADDER_CAP+ client
lawnLookup mirror). - Quarterly lawn and the basic/4-app tier are retired for NEW sales
(accept-time 409retired_lawn_cadence_selection); legacy plans keep
billing as sold. - Pest cadence curve (quarterly 1.00 / bi-monthly 0.88 / monthly 0.78)
applies to RECURRING pest only — one-time pest anchors on the
undiscounted quarterly base. The curve is code-only; the oldpricing_config.pest_frequencyrow is inert/removed — never rewire it
or add an admin editor for it. - Mosquito recurring rate is settled (2026-07-26, +10% ≈ 60% margin) —
never re-cut it. - ≥5-unit condo/association stacks price as Multifamily COMMERCIAL;
don't lowerAGGREGATE_MIN_UNITS(5).
Related guardrails
- Marketing pages never hardcode dollar amounts — link to
/pest-control-calculator/instead. - Services without a catalog price stay blank — never default to $0.00.
- The $99 WaveGuard membership fee applies to SOLO recurring pest or SOLO
recurring mosquito only (bundle = no fee; never on lawn/T&S/termite) —
see waves-billing invariant 9 for the code authority.