Vibecode Hive CPQ
track this build4 phases, 12 steps, beginner friendly0%The mechanical core of CPQ is not mysterious: a product tree, option groups, compatibility rules, a pricing formula, a PDF at the end. An agent can produce a working configurator for one product family in a weekend, and if you sell a few hundred SKUs with predictable option logic, that build may genuinely be enough. What you are not one-shotting is the part that makes CPQ a purchase: ERP and CRM sync, dealer accounts with their own price lists and discounts, 3D or 2D visual previews, multi-language catalogs, and someone maintaining the rule set when engineering changes a hinge. Also worth being honest that CPQ failure is expensive: a bad rule ships a quote that cannot be manufactured at the price you promised. So: buildable, and a bad idea to trust for anything you actually invoice on until the rules have been beaten on for months.
You are building a lean indie version of Hive CPQ. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Hive CPQ · indie build Configure, price and quote for manufactured products, where the core is a constraint solver over option rules that must explain why something is unavailable, and the discipline is that a quote is a priced snapshot that cannot silently change after it is sent. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Rules | Declarative constraints over options, evaluated by a solver | hand-written conditionals become unmaintainable at a few hundred rules | | Explanation | Every exclusion traceable to the rules that caused it | an option greyed out with no reason is the top complaint about every configurator | | Pricing | Exact decimal with a priced snapshot per quote | a quote whose price moves after sending is a dispute | | Versioning | Rules and price lists versioned, quotes pinned to a version | the catalogue changes weekly and quotes live for months | | Runtime | TypeScript with PostgreSQL | the work is rules, money and documents | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the server, the solver and the document renderer. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **PostgreSQL 16 or newer** · free locally - Why: Versioned rules, price lists and immutable quotes want real constraints. - Get it: Docker or your package manager. - Verify: psql connects and an insert-only table can be enforced - [ ] **An exact decimal arithmetic library** · free - Why: Percentage discounts over rounded line items produce arithmetic a customer will dispute. - Get it: Any well-tested decimal library for your runtime. - Verify: Line items sum exactly to a discounted total - [ ] **A real product with at least fifty option rules** · free, and it takes a conversation with whoever knows the product - Why: Contradiction detection, explanation quality and solver performance are all uninteresting below a few dozen rules and become the whole problem above a hundred. - Get it: Take a real configurable product and write out its rules, including the ones the sales team keep in their heads. - Verify: Fifty rules exist, including at least one pair that turns out to contradict - [ ] **Your rounding and discount rules** · free - Why: Where rounding happens and whether discounts apply per line or to the total are decisions that change the number, and reconstructing them later from disputed quotes is worse. - Get it: Decide the rounding point, the discount order and the approval threshold, and write them down before pricing anything. - Verify: A written note states the rounding point, discount order and approval threshold ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Manufacturing and resource planning integration. - Three-dimensional product visualisation. - Dealer portals with per-partner pricing. - Tax, shipping and cross-border compliance. - ERP and CRM integrations, so accepted quotes become copy-paste work - A dealer or reseller portal with per-account price lists, discount tiers and order history - Visual product previews, 3D or otherwise, which is often the thing that closes the sale - Anyone but you maintaining the rule engine when the catalog changes - Version control on quotes and catalogs, audit trails, and the boring guarantees a buyer expects when a quote is contractual If one of those is essential to you, that is the reason to keep paying for Hive CPQ, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Hive CPQ The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a product configurator and quote builder where an impossible configuration cannot be quoted to replace Hive CPQ. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Rules: Declarative constraints over options, evaluated by a solver · hand-written conditionals become unmaintainable at a few hundred rules - Explanation: Every exclusion traceable to the rules that caused it · an option greyed out with no reason is the top complaint about every configurator - Pricing: Exact decimal with a priced snapshot per quote · a quote whose price moves after sending is a dispute - Versioning: Rules and price lists versioned, quotes pinned to a version · the catalogue changes weekly and quotes live for months - Runtime: TypeScript with PostgreSQL · the work is rules, money and documents ### Data model (create this before Phase 1) - products(id, name, active_from, active_to) - options(id, product_id, group_id, code, name, active) - rules(id, product_id, version, kind, expression_json, message) · kind is requires, excludes, implies or constrains - rule_versions(product_id, version, published_at, published_by) - price_lists(id, product_id, version, currency, valid_from, valid_to) - prices(price_list_id, option_id, amount_minor, kind) · kind is base, per_unit or surcharge - quotes(id, customer_ref, product_id, rule_version, price_list_id, configuration_json, total_minor, currency, valid_until, status, sent_at) - A quote pins the rule version and price list it was built from, because both will have changed by the time the customer replies. ### Phase 1 · Rules as data, not code Build: Model option groups and constraints as data: requires, excludes, implies and numeric constraints. Evaluate a configuration against them. Detect contradictory rules at publish time rather than when a customer hits them. Trap: Expressing configuration rules as conditional code means every catalogue change is a deployment, and after two hundred rules nobody can tell whether a new one contradicts an existing one. Keep them as data, and check the rule set for contradictions and unreachable options when it is published. Done when: Rules are data and a new constraint can be added without deploying. A contradictory pair is detected at publish time, tested with a crafted pair. An option no configuration can ever select is reported as unreachable. Evaluating a configuration against two hundred rules is fast enough to be interactive. Do not build yet: No explanation, pricing or quotes. ### Phase 2 · Saying why an option is unavailable Build: When evaluating a configuration, record which rules excluded each unavailable option. Present the reason in the interface. Offer the change that would make it available. Trap: Greying out an option without a reason is the single most common complaint about configurators, because the user cannot tell whether it is genuinely unavailable or whether they made a wrong choice three steps earlier. Track the excluding rules during evaluation rather than recomputing an explanation afterwards, since the second version drifts from the first. Done when: Every unavailable option names the rule and the selected choice that excluded it. The suggested change to make it available is offered and works when applied. Explanations come from the same evaluation as the exclusion, verified by inspecting the code path. A configuration with several conflicting selections explains all of them rather than only the first. Do not build yet: No pricing or quotes yet. ### Phase 3 · Pricing a configuration exactly Build: Price from a versioned price list with base, per-unit and surcharge components. Use exact decimal arithmetic. Handle quantity breaks, currency, and options whose price depends on another selection. Trap: Applying a percentage discount to a total computed from rounded line items gives a different answer from discounting each line, and a customer checking your arithmetic will find the discrepancy. Decide where rounding happens, apply it once, and state the rule. Done when: A configuration prices identically on repeated evaluation. Quantity breaks apply at the correct thresholds, tested at the boundary. Dependent pricing resolves in a stated order. Line items sum exactly to the total with a documented rounding point, asserted in a test. Do not build yet: No quotes or documents yet. ### Phase 4 · Quotes that do not change after sending Build: Create a quote pinning the rule version, price list and full configuration. Set a validity period. Make a sent quote immutable, with changes producing a new revision rather than editing the original. Trap: A quote that references the current price list rather than a pinned one reprices itself when the catalogue is updated, and the customer accepts a figure that no longer matches what you sent. Pin everything at creation, and make a sent quote a snapshot rather than a view. Done when: A quote reprices identically after the price list changes, tested by publishing a new list. A sent quote cannot be edited; changes create a revision. The validity period is enforced and an expired quote cannot be accepted without a recorded decision. Rebuilding a quote from its pinned versions reproduces the same total. Do not build yet: No documents or approvals yet. ### Phase 5 · Documents, approvals and handing off to production Build: Render a quote document reproducibly from its pinned data. Require approval above a discount threshold. Produce a production-ready bill of materials from the configuration so what was sold is what gets built. Trap: A bill of materials derived separately from the configuration will diverge from what was quoted, and the factory builds something the customer did not buy. Derive it from the same configuration and rule version the quote was priced from, and assert the two agree. Done when: A quote document renders identically from stored data, verified by checksum. A discount above the threshold requires approval before the quote can be sent. The bill of materials derives from the same configuration as the price, asserted in a test. A configuration that is valid but unbuildable is caught before the quote is sent. Do not build yet: Nothing after this. ### Out of scope (and why) - Integration with manufacturing and resource planning systems, which is where most of the hosted product's implementation effort actually goes. - Three-dimensional visualisation of the configured product, which is a substantial graphics project on its own. - Dealer portals with per-partner pricing and permissions, which is a whole access model on top of this. - Tax, shipping and cross-border compliance, which is specialist work that varies by jurisdiction. ### README must contain - That rules are data so a catalogue change is not a deployment - That contradictory and unreachable rules are detected at publish rather than by a customer - That every unavailable option names the rule and choice that excluded it - That a quote pins its rule version and price list so it cannot reprice itself - That the bill of materials derives from the same configuration as the price ===== AGENTS.md ===== # Agent instructions · Hive CPQ indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Declarative constraints over options, evaluated by a solver, Every exclusion traceable to the rules that caused it, Exact decimal with a priced snapshot per quote, Rules and price lists versioned, quotes pinned to a version, TypeScript with PostgreSQL. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Expressing configuration rules as conditional code means every catalogue change is a deployment, and after two hundred rules nobody can tell whether a new one contradicts an existing one. Keep them as data, and check the rule set for contradictions and unreachable options when it is published. - Greying out an option without a reason is the single most common complaint about configurators, because the user cannot tell whether it is genuinely unavailable or whether they made a wrong choice three steps earlier. Track the excluding rules during evaluation rather than recomputing an explanation afterwards, since the second version drifts from the first. - Applying a percentage discount to a total computed from rounded line items gives a different answer from discounting each line, and a customer checking your arithmetic will find the discrepancy. Decide where rounding happens, apply it once, and state the rule. - A quote that references the current price list rather than a pinned one reprices itself when the catalogue is updated, and the customer accepts a figure that no longer matches what you sent. Pin everything at creation, and make a sent quote a snapshot rather than a view. ===== BUILD_PLAN.md ===== # Build plan · Hive CPQ Configure, price and quote for manufactured products, where the core is a constraint solver over option rules that must explain why something is unavailable, and the discipline is that a quote is a priced snapshot that cannot silently change after it is sent. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Rules as data, not code Option constraints are declarative and can be changed without deploying. ### Steps 1. Model constraints as data Four kinds cover most of it. Code means every change is a deployment. Files: `src/rules.ts` 2. Detect contradictions at publish Before a customer finds them, which is the alternative. Files: `src/analyse.ts`, `src/analyse.test.ts` 3. Report unreachable options An option nothing can select is a rule mistake, not a product decision. Files: `src/analyse.ts` ### Done when - [ ] Rules are data and change without deploying - [ ] Contradictory rules are detected at publish - [ ] Unreachable options are reported - [ ] Two hundred rules evaluate interactively ### Watch out - Expressing configuration rules as conditional code means every catalogue change is a deployment, and after two hundred rules nobody can tell whether a new one contradicts an existing one. Keep them as data, and check the rule set for contradictions and unreachable options when it is published. ## Phase 2 · Saying why an option is unavailable Every disabled option can explain which choice disabled it. ### Steps 1. Record exclusions during evaluation Not afterwards. A separate explanation path drifts from the real one. Files: `src/evaluate.ts` 2. Name the rule and the triggering choice Both. The rule alone does not tell the user what to change. Files: `src/explain.ts` 3. Offer the change that would help And make applying it actually work. Files: `src/suggest.ts` ### Done when - [ ] Unavailable options name their excluding rule and choice - [ ] The suggested change works when applied - [ ] Explanations come from the evaluation itself - [ ] Several conflicts are all explained ### Watch out - Greying out an option without a reason is the single most common complaint about configurators, because the user cannot tell whether it is genuinely unavailable or whether they made a wrong choice three steps earlier. Track the excluding rules during evaluation rather than recomputing an explanation afterwards, since the second version drifts from the first. ## Phase 3 · Pricing a configuration exactly A configured product prices correctly including the awkward parts. ### Steps 1. Use exact decimal throughout Percentages on rounded lines produce arithmetic customers dispute. Files: `src/money.ts` 2. Test quantity breaks at the boundary The threshold itself is where off-by-one pricing lives. Files: `src/pricing.test.ts` 3. Define one rounding point And assert lines sum exactly to the total. Files: `src/pricing.ts` ### Done when - [ ] Repeated pricing gives an identical result - [ ] Quantity breaks apply correctly at boundaries - [ ] Dependent pricing resolves in a stated order - [ ] Lines sum exactly to the total ### Watch out - Applying a percentage discount to a total computed from rounded line items gives a different answer from discounting each line, and a customer checking your arithmetic will find the discrepancy. Decide where rounding happens, apply it once, and state the rule. ## Phase 4 · Quotes that do not change after sending What the customer received is what you can still see months later. ### Steps 1. Pin rule version and price list At creation. Referencing current means the quote changes itself. Files: `src/quotes.ts` 2. Make sent quotes immutable Revisions, not edits. The customer has a copy. Files: `src/quotes.ts`, `tests/immutable.test.ts` 3. Enforce validity An expired quote accepted at the old price is a loss you chose by accident. Files: `src/validity.ts` ### Done when - [ ] A quote reprices identically after a catalogue change - [ ] A sent quote cannot be edited - [ ] Expired quotes require a recorded decision - [ ] Rebuilding from pinned versions reproduces the total ### Watch out - A quote that references the current price list rather than a pinned one reprices itself when the catalogue is updated, and the customer accepts a figure that no longer matches what you sent. Pin everything at creation, and make a sent quote a snapshot rather than a view. ## Not in this build - Manufacturing and resource planning integration. - Three-dimensional product visualisation. - Dealer portals with per-partner pricing. - Tax, shipping and cross-border compliance. ## After v1, if you want it - Guided selling that orders questions by what constrains most, which shortens configuration - Quote comparison for a customer weighing two configurations - Rule change impact analysis, showing which existing quotes a new rule would invalidate ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Rules, prices and customer quotes. DATABASE_URL=postgres://localhost:5432/cpq # Required. Where rounding happens. Applied once, stated on the quote. ROUNDING_POINT=line_item # Required. Above this a quote needs approval before sending. DISCOUNT_APPROVAL_THRESHOLD_BPS=1000 # Required. How long a quote holds its price. Pinned data makes this enforceable. QUOTE_VALIDITY_DAYS=30 # Required. Configuration must stay interactive. A slow solver is an abandoned configurator. SOLVER_TIMEOUT_MS=200 # Required. Present as a constant so the answer is visible. Revisions, not edits. ALLOW_EDIT_SENT_QUOTES=never
You are building a lean indie version of Hive CPQ. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Hive CPQ · indie build Configure, price and quote for manufactured products, where the core is a constraint solver over option rules that must explain why something is unavailable, and the discipline is that a quote is a priced snapshot that cannot silently change after it is sent. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Rules | Declarative constraints over options, evaluated by a solver | hand-written conditionals become unmaintainable at a few hundred rules | | Explanation | Every exclusion traceable to the rules that caused it | an option greyed out with no reason is the top complaint about every configurator | | Pricing | Exact decimal with a priced snapshot per quote | a quote whose price moves after sending is a dispute | | Versioning | Rules and price lists versioned, quotes pinned to a version | the catalogue changes weekly and quotes live for months | | Runtime | TypeScript with PostgreSQL | the work is rules, money and documents | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the server, the solver and the document renderer. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **PostgreSQL 16 or newer** · free locally - Why: Versioned rules, price lists and immutable quotes want real constraints. - Get it: Docker or your package manager. - Verify: psql connects and an insert-only table can be enforced - [ ] **An exact decimal arithmetic library** · free - Why: Percentage discounts over rounded line items produce arithmetic a customer will dispute. - Get it: Any well-tested decimal library for your runtime. - Verify: Line items sum exactly to a discounted total - [ ] **A real product with at least fifty option rules** · free, and it takes a conversation with whoever knows the product - Why: Contradiction detection, explanation quality and solver performance are all uninteresting below a few dozen rules and become the whole problem above a hundred. - Get it: Take a real configurable product and write out its rules, including the ones the sales team keep in their heads. - Verify: Fifty rules exist, including at least one pair that turns out to contradict - [ ] **Your rounding and discount rules** · free - Why: Where rounding happens and whether discounts apply per line or to the total are decisions that change the number, and reconstructing them later from disputed quotes is worse. - Get it: Decide the rounding point, the discount order and the approval threshold, and write them down before pricing anything. - Verify: A written note states the rounding point, discount order and approval threshold ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Manufacturing and resource planning integration. - Three-dimensional product visualisation. - Dealer portals with per-partner pricing. - Tax, shipping and cross-border compliance. - ERP and CRM integrations, so accepted quotes become copy-paste work - A dealer or reseller portal with per-account price lists, discount tiers and order history - Visual product previews, 3D or otherwise, which is often the thing that closes the sale - Anyone but you maintaining the rule engine when the catalog changes - Version control on quotes and catalogs, audit trails, and the boring guarantees a buyer expects when a quote is contractual If one of those is essential to you, that is the reason to keep paying for Hive CPQ, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Hive CPQ The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a product configurator and quote builder where an impossible configuration cannot be quoted to replace Hive CPQ. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Rules: Declarative constraints over options, evaluated by a solver · hand-written conditionals become unmaintainable at a few hundred rules - Explanation: Every exclusion traceable to the rules that caused it · an option greyed out with no reason is the top complaint about every configurator - Pricing: Exact decimal with a priced snapshot per quote · a quote whose price moves after sending is a dispute - Versioning: Rules and price lists versioned, quotes pinned to a version · the catalogue changes weekly and quotes live for months - Runtime: TypeScript with PostgreSQL · the work is rules, money and documents ### Data model (create this before Phase 1) - products(id, name, active_from, active_to) - options(id, product_id, group_id, code, name, active) - rules(id, product_id, version, kind, expression_json, message) · kind is requires, excludes, implies or constrains - rule_versions(product_id, version, published_at, published_by) - price_lists(id, product_id, version, currency, valid_from, valid_to) - prices(price_list_id, option_id, amount_minor, kind) · kind is base, per_unit or surcharge - quotes(id, customer_ref, product_id, rule_version, price_list_id, configuration_json, total_minor, currency, valid_until, status, sent_at) - A quote pins the rule version and price list it was built from, because both will have changed by the time the customer replies. ### Phase 1 · Rules as data, not code Build: Model option groups and constraints as data: requires, excludes, implies and numeric constraints. Evaluate a configuration against them. Detect contradictory rules at publish time rather than when a customer hits them. Trap: Expressing configuration rules as conditional code means every catalogue change is a deployment, and after two hundred rules nobody can tell whether a new one contradicts an existing one. Keep them as data, and check the rule set for contradictions and unreachable options when it is published. Done when: Rules are data and a new constraint can be added without deploying. A contradictory pair is detected at publish time, tested with a crafted pair. An option no configuration can ever select is reported as unreachable. Evaluating a configuration against two hundred rules is fast enough to be interactive. Do not build yet: No explanation, pricing or quotes. ### Phase 2 · Saying why an option is unavailable Build: When evaluating a configuration, record which rules excluded each unavailable option. Present the reason in the interface. Offer the change that would make it available. Trap: Greying out an option without a reason is the single most common complaint about configurators, because the user cannot tell whether it is genuinely unavailable or whether they made a wrong choice three steps earlier. Track the excluding rules during evaluation rather than recomputing an explanation afterwards, since the second version drifts from the first. Done when: Every unavailable option names the rule and the selected choice that excluded it. The suggested change to make it available is offered and works when applied. Explanations come from the same evaluation as the exclusion, verified by inspecting the code path. A configuration with several conflicting selections explains all of them rather than only the first. Do not build yet: No pricing or quotes yet. ### Phase 3 · Pricing a configuration exactly Build: Price from a versioned price list with base, per-unit and surcharge components. Use exact decimal arithmetic. Handle quantity breaks, currency, and options whose price depends on another selection. Trap: Applying a percentage discount to a total computed from rounded line items gives a different answer from discounting each line, and a customer checking your arithmetic will find the discrepancy. Decide where rounding happens, apply it once, and state the rule. Done when: A configuration prices identically on repeated evaluation. Quantity breaks apply at the correct thresholds, tested at the boundary. Dependent pricing resolves in a stated order. Line items sum exactly to the total with a documented rounding point, asserted in a test. Do not build yet: No quotes or documents yet. ### Phase 4 · Quotes that do not change after sending Build: Create a quote pinning the rule version, price list and full configuration. Set a validity period. Make a sent quote immutable, with changes producing a new revision rather than editing the original. Trap: A quote that references the current price list rather than a pinned one reprices itself when the catalogue is updated, and the customer accepts a figure that no longer matches what you sent. Pin everything at creation, and make a sent quote a snapshot rather than a view. Done when: A quote reprices identically after the price list changes, tested by publishing a new list. A sent quote cannot be edited; changes create a revision. The validity period is enforced and an expired quote cannot be accepted without a recorded decision. Rebuilding a quote from its pinned versions reproduces the same total. Do not build yet: No documents or approvals yet. ### Phase 5 · Documents, approvals and handing off to production Build: Render a quote document reproducibly from its pinned data. Require approval above a discount threshold. Produce a production-ready bill of materials from the configuration so what was sold is what gets built. Trap: A bill of materials derived separately from the configuration will diverge from what was quoted, and the factory builds something the customer did not buy. Derive it from the same configuration and rule version the quote was priced from, and assert the two agree. Done when: A quote document renders identically from stored data, verified by checksum. A discount above the threshold requires approval before the quote can be sent. The bill of materials derives from the same configuration as the price, asserted in a test. A configuration that is valid but unbuildable is caught before the quote is sent. Do not build yet: Nothing after this. ### Out of scope (and why) - Integration with manufacturing and resource planning systems, which is where most of the hosted product's implementation effort actually goes. - Three-dimensional visualisation of the configured product, which is a substantial graphics project on its own. - Dealer portals with per-partner pricing and permissions, which is a whole access model on top of this. - Tax, shipping and cross-border compliance, which is specialist work that varies by jurisdiction. ### README must contain - That rules are data so a catalogue change is not a deployment - That contradictory and unreachable rules are detected at publish rather than by a customer - That every unavailable option names the rule and choice that excluded it - That a quote pins its rule version and price list so it cannot reprice itself - That the bill of materials derives from the same configuration as the price ===== AGENTS.md ===== # Agent instructions · Hive CPQ indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Declarative constraints over options, evaluated by a solver, Every exclusion traceable to the rules that caused it, Exact decimal with a priced snapshot per quote, Rules and price lists versioned, quotes pinned to a version, TypeScript with PostgreSQL. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Expressing configuration rules as conditional code means every catalogue change is a deployment, and after two hundred rules nobody can tell whether a new one contradicts an existing one. Keep them as data, and check the rule set for contradictions and unreachable options when it is published. - Greying out an option without a reason is the single most common complaint about configurators, because the user cannot tell whether it is genuinely unavailable or whether they made a wrong choice three steps earlier. Track the excluding rules during evaluation rather than recomputing an explanation afterwards, since the second version drifts from the first. - Applying a percentage discount to a total computed from rounded line items gives a different answer from discounting each line, and a customer checking your arithmetic will find the discrepancy. Decide where rounding happens, apply it once, and state the rule. - A quote that references the current price list rather than a pinned one reprices itself when the catalogue is updated, and the customer accepts a figure that no longer matches what you sent. Pin everything at creation, and make a sent quote a snapshot rather than a view. ===== BUILD_PLAN.md ===== # Build plan · Hive CPQ Configure, price and quote for manufactured products, where the core is a constraint solver over option rules that must explain why something is unavailable, and the discipline is that a quote is a priced snapshot that cannot silently change after it is sent. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Rules as data, not code Option constraints are declarative and can be changed without deploying. ### Steps 1. Model constraints as data Four kinds cover most of it. Code means every change is a deployment. Files: `src/rules.ts` 2. Detect contradictions at publish Before a customer finds them, which is the alternative. Files: `src/analyse.ts`, `src/analyse.test.ts` 3. Report unreachable options An option nothing can select is a rule mistake, not a product decision. Files: `src/analyse.ts` ### Done when - [ ] Rules are data and change without deploying - [ ] Contradictory rules are detected at publish - [ ] Unreachable options are reported - [ ] Two hundred rules evaluate interactively ### Watch out - Expressing configuration rules as conditional code means every catalogue change is a deployment, and after two hundred rules nobody can tell whether a new one contradicts an existing one. Keep them as data, and check the rule set for contradictions and unreachable options when it is published. ## Phase 2 · Saying why an option is unavailable Every disabled option can explain which choice disabled it. ### Steps 1. Record exclusions during evaluation Not afterwards. A separate explanation path drifts from the real one. Files: `src/evaluate.ts` 2. Name the rule and the triggering choice Both. The rule alone does not tell the user what to change. Files: `src/explain.ts` 3. Offer the change that would help And make applying it actually work. Files: `src/suggest.ts` ### Done when - [ ] Unavailable options name their excluding rule and choice - [ ] The suggested change works when applied - [ ] Explanations come from the evaluation itself - [ ] Several conflicts are all explained ### Watch out - Greying out an option without a reason is the single most common complaint about configurators, because the user cannot tell whether it is genuinely unavailable or whether they made a wrong choice three steps earlier. Track the excluding rules during evaluation rather than recomputing an explanation afterwards, since the second version drifts from the first. ## Phase 3 · Pricing a configuration exactly A configured product prices correctly including the awkward parts. ### Steps 1. Use exact decimal throughout Percentages on rounded lines produce arithmetic customers dispute. Files: `src/money.ts` 2. Test quantity breaks at the boundary The threshold itself is where off-by-one pricing lives. Files: `src/pricing.test.ts` 3. Define one rounding point And assert lines sum exactly to the total. Files: `src/pricing.ts` ### Done when - [ ] Repeated pricing gives an identical result - [ ] Quantity breaks apply correctly at boundaries - [ ] Dependent pricing resolves in a stated order - [ ] Lines sum exactly to the total ### Watch out - Applying a percentage discount to a total computed from rounded line items gives a different answer from discounting each line, and a customer checking your arithmetic will find the discrepancy. Decide where rounding happens, apply it once, and state the rule. ## Phase 4 · Quotes that do not change after sending What the customer received is what you can still see months later. ### Steps 1. Pin rule version and price list At creation. Referencing current means the quote changes itself. Files: `src/quotes.ts` 2. Make sent quotes immutable Revisions, not edits. The customer has a copy. Files: `src/quotes.ts`, `tests/immutable.test.ts` 3. Enforce validity An expired quote accepted at the old price is a loss you chose by accident. Files: `src/validity.ts` ### Done when - [ ] A quote reprices identically after a catalogue change - [ ] A sent quote cannot be edited - [ ] Expired quotes require a recorded decision - [ ] Rebuilding from pinned versions reproduces the total ### Watch out - A quote that references the current price list rather than a pinned one reprices itself when the catalogue is updated, and the customer accepts a figure that no longer matches what you sent. Pin everything at creation, and make a sent quote a snapshot rather than a view. ## Not in this build - Manufacturing and resource planning integration. - Three-dimensional product visualisation. - Dealer portals with per-partner pricing. - Tax, shipping and cross-border compliance. ## After v1, if you want it - Guided selling that orders questions by what constrains most, which shortens configuration - Quote comparison for a customer weighing two configurations - Rule change impact analysis, showing which existing quotes a new rule would invalidate ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Rules, prices and customer quotes. DATABASE_URL=postgres://localhost:5432/cpq # Required. Where rounding happens. Applied once, stated on the quote. ROUNDING_POINT=line_item # Required. Above this a quote needs approval before sending. DISCOUNT_APPROVAL_THRESHOLD_BPS=1000 # Required. How long a quote holds its price. Pinned data makes this enforceable. QUOTE_VALIDITY_DAYS=30 # Required. Configuration must stay interactive. A slow solver is an abandoned configurator. SOLVER_TIMEOUT_MS=200 # Required. Present as a constant so the answer is visible. Revisions, not edits. ALLOW_EDIT_SENT_QUOTES=never
You are building a production product version of Hive CPQ. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== PRODUCT.md ===== # Hive CPQ · product brief ## Problem The mechanical core of CPQ is not mysterious: a product tree, option groups, compatibility rules, a pricing formula, a PDF at the end. An agent can produce a working configurator for one product family in a weekend, and if you sell a few hundred SKUs with predictable option logic, that build may genuinely be enough. What you are not one-shotting is the part that makes CPQ a purchase: ERP and CRM sync, dealer accounts with their own price lists and discounts, 3D or 2D visual previews, multi-language catalogs, and someone maintaining the rule set when engineering changes a hinge. Also worth being honest that CPQ failure is expensive: a bad rule ships a quote that cannot be manufactured at the price you promised. So: buildable, and a bad idea to trust for anything you actually invoice on until the rules have been beaten on for months. ## Product outcome A configurator that can always say why an option is unavailable, a quote that cannot reprice itself after you sent it, and a bill of materials that matches what the customer actually bought. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - Your own product rules written down: option groups, incompatibilities, required combinations - A pricing model you can express as formulas, base price plus option deltas plus margin - Node 20 and a place to run it, local or a small VPS - Manual re-entry of accepted quotes into whatever ERP or accounting system you actually use ## Explicit non-goals for v1 - Manufacturing and resource planning integration. - Three-dimensional product visualisation. - Dealer portals with per-partner pricing. - Tax, shipping and cross-border compliance. - ERP and CRM integrations, so accepted quotes become copy-paste work - A dealer or reseller portal with per-account price lists, discount tiers and order history - Visual product previews, 3D or otherwise, which is often the thing that closes the sale - Anyone but you maintaining the rule engine when the catalog changes - Version control on quotes and catalogs, audit trails, and the boring guarantees a buyer expects when a quote is contractual ## Success criteria - Contradictory rules are confirmed detected at publish - Unavailable options are confirmed to name their excluding rule - Line items are confirmed to sum exactly to the total - A quote is confirmed to reprice identically after a catalogue change - The bill of materials is confirmed to match the priced configuration ===== BRIEF.md ===== # Build brief · Hive CPQ The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a product configurator and quote builder where an impossible configuration cannot be quoted to replace Hive CPQ. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Rules: Declarative constraints over options, evaluated by a solver · hand-written conditionals become unmaintainable at a few hundred rules - Explanation: Every exclusion traceable to the rules that caused it · an option greyed out with no reason is the top complaint about every configurator - Pricing: Exact decimal with a priced snapshot per quote · a quote whose price moves after sending is a dispute - Versioning: Rules and price lists versioned, quotes pinned to a version · the catalogue changes weekly and quotes live for months - Runtime: TypeScript with PostgreSQL · the work is rules, money and documents ### Data model (create this before Phase 1) - products(id, name, active_from, active_to) - options(id, product_id, group_id, code, name, active) - rules(id, product_id, version, kind, expression_json, message) · kind is requires, excludes, implies or constrains - rule_versions(product_id, version, published_at, published_by) - price_lists(id, product_id, version, currency, valid_from, valid_to) - prices(price_list_id, option_id, amount_minor, kind) · kind is base, per_unit or surcharge - quotes(id, customer_ref, product_id, rule_version, price_list_id, configuration_json, total_minor, currency, valid_until, status, sent_at) - A quote pins the rule version and price list it was built from, because both will have changed by the time the customer replies. ### Phase 1 · Rules as data, not code Build: Model option groups and constraints as data: requires, excludes, implies and numeric constraints. Evaluate a configuration against them. Detect contradictory rules at publish time rather than when a customer hits them. Trap: Expressing configuration rules as conditional code means every catalogue change is a deployment, and after two hundred rules nobody can tell whether a new one contradicts an existing one. Keep them as data, and check the rule set for contradictions and unreachable options when it is published. Done when: Rules are data and a new constraint can be added without deploying. A contradictory pair is detected at publish time, tested with a crafted pair. An option no configuration can ever select is reported as unreachable. Evaluating a configuration against two hundred rules is fast enough to be interactive. Do not build yet: No explanation, pricing or quotes. ### Phase 2 · Saying why an option is unavailable Build: When evaluating a configuration, record which rules excluded each unavailable option. Present the reason in the interface. Offer the change that would make it available. Trap: Greying out an option without a reason is the single most common complaint about configurators, because the user cannot tell whether it is genuinely unavailable or whether they made a wrong choice three steps earlier. Track the excluding rules during evaluation rather than recomputing an explanation afterwards, since the second version drifts from the first. Done when: Every unavailable option names the rule and the selected choice that excluded it. The suggested change to make it available is offered and works when applied. Explanations come from the same evaluation as the exclusion, verified by inspecting the code path. A configuration with several conflicting selections explains all of them rather than only the first. Do not build yet: No pricing or quotes yet. ### Phase 3 · Pricing a configuration exactly Build: Price from a versioned price list with base, per-unit and surcharge components. Use exact decimal arithmetic. Handle quantity breaks, currency, and options whose price depends on another selection. Trap: Applying a percentage discount to a total computed from rounded line items gives a different answer from discounting each line, and a customer checking your arithmetic will find the discrepancy. Decide where rounding happens, apply it once, and state the rule. Done when: A configuration prices identically on repeated evaluation. Quantity breaks apply at the correct thresholds, tested at the boundary. Dependent pricing resolves in a stated order. Line items sum exactly to the total with a documented rounding point, asserted in a test. Do not build yet: No quotes or documents yet. ### Phase 4 · Quotes that do not change after sending Build: Create a quote pinning the rule version, price list and full configuration. Set a validity period. Make a sent quote immutable, with changes producing a new revision rather than editing the original. Trap: A quote that references the current price list rather than a pinned one reprices itself when the catalogue is updated, and the customer accepts a figure that no longer matches what you sent. Pin everything at creation, and make a sent quote a snapshot rather than a view. Done when: A quote reprices identically after the price list changes, tested by publishing a new list. A sent quote cannot be edited; changes create a revision. The validity period is enforced and an expired quote cannot be accepted without a recorded decision. Rebuilding a quote from its pinned versions reproduces the same total. Do not build yet: No documents or approvals yet. ### Phase 5 · Documents, approvals and handing off to production Build: Render a quote document reproducibly from its pinned data. Require approval above a discount threshold. Produce a production-ready bill of materials from the configuration so what was sold is what gets built. Trap: A bill of materials derived separately from the configuration will diverge from what was quoted, and the factory builds something the customer did not buy. Derive it from the same configuration and rule version the quote was priced from, and assert the two agree. Done when: A quote document renders identically from stored data, verified by checksum. A discount above the threshold requires approval before the quote can be sent. The bill of materials derives from the same configuration as the price, asserted in a test. A configuration that is valid but unbuildable is caught before the quote is sent. Do not build yet: Nothing after this. ### Out of scope (and why) - Integration with manufacturing and resource planning systems, which is where most of the hosted product's implementation effort actually goes. - Three-dimensional visualisation of the configured product, which is a substantial graphics project on its own. - Dealer portals with per-partner pricing and permissions, which is a whole access model on top of this. - Tax, shipping and cross-border compliance, which is specialist work that varies by jurisdiction. ### README must contain - That rules are data so a catalogue change is not a deployment - That contradictory and unreachable rules are detected at publish rather than by a customer - That every unavailable option names the rule and choice that excluded it - That a quote pins its rule version and price list so it cannot reprice itself - That the bill of materials derives from the same configuration as the price ===== ARCHITECTURE.md ===== # Architecture · Hive CPQ ## Stack | Part | Choice | Why | | --- | --- | --- | | Rules | Declarative constraints over options, evaluated by a solver | hand-written conditionals become unmaintainable at a few hundred rules | | Explanation | Every exclusion traceable to the rules that caused it | an option greyed out with no reason is the top complaint about every configurator | | Pricing | Exact decimal with a priced snapshot per quote | a quote whose price moves after sending is a dispute | | Versioning | Rules and price lists versioned, quotes pinned to a version | the catalogue changes weekly and quotes live for months | | Runtime | TypeScript with PostgreSQL | the work is rules, money and documents | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Rules | Declarative constraints and publish-time analysis. | Any representation, provided rules are data rather than code. | | Explanation | Exclusion reasons from the evaluation itself. | Any presentation, provided it is not a second computation that can drift. | | Pricing | Exact arithmetic with one rounding point. | Any implementation, provided lines sum exactly to the total. | | Quotes | Pinned versions and immutability after sending. | Not swappable for live references, which make a quote reprice itself. | | Handoff | Bill of materials from the priced configuration. | Any generator, provided it derives from the same configuration. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `DATABASE_URL` · required, secret · Rules, prices and customer quotes. - `ROUNDING_POINT` · required · Where rounding happens. Applied once, stated on the quote. - `DISCOUNT_APPROVAL_THRESHOLD_BPS` · required · Above this a quote needs approval before sending. - `QUOTE_VALIDITY_DAYS` · required · How long a quote holds its price. Pinned data makes this enforceable. - `SOLVER_TIMEOUT_MS` · required · Configuration must stay interactive. A slow solver is an abandoned configurator. - `ALLOW_EDIT_SENT_QUOTES` · required · Present as a constant so the answer is visible. Revisions, not edits. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it. ===== AGENTS.md ===== # Agent instructions · Hive CPQ product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Declarative constraints over options, evaluated by a solver, Every exclusion traceable to the rules that caused it, Exact decimal with a priced snapshot per quote, Rules and price lists versioned, quotes pinned to a version, TypeScript with PostgreSQL. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Expressing configuration rules as conditional code means every catalogue change is a deployment, and after two hundred rules nobody can tell whether a new one contradicts an existing one. Keep them as data, and check the rule set for contradictions and unreachable options when it is published. - Greying out an option without a reason is the single most common complaint about configurators, because the user cannot tell whether it is genuinely unavailable or whether they made a wrong choice three steps earlier. Track the excluding rules during evaluation rather than recomputing an explanation afterwards, since the second version drifts from the first. - Applying a percentage discount to a total computed from rounded line items gives a different answer from discounting each line, and a customer checking your arithmetic will find the discrepancy. Decide where rounding happens, apply it once, and state the rule. - A quote that references the current price list rather than a pinned one reprices itself when the catalogue is updated, and the customer accepts a figure that no longer matches what you sent. Pin everything at creation, and make a sent quote a snapshot rather than a view. - A bill of materials derived separately from the configuration will diverge from what was quoted, and the factory builds something the customer did not buy. Derive it from the same configuration and rule version the quote was priced from, and assert the two agree. ===== MILESTONES.md ===== # Delivery milestones · Hive CPQ Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Rules as data, not code Option constraints are declarative and can be changed without deploying. ### Steps 1. Model constraints as data Four kinds cover most of it. Code means every change is a deployment. Files: `src/rules.ts` 2. Detect contradictions at publish Before a customer finds them, which is the alternative. Files: `src/analyse.ts`, `src/analyse.test.ts` 3. Report unreachable options An option nothing can select is a rule mistake, not a product decision. Files: `src/analyse.ts` ### Done when - [ ] Rules are data and change without deploying - [ ] Contradictory rules are detected at publish - [ ] Unreachable options are reported - [ ] Two hundred rules evaluate interactively ### Watch out - Expressing configuration rules as conditional code means every catalogue change is a deployment, and after two hundred rules nobody can tell whether a new one contradicts an existing one. Keep them as data, and check the rule set for contradictions and unreachable options when it is published. ## M2 · Saying why an option is unavailable Every disabled option can explain which choice disabled it. ### Steps 1. Record exclusions during evaluation Not afterwards. A separate explanation path drifts from the real one. Files: `src/evaluate.ts` 2. Name the rule and the triggering choice Both. The rule alone does not tell the user what to change. Files: `src/explain.ts` 3. Offer the change that would help And make applying it actually work. Files: `src/suggest.ts` ### Done when - [ ] Unavailable options name their excluding rule and choice - [ ] The suggested change works when applied - [ ] Explanations come from the evaluation itself - [ ] Several conflicts are all explained ### Watch out - Greying out an option without a reason is the single most common complaint about configurators, because the user cannot tell whether it is genuinely unavailable or whether they made a wrong choice three steps earlier. Track the excluding rules during evaluation rather than recomputing an explanation afterwards, since the second version drifts from the first. ## M3 · Pricing a configuration exactly A configured product prices correctly including the awkward parts. ### Steps 1. Use exact decimal throughout Percentages on rounded lines produce arithmetic customers dispute. Files: `src/money.ts` 2. Test quantity breaks at the boundary The threshold itself is where off-by-one pricing lives. Files: `src/pricing.test.ts` 3. Define one rounding point And assert lines sum exactly to the total. Files: `src/pricing.ts` ### Done when - [ ] Repeated pricing gives an identical result - [ ] Quantity breaks apply correctly at boundaries - [ ] Dependent pricing resolves in a stated order - [ ] Lines sum exactly to the total ### Watch out - Applying a percentage discount to a total computed from rounded line items gives a different answer from discounting each line, and a customer checking your arithmetic will find the discrepancy. Decide where rounding happens, apply it once, and state the rule. ## M4 · Quotes that do not change after sending What the customer received is what you can still see months later. ### Steps 1. Pin rule version and price list At creation. Referencing current means the quote changes itself. Files: `src/quotes.ts` 2. Make sent quotes immutable Revisions, not edits. The customer has a copy. Files: `src/quotes.ts`, `tests/immutable.test.ts` 3. Enforce validity An expired quote accepted at the old price is a loss you chose by accident. Files: `src/validity.ts` ### Done when - [ ] A quote reprices identically after a catalogue change - [ ] A sent quote cannot be edited - [ ] Expired quotes require a recorded decision - [ ] Rebuilding from pinned versions reproduces the total ### Watch out - A quote that references the current price list rather than a pinned one reprices itself when the catalogue is updated, and the customer accepts a figure that no longer matches what you sent. Pin everything at creation, and make a sent quote a snapshot rather than a view. ## M5 · Documents, approvals and handing off to production (production only) A quote becomes a document, an approval and something the factory can build. ### Steps 1. Render reproducibly from pinned data The document a customer holds must be reproducible exactly. Files: `src/document.ts` 2. Gate discounts on approval Above a threshold, recorded, before sending. Files: `src/approvals.ts` 3. Derive the bill of materials from the same configuration A separate derivation diverges and the factory builds the wrong thing. Files: `src/bom.ts`, `tests/bom-agreement.test.ts` ### Done when - [ ] Documents render identically from stored data - [ ] Discounts above threshold require approval - [ ] The bill of materials matches the priced configuration - [ ] An unbuildable configuration is caught before sending ### Watch out - A bill of materials derived separately from the configuration will diverge from what was quoted, and the factory builds something the customer did not buy. Derive it from the same configuration and rule version the quote was priced from, and assert the two agree. ===== OPERATIONS.md ===== # Operations · Hive CPQ ## Backup Quotes are commercial commitments and rule versions are what make them reproducible. Back up both, and keep old rule versions rather than pruning them, since a quote from six months ago needs its version to be rebuildable. ## Restore Restore and confirm quotes still rebuild to their original totals, since a restore that loses an old price list leaves quotes that cannot be reproduced and therefore cannot be defended. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Solver evaluation time and the rate of configurations abandoned at a particular option. The second usually identifies a rule that is confusing rather than a product that is unpopular. ## Incident checklist A quote whose total changed after sending is the failure that reaches a customer, and it means something referenced the current catalogue rather than the pinned version. The pinned data is what makes the case answerable. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] Contradictory rules are confirmed detected at publish - [ ] Unavailable options are confirmed to name their excluding rule - [ ] Line items are confirmed to sum exactly to the total - [ ] A quote is confirmed to reprice identically after a catalogue change - [ ] The bill of materials is confirmed to match the priced configuration ## Launch constraint Do not market omitted Hive CPQ capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered. ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Rules, prices and customer quotes. DATABASE_URL=postgres://localhost:5432/cpq # Required. Where rounding happens. Applied once, stated on the quote. ROUNDING_POINT=line_item # Required. Above this a quote needs approval before sending. DISCOUNT_APPROVAL_THRESHOLD_BPS=1000 # Required. How long a quote holds its price. Pinned data makes this enforceable. QUOTE_VALIDITY_DAYS=30 # Required. Configuration must stay interactive. A slow solver is an abandoned configurator. SOLVER_TIMEOUT_MS=200 # Required. Present as a constant so the answer is visible. Revisions, not edits. ALLOW_EDIT_SENT_QUOTES=never
# Hive CPQ · indie build Configure, price and quote for manufactured products, where the core is a constraint solver over option rules that must explain why something is unavailable, and the discipline is that a quote is a priced snapshot that cannot silently change after it is sent. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Rules | Declarative constraints over options, evaluated by a solver | hand-written conditionals become unmaintainable at a few hundred rules | | Explanation | Every exclusion traceable to the rules that caused it | an option greyed out with no reason is the top complaint about every configurator | | Pricing | Exact decimal with a priced snapshot per quote | a quote whose price moves after sending is a dispute | | Versioning | Rules and price lists versioned, quotes pinned to a version | the catalogue changes weekly and quotes live for months | | Runtime | TypeScript with PostgreSQL | the work is rules, money and documents | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the server, the solver and the document renderer. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **PostgreSQL 16 or newer** · free locally - Why: Versioned rules, price lists and immutable quotes want real constraints. - Get it: Docker or your package manager. - Verify: psql connects and an insert-only table can be enforced - [ ] **An exact decimal arithmetic library** · free - Why: Percentage discounts over rounded line items produce arithmetic a customer will dispute. - Get it: Any well-tested decimal library for your runtime. - Verify: Line items sum exactly to a discounted total - [ ] **A real product with at least fifty option rules** · free, and it takes a conversation with whoever knows the product - Why: Contradiction detection, explanation quality and solver performance are all uninteresting below a few dozen rules and become the whole problem above a hundred. - Get it: Take a real configurable product and write out its rules, including the ones the sales team keep in their heads. - Verify: Fifty rules exist, including at least one pair that turns out to contradict - [ ] **Your rounding and discount rules** · free - Why: Where rounding happens and whether discounts apply per line or to the total are decisions that change the number, and reconstructing them later from disputed quotes is worse. - Get it: Decide the rounding point, the discount order and the approval threshold, and write them down before pricing anything. - Verify: A written note states the rounding point, discount order and approval threshold ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Manufacturing and resource planning integration. - Three-dimensional product visualisation. - Dealer portals with per-partner pricing. - Tax, shipping and cross-border compliance. - ERP and CRM integrations, so accepted quotes become copy-paste work - A dealer or reseller portal with per-account price lists, discount tiers and order history - Visual product previews, 3D or otherwise, which is often the thing that closes the sale - Anyone but you maintaining the rule engine when the catalog changes - Version control on quotes and catalogs, audit trails, and the boring guarantees a buyer expects when a quote is contractual If one of those is essential to you, that is the reason to keep paying for Hive CPQ, and the README should say so rather than pretend.
# Build brief · Hive CPQ The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a product configurator and quote builder where an impossible configuration cannot be quoted to replace Hive CPQ. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Rules: Declarative constraints over options, evaluated by a solver · hand-written conditionals become unmaintainable at a few hundred rules - Explanation: Every exclusion traceable to the rules that caused it · an option greyed out with no reason is the top complaint about every configurator - Pricing: Exact decimal with a priced snapshot per quote · a quote whose price moves after sending is a dispute - Versioning: Rules and price lists versioned, quotes pinned to a version · the catalogue changes weekly and quotes live for months - Runtime: TypeScript with PostgreSQL · the work is rules, money and documents ### Data model (create this before Phase 1) - products(id, name, active_from, active_to) - options(id, product_id, group_id, code, name, active) - rules(id, product_id, version, kind, expression_json, message) · kind is requires, excludes, implies or constrains - rule_versions(product_id, version, published_at, published_by) - price_lists(id, product_id, version, currency, valid_from, valid_to) - prices(price_list_id, option_id, amount_minor, kind) · kind is base, per_unit or surcharge - quotes(id, customer_ref, product_id, rule_version, price_list_id, configuration_json, total_minor, currency, valid_until, status, sent_at) - A quote pins the rule version and price list it was built from, because both will have changed by the time the customer replies. ### Phase 1 · Rules as data, not code Build: Model option groups and constraints as data: requires, excludes, implies and numeric constraints. Evaluate a configuration against them. Detect contradictory rules at publish time rather than when a customer hits them. Trap: Expressing configuration rules as conditional code means every catalogue change is a deployment, and after two hundred rules nobody can tell whether a new one contradicts an existing one. Keep them as data, and check the rule set for contradictions and unreachable options when it is published. Done when: Rules are data and a new constraint can be added without deploying. A contradictory pair is detected at publish time, tested with a crafted pair. An option no configuration can ever select is reported as unreachable. Evaluating a configuration against two hundred rules is fast enough to be interactive. Do not build yet: No explanation, pricing or quotes. ### Phase 2 · Saying why an option is unavailable Build: When evaluating a configuration, record which rules excluded each unavailable option. Present the reason in the interface. Offer the change that would make it available. Trap: Greying out an option without a reason is the single most common complaint about configurators, because the user cannot tell whether it is genuinely unavailable or whether they made a wrong choice three steps earlier. Track the excluding rules during evaluation rather than recomputing an explanation afterwards, since the second version drifts from the first. Done when: Every unavailable option names the rule and the selected choice that excluded it. The suggested change to make it available is offered and works when applied. Explanations come from the same evaluation as the exclusion, verified by inspecting the code path. A configuration with several conflicting selections explains all of them rather than only the first. Do not build yet: No pricing or quotes yet. ### Phase 3 · Pricing a configuration exactly Build: Price from a versioned price list with base, per-unit and surcharge components. Use exact decimal arithmetic. Handle quantity breaks, currency, and options whose price depends on another selection. Trap: Applying a percentage discount to a total computed from rounded line items gives a different answer from discounting each line, and a customer checking your arithmetic will find the discrepancy. Decide where rounding happens, apply it once, and state the rule. Done when: A configuration prices identically on repeated evaluation. Quantity breaks apply at the correct thresholds, tested at the boundary. Dependent pricing resolves in a stated order. Line items sum exactly to the total with a documented rounding point, asserted in a test. Do not build yet: No quotes or documents yet. ### Phase 4 · Quotes that do not change after sending Build: Create a quote pinning the rule version, price list and full configuration. Set a validity period. Make a sent quote immutable, with changes producing a new revision rather than editing the original. Trap: A quote that references the current price list rather than a pinned one reprices itself when the catalogue is updated, and the customer accepts a figure that no longer matches what you sent. Pin everything at creation, and make a sent quote a snapshot rather than a view. Done when: A quote reprices identically after the price list changes, tested by publishing a new list. A sent quote cannot be edited; changes create a revision. The validity period is enforced and an expired quote cannot be accepted without a recorded decision. Rebuilding a quote from its pinned versions reproduces the same total. Do not build yet: No documents or approvals yet. ### Phase 5 · Documents, approvals and handing off to production Build: Render a quote document reproducibly from its pinned data. Require approval above a discount threshold. Produce a production-ready bill of materials from the configuration so what was sold is what gets built. Trap: A bill of materials derived separately from the configuration will diverge from what was quoted, and the factory builds something the customer did not buy. Derive it from the same configuration and rule version the quote was priced from, and assert the two agree. Done when: A quote document renders identically from stored data, verified by checksum. A discount above the threshold requires approval before the quote can be sent. The bill of materials derives from the same configuration as the price, asserted in a test. A configuration that is valid but unbuildable is caught before the quote is sent. Do not build yet: Nothing after this. ### Out of scope (and why) - Integration with manufacturing and resource planning systems, which is where most of the hosted product's implementation effort actually goes. - Three-dimensional visualisation of the configured product, which is a substantial graphics project on its own. - Dealer portals with per-partner pricing and permissions, which is a whole access model on top of this. - Tax, shipping and cross-border compliance, which is specialist work that varies by jurisdiction. ### README must contain - That rules are data so a catalogue change is not a deployment - That contradictory and unreachable rules are detected at publish rather than by a customer - That every unavailable option names the rule and choice that excluded it - That a quote pins its rule version and price list so it cannot reprice itself - That the bill of materials derives from the same configuration as the price
# Agent instructions · Hive CPQ indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Declarative constraints over options, evaluated by a solver, Every exclusion traceable to the rules that caused it, Exact decimal with a priced snapshot per quote, Rules and price lists versioned, quotes pinned to a version, TypeScript with PostgreSQL. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Expressing configuration rules as conditional code means every catalogue change is a deployment, and after two hundred rules nobody can tell whether a new one contradicts an existing one. Keep them as data, and check the rule set for contradictions and unreachable options when it is published. - Greying out an option without a reason is the single most common complaint about configurators, because the user cannot tell whether it is genuinely unavailable or whether they made a wrong choice three steps earlier. Track the excluding rules during evaluation rather than recomputing an explanation afterwards, since the second version drifts from the first. - Applying a percentage discount to a total computed from rounded line items gives a different answer from discounting each line, and a customer checking your arithmetic will find the discrepancy. Decide where rounding happens, apply it once, and state the rule. - A quote that references the current price list rather than a pinned one reprices itself when the catalogue is updated, and the customer accepts a figure that no longer matches what you sent. Pin everything at creation, and make a sent quote a snapshot rather than a view.
# Build plan · Hive CPQ Configure, price and quote for manufactured products, where the core is a constraint solver over option rules that must explain why something is unavailable, and the discipline is that a quote is a priced snapshot that cannot silently change after it is sent. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Rules as data, not code Option constraints are declarative and can be changed without deploying. ### Steps 1. Model constraints as data Four kinds cover most of it. Code means every change is a deployment. Files: `src/rules.ts` 2. Detect contradictions at publish Before a customer finds them, which is the alternative. Files: `src/analyse.ts`, `src/analyse.test.ts` 3. Report unreachable options An option nothing can select is a rule mistake, not a product decision. Files: `src/analyse.ts` ### Done when - [ ] Rules are data and change without deploying - [ ] Contradictory rules are detected at publish - [ ] Unreachable options are reported - [ ] Two hundred rules evaluate interactively ### Watch out - Expressing configuration rules as conditional code means every catalogue change is a deployment, and after two hundred rules nobody can tell whether a new one contradicts an existing one. Keep them as data, and check the rule set for contradictions and unreachable options when it is published. ## Phase 2 · Saying why an option is unavailable Every disabled option can explain which choice disabled it. ### Steps 1. Record exclusions during evaluation Not afterwards. A separate explanation path drifts from the real one. Files: `src/evaluate.ts` 2. Name the rule and the triggering choice Both. The rule alone does not tell the user what to change. Files: `src/explain.ts` 3. Offer the change that would help And make applying it actually work. Files: `src/suggest.ts` ### Done when - [ ] Unavailable options name their excluding rule and choice - [ ] The suggested change works when applied - [ ] Explanations come from the evaluation itself - [ ] Several conflicts are all explained ### Watch out - Greying out an option without a reason is the single most common complaint about configurators, because the user cannot tell whether it is genuinely unavailable or whether they made a wrong choice three steps earlier. Track the excluding rules during evaluation rather than recomputing an explanation afterwards, since the second version drifts from the first. ## Phase 3 · Pricing a configuration exactly A configured product prices correctly including the awkward parts. ### Steps 1. Use exact decimal throughout Percentages on rounded lines produce arithmetic customers dispute. Files: `src/money.ts` 2. Test quantity breaks at the boundary The threshold itself is where off-by-one pricing lives. Files: `src/pricing.test.ts` 3. Define one rounding point And assert lines sum exactly to the total. Files: `src/pricing.ts` ### Done when - [ ] Repeated pricing gives an identical result - [ ] Quantity breaks apply correctly at boundaries - [ ] Dependent pricing resolves in a stated order - [ ] Lines sum exactly to the total ### Watch out - Applying a percentage discount to a total computed from rounded line items gives a different answer from discounting each line, and a customer checking your arithmetic will find the discrepancy. Decide where rounding happens, apply it once, and state the rule. ## Phase 4 · Quotes that do not change after sending What the customer received is what you can still see months later. ### Steps 1. Pin rule version and price list At creation. Referencing current means the quote changes itself. Files: `src/quotes.ts` 2. Make sent quotes immutable Revisions, not edits. The customer has a copy. Files: `src/quotes.ts`, `tests/immutable.test.ts` 3. Enforce validity An expired quote accepted at the old price is a loss you chose by accident. Files: `src/validity.ts` ### Done when - [ ] A quote reprices identically after a catalogue change - [ ] A sent quote cannot be edited - [ ] Expired quotes require a recorded decision - [ ] Rebuilding from pinned versions reproduces the total ### Watch out - A quote that references the current price list rather than a pinned one reprices itself when the catalogue is updated, and the customer accepts a figure that no longer matches what you sent. Pin everything at creation, and make a sent quote a snapshot rather than a view. ## Not in this build - Manufacturing and resource planning integration. - Three-dimensional product visualisation. - Dealer portals with per-partner pricing. - Tax, shipping and cross-border compliance. ## After v1, if you want it - Guided selling that orders questions by what constrains most, which shortens configuration - Quote comparison for a customer weighing two configurations - Rule change impact analysis, showing which existing quotes a new rule would invalidate
# Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Rules, prices and customer quotes. DATABASE_URL=postgres://localhost:5432/cpq # Required. Where rounding happens. Applied once, stated on the quote. ROUNDING_POINT=line_item # Required. Above this a quote needs approval before sending. DISCOUNT_APPROVAL_THRESHOLD_BPS=1000 # Required. How long a quote holds its price. Pinned data makes this enforceable. QUOTE_VALIDITY_DAYS=30 # Required. Configuration must stay interactive. A slow solver is an abandoned configurator. SOLVER_TIMEOUT_MS=200 # Required. Present as a constant so the answer is visible. Revisions, not edits. ALLOW_EDIT_SENT_QUOTES=never
# Hive CPQ · product brief ## Problem The mechanical core of CPQ is not mysterious: a product tree, option groups, compatibility rules, a pricing formula, a PDF at the end. An agent can produce a working configurator for one product family in a weekend, and if you sell a few hundred SKUs with predictable option logic, that build may genuinely be enough. What you are not one-shotting is the part that makes CPQ a purchase: ERP and CRM sync, dealer accounts with their own price lists and discounts, 3D or 2D visual previews, multi-language catalogs, and someone maintaining the rule set when engineering changes a hinge. Also worth being honest that CPQ failure is expensive: a bad rule ships a quote that cannot be manufactured at the price you promised. So: buildable, and a bad idea to trust for anything you actually invoice on until the rules have been beaten on for months. ## Product outcome A configurator that can always say why an option is unavailable, a quote that cannot reprice itself after you sent it, and a bill of materials that matches what the customer actually bought. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - Your own product rules written down: option groups, incompatibilities, required combinations - A pricing model you can express as formulas, base price plus option deltas plus margin - Node 20 and a place to run it, local or a small VPS - Manual re-entry of accepted quotes into whatever ERP or accounting system you actually use ## Explicit non-goals for v1 - Manufacturing and resource planning integration. - Three-dimensional product visualisation. - Dealer portals with per-partner pricing. - Tax, shipping and cross-border compliance. - ERP and CRM integrations, so accepted quotes become copy-paste work - A dealer or reseller portal with per-account price lists, discount tiers and order history - Visual product previews, 3D or otherwise, which is often the thing that closes the sale - Anyone but you maintaining the rule engine when the catalog changes - Version control on quotes and catalogs, audit trails, and the boring guarantees a buyer expects when a quote is contractual ## Success criteria - Contradictory rules are confirmed detected at publish - Unavailable options are confirmed to name their excluding rule - Line items are confirmed to sum exactly to the total - A quote is confirmed to reprice identically after a catalogue change - The bill of materials is confirmed to match the priced configuration
# Build brief · Hive CPQ The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a product configurator and quote builder where an impossible configuration cannot be quoted to replace Hive CPQ. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Rules: Declarative constraints over options, evaluated by a solver · hand-written conditionals become unmaintainable at a few hundred rules - Explanation: Every exclusion traceable to the rules that caused it · an option greyed out with no reason is the top complaint about every configurator - Pricing: Exact decimal with a priced snapshot per quote · a quote whose price moves after sending is a dispute - Versioning: Rules and price lists versioned, quotes pinned to a version · the catalogue changes weekly and quotes live for months - Runtime: TypeScript with PostgreSQL · the work is rules, money and documents ### Data model (create this before Phase 1) - products(id, name, active_from, active_to) - options(id, product_id, group_id, code, name, active) - rules(id, product_id, version, kind, expression_json, message) · kind is requires, excludes, implies or constrains - rule_versions(product_id, version, published_at, published_by) - price_lists(id, product_id, version, currency, valid_from, valid_to) - prices(price_list_id, option_id, amount_minor, kind) · kind is base, per_unit or surcharge - quotes(id, customer_ref, product_id, rule_version, price_list_id, configuration_json, total_minor, currency, valid_until, status, sent_at) - A quote pins the rule version and price list it was built from, because both will have changed by the time the customer replies. ### Phase 1 · Rules as data, not code Build: Model option groups and constraints as data: requires, excludes, implies and numeric constraints. Evaluate a configuration against them. Detect contradictory rules at publish time rather than when a customer hits them. Trap: Expressing configuration rules as conditional code means every catalogue change is a deployment, and after two hundred rules nobody can tell whether a new one contradicts an existing one. Keep them as data, and check the rule set for contradictions and unreachable options when it is published. Done when: Rules are data and a new constraint can be added without deploying. A contradictory pair is detected at publish time, tested with a crafted pair. An option no configuration can ever select is reported as unreachable. Evaluating a configuration against two hundred rules is fast enough to be interactive. Do not build yet: No explanation, pricing or quotes. ### Phase 2 · Saying why an option is unavailable Build: When evaluating a configuration, record which rules excluded each unavailable option. Present the reason in the interface. Offer the change that would make it available. Trap: Greying out an option without a reason is the single most common complaint about configurators, because the user cannot tell whether it is genuinely unavailable or whether they made a wrong choice three steps earlier. Track the excluding rules during evaluation rather than recomputing an explanation afterwards, since the second version drifts from the first. Done when: Every unavailable option names the rule and the selected choice that excluded it. The suggested change to make it available is offered and works when applied. Explanations come from the same evaluation as the exclusion, verified by inspecting the code path. A configuration with several conflicting selections explains all of them rather than only the first. Do not build yet: No pricing or quotes yet. ### Phase 3 · Pricing a configuration exactly Build: Price from a versioned price list with base, per-unit and surcharge components. Use exact decimal arithmetic. Handle quantity breaks, currency, and options whose price depends on another selection. Trap: Applying a percentage discount to a total computed from rounded line items gives a different answer from discounting each line, and a customer checking your arithmetic will find the discrepancy. Decide where rounding happens, apply it once, and state the rule. Done when: A configuration prices identically on repeated evaluation. Quantity breaks apply at the correct thresholds, tested at the boundary. Dependent pricing resolves in a stated order. Line items sum exactly to the total with a documented rounding point, asserted in a test. Do not build yet: No quotes or documents yet. ### Phase 4 · Quotes that do not change after sending Build: Create a quote pinning the rule version, price list and full configuration. Set a validity period. Make a sent quote immutable, with changes producing a new revision rather than editing the original. Trap: A quote that references the current price list rather than a pinned one reprices itself when the catalogue is updated, and the customer accepts a figure that no longer matches what you sent. Pin everything at creation, and make a sent quote a snapshot rather than a view. Done when: A quote reprices identically after the price list changes, tested by publishing a new list. A sent quote cannot be edited; changes create a revision. The validity period is enforced and an expired quote cannot be accepted without a recorded decision. Rebuilding a quote from its pinned versions reproduces the same total. Do not build yet: No documents or approvals yet. ### Phase 5 · Documents, approvals and handing off to production Build: Render a quote document reproducibly from its pinned data. Require approval above a discount threshold. Produce a production-ready bill of materials from the configuration so what was sold is what gets built. Trap: A bill of materials derived separately from the configuration will diverge from what was quoted, and the factory builds something the customer did not buy. Derive it from the same configuration and rule version the quote was priced from, and assert the two agree. Done when: A quote document renders identically from stored data, verified by checksum. A discount above the threshold requires approval before the quote can be sent. The bill of materials derives from the same configuration as the price, asserted in a test. A configuration that is valid but unbuildable is caught before the quote is sent. Do not build yet: Nothing after this. ### Out of scope (and why) - Integration with manufacturing and resource planning systems, which is where most of the hosted product's implementation effort actually goes. - Three-dimensional visualisation of the configured product, which is a substantial graphics project on its own. - Dealer portals with per-partner pricing and permissions, which is a whole access model on top of this. - Tax, shipping and cross-border compliance, which is specialist work that varies by jurisdiction. ### README must contain - That rules are data so a catalogue change is not a deployment - That contradictory and unreachable rules are detected at publish rather than by a customer - That every unavailable option names the rule and choice that excluded it - That a quote pins its rule version and price list so it cannot reprice itself - That the bill of materials derives from the same configuration as the price
# Architecture · Hive CPQ ## Stack | Part | Choice | Why | | --- | --- | --- | | Rules | Declarative constraints over options, evaluated by a solver | hand-written conditionals become unmaintainable at a few hundred rules | | Explanation | Every exclusion traceable to the rules that caused it | an option greyed out with no reason is the top complaint about every configurator | | Pricing | Exact decimal with a priced snapshot per quote | a quote whose price moves after sending is a dispute | | Versioning | Rules and price lists versioned, quotes pinned to a version | the catalogue changes weekly and quotes live for months | | Runtime | TypeScript with PostgreSQL | the work is rules, money and documents | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Rules | Declarative constraints and publish-time analysis. | Any representation, provided rules are data rather than code. | | Explanation | Exclusion reasons from the evaluation itself. | Any presentation, provided it is not a second computation that can drift. | | Pricing | Exact arithmetic with one rounding point. | Any implementation, provided lines sum exactly to the total. | | Quotes | Pinned versions and immutability after sending. | Not swappable for live references, which make a quote reprice itself. | | Handoff | Bill of materials from the priced configuration. | Any generator, provided it derives from the same configuration. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `DATABASE_URL` · required, secret · Rules, prices and customer quotes. - `ROUNDING_POINT` · required · Where rounding happens. Applied once, stated on the quote. - `DISCOUNT_APPROVAL_THRESHOLD_BPS` · required · Above this a quote needs approval before sending. - `QUOTE_VALIDITY_DAYS` · required · How long a quote holds its price. Pinned data makes this enforceable. - `SOLVER_TIMEOUT_MS` · required · Configuration must stay interactive. A slow solver is an abandoned configurator. - `ALLOW_EDIT_SENT_QUOTES` · required · Present as a constant so the answer is visible. Revisions, not edits. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it.
# Agent instructions · Hive CPQ product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Declarative constraints over options, evaluated by a solver, Every exclusion traceable to the rules that caused it, Exact decimal with a priced snapshot per quote, Rules and price lists versioned, quotes pinned to a version, TypeScript with PostgreSQL. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Expressing configuration rules as conditional code means every catalogue change is a deployment, and after two hundred rules nobody can tell whether a new one contradicts an existing one. Keep them as data, and check the rule set for contradictions and unreachable options when it is published. - Greying out an option without a reason is the single most common complaint about configurators, because the user cannot tell whether it is genuinely unavailable or whether they made a wrong choice three steps earlier. Track the excluding rules during evaluation rather than recomputing an explanation afterwards, since the second version drifts from the first. - Applying a percentage discount to a total computed from rounded line items gives a different answer from discounting each line, and a customer checking your arithmetic will find the discrepancy. Decide where rounding happens, apply it once, and state the rule. - A quote that references the current price list rather than a pinned one reprices itself when the catalogue is updated, and the customer accepts a figure that no longer matches what you sent. Pin everything at creation, and make a sent quote a snapshot rather than a view. - A bill of materials derived separately from the configuration will diverge from what was quoted, and the factory builds something the customer did not buy. Derive it from the same configuration and rule version the quote was priced from, and assert the two agree.
# Delivery milestones · Hive CPQ Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Rules as data, not code Option constraints are declarative and can be changed without deploying. ### Steps 1. Model constraints as data Four kinds cover most of it. Code means every change is a deployment. Files: `src/rules.ts` 2. Detect contradictions at publish Before a customer finds them, which is the alternative. Files: `src/analyse.ts`, `src/analyse.test.ts` 3. Report unreachable options An option nothing can select is a rule mistake, not a product decision. Files: `src/analyse.ts` ### Done when - [ ] Rules are data and change without deploying - [ ] Contradictory rules are detected at publish - [ ] Unreachable options are reported - [ ] Two hundred rules evaluate interactively ### Watch out - Expressing configuration rules as conditional code means every catalogue change is a deployment, and after two hundred rules nobody can tell whether a new one contradicts an existing one. Keep them as data, and check the rule set for contradictions and unreachable options when it is published. ## M2 · Saying why an option is unavailable Every disabled option can explain which choice disabled it. ### Steps 1. Record exclusions during evaluation Not afterwards. A separate explanation path drifts from the real one. Files: `src/evaluate.ts` 2. Name the rule and the triggering choice Both. The rule alone does not tell the user what to change. Files: `src/explain.ts` 3. Offer the change that would help And make applying it actually work. Files: `src/suggest.ts` ### Done when - [ ] Unavailable options name their excluding rule and choice - [ ] The suggested change works when applied - [ ] Explanations come from the evaluation itself - [ ] Several conflicts are all explained ### Watch out - Greying out an option without a reason is the single most common complaint about configurators, because the user cannot tell whether it is genuinely unavailable or whether they made a wrong choice three steps earlier. Track the excluding rules during evaluation rather than recomputing an explanation afterwards, since the second version drifts from the first. ## M3 · Pricing a configuration exactly A configured product prices correctly including the awkward parts. ### Steps 1. Use exact decimal throughout Percentages on rounded lines produce arithmetic customers dispute. Files: `src/money.ts` 2. Test quantity breaks at the boundary The threshold itself is where off-by-one pricing lives. Files: `src/pricing.test.ts` 3. Define one rounding point And assert lines sum exactly to the total. Files: `src/pricing.ts` ### Done when - [ ] Repeated pricing gives an identical result - [ ] Quantity breaks apply correctly at boundaries - [ ] Dependent pricing resolves in a stated order - [ ] Lines sum exactly to the total ### Watch out - Applying a percentage discount to a total computed from rounded line items gives a different answer from discounting each line, and a customer checking your arithmetic will find the discrepancy. Decide where rounding happens, apply it once, and state the rule. ## M4 · Quotes that do not change after sending What the customer received is what you can still see months later. ### Steps 1. Pin rule version and price list At creation. Referencing current means the quote changes itself. Files: `src/quotes.ts` 2. Make sent quotes immutable Revisions, not edits. The customer has a copy. Files: `src/quotes.ts`, `tests/immutable.test.ts` 3. Enforce validity An expired quote accepted at the old price is a loss you chose by accident. Files: `src/validity.ts` ### Done when - [ ] A quote reprices identically after a catalogue change - [ ] A sent quote cannot be edited - [ ] Expired quotes require a recorded decision - [ ] Rebuilding from pinned versions reproduces the total ### Watch out - A quote that references the current price list rather than a pinned one reprices itself when the catalogue is updated, and the customer accepts a figure that no longer matches what you sent. Pin everything at creation, and make a sent quote a snapshot rather than a view. ## M5 · Documents, approvals and handing off to production (production only) A quote becomes a document, an approval and something the factory can build. ### Steps 1. Render reproducibly from pinned data The document a customer holds must be reproducible exactly. Files: `src/document.ts` 2. Gate discounts on approval Above a threshold, recorded, before sending. Files: `src/approvals.ts` 3. Derive the bill of materials from the same configuration A separate derivation diverges and the factory builds the wrong thing. Files: `src/bom.ts`, `tests/bom-agreement.test.ts` ### Done when - [ ] Documents render identically from stored data - [ ] Discounts above threshold require approval - [ ] The bill of materials matches the priced configuration - [ ] An unbuildable configuration is caught before sending ### Watch out - A bill of materials derived separately from the configuration will diverge from what was quoted, and the factory builds something the customer did not buy. Derive it from the same configuration and rule version the quote was priced from, and assert the two agree.
# Operations · Hive CPQ ## Backup Quotes are commercial commitments and rule versions are what make them reproducible. Back up both, and keep old rule versions rather than pruning them, since a quote from six months ago needs its version to be rebuildable. ## Restore Restore and confirm quotes still rebuild to their original totals, since a restore that loses an old price list leaves quotes that cannot be reproduced and therefore cannot be defended. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Solver evaluation time and the rate of configurations abandoned at a particular option. The second usually identifies a rule that is confusing rather than a product that is unpopular. ## Incident checklist A quote whose total changed after sending is the failure that reaches a customer, and it means something referenced the current catalogue rather than the pinned version. The pinned data is what makes the case answerable. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] Contradictory rules are confirmed detected at publish - [ ] Unavailable options are confirmed to name their excluding rule - [ ] Line items are confirmed to sum exactly to the total - [ ] A quote is confirmed to reprice identically after a catalogue change - [ ] The bill of materials is confirmed to match the priced configuration ## Launch constraint Do not market omitted Hive CPQ capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered.
# Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Rules, prices and customer quotes. DATABASE_URL=postgres://localhost:5432/cpq # Required. Where rounding happens. Applied once, stated on the quote. ROUNDING_POINT=line_item # Required. Above this a quote needs approval before sending. DISCOUNT_APPROVAL_THRESHOLD_BPS=1000 # Required. How long a quote holds its price. Pinned data makes this enforceable. QUOTE_VALIDITY_DAYS=30 # Required. Configuration must stay interactive. A slow solver is an abandoned configurator. SOLVER_TIMEOUT_MS=200 # Required. Present as a constant so the answer is visible. Revisions, not edits. ALLOW_EDIT_SENT_QUOTES=never
$ choose a build depth, inspect the files, then open the complete pack in your agent
Because the software is the cheap part. Manufacturers pay for a vendor who will sit with their engineers, turn a messy catalog and a folder of Excel price lists into a rule set that does not produce impossible configurations, then keep it working as products change and push results into ERP. A solo build covers your own product line if you are the engineer, the salesperson and the person who maintains the rules. A company with fifty dealers and a configurable machine cannot staff that with one person and a JSON file.
xERP and CRM integrations, so accepted quotes become copy-paste work
xA dealer or reseller portal with per-account price lists, discount tiers and order history
xVisual product previews, 3D or otherwise, which is often the thing that closes the sale
xAnyone but you maintaining the rule engine when the catalog changes
xVersion control on quotes and catalogs, audit trails, and the boring guarantees a buyer expects when a quote is contractual
Nothing worth pointing at. That's why the prompt exists.
Vibecode Hive CPQ
Kinda. The core of Hive CPQ is buildable in a weekend with the prompt on this page, but there are real gaps: ERP and CRM integrations, so accepted quotes become copy-paste work, A dealer or reseller portal with per-account price lists, discount tiers and order history. Read the honest list above before committing.
How much does Hive CPQ cost?
Hive CPQ's pricing is usage-based or varies by plan · No pricing page exists: /pricing returns 404 and the sitemap index lists no pricing URL. Site offers 'Request a trial' and 'Talk to an expert' only..
What do I lose by replacing Hive CPQ?
Honestly: ERP and CRM integrations, so accepted quotes become copy-paste work; A dealer or reseller portal with per-account price lists, discount tiers and order history; Visual product previews, 3D or otherwise, which is often the thing that closes the sale; Anyone but you maintaining the rule engine when the catalog changes; Version control on quotes and catalogs, audit trails, and the boring guarantees a buyer expects when a quote is contractual. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to Hive CPQ?
No mature open-source alternative worth pointing at, which is exactly why the one-shot prompt on this page exists.