Wiki · Workflow companion

Vendor cost update

Daily ops. Vendor raises (or lowers) a cost; the change cascades through assembly cost rollups, margin checks on open bids, and proposed customer pricing — Mike sees the full impact before approving.

Real · Cascade engine
What this is

The cascade that protects margin

A vendor cost update is rarely a single-row change. A 5% butter increase from Bongards moves the landed cost on every assembly that uses butter, which moves the margin band on every active bid line containing those assemblies, which may breach the 22% gross margin floor on customer-facing quotes. This workflow encodes that cascade so Mike sees the second- and third-order impact before he says yes.

The contract lives in workflow_definitions WHERE workflow_type='vendor_cost_review'. Trigger is typically event (a new vendor invoice or RESTlet poll detects a price change), risk level 3, expected duration 20 minutes including Mike's review of the impact report.

The thing this workflow gets right that a manual spreadsheet doesn't: it walks the BOM graph. If Bongards Salted Butter is an ingredient in Right Start Breakfast Burrito and that's a component of the SY2026 NYC DOE bid, the workflow shows you that chain in one view.

When to use it

Trigger conditions

Worked example

Bongards raises butter price 5%

Scenario

Bongards Creameries (vendor #387) emails a new price list: salted butter (item #BTRSLT-1) moves from $2.80/lb to $2.94/lb (+5.0%). The cost ingestion page parses the email attachment; a proposed action lands in Mike's queue.

The workflow runs the impact simulation: 14 assemblies use BTRSLT-1, the landed cost on Right Start Breakfast Burrito moves from $3.42 to $3.51 (+2.6%), the SY2026 NYC DOE bid line for that assembly drops from 22.0% to 19.4% gross margin — below the 20% floor. The workflow surfaces this with a red band on that bid line.

Mike has two choices: accept the cost and re-price the bid (triggers bid price update workflow), or push back on Bongards. He approves the cost change but routes the SY2026 line to a re-pricing follow-up. Cascade total: vendor_costs updated, 14 assemblies recomputed, 31 open bid lines flagged, 4 active customer quotes flagged for review.

Step-by-step what happens

The four beats

  1. 01

    Detect the change (auto)

    vendor_last_cost_change compares the incoming cost to vendor_costs.current_cost. If the delta exceeds the per-item tolerance (default 0.5%), a row is written to proposed_actions with action_type='vendor_cost_update'.

    Reads vendor_costs
    Time ~150ms
  2. 02

    Simulate the impact (auto)

    simulate_cost_increase walks the BOM graph from bom_components, recomputes each affected assembly_cost_rollup in memory (no writes), and joins to bid_lines + quote_lines to identify margin breaches. A structured impact report is attached to the proposed action.

    Reads bom_components, assembly_cost_rollup, bid_lines, quote_lines, pricing_master
    Time ~800ms for typical 14-assembly fanout
  3. 03

    Propose + approve (HITL)

    Mike sees the impact report in /proposed-actions.html: cost delta, list of affected assemblies, list of margin-breach bid lines, recommended follow-ups. He approves or rejects. R560 atomic claim applies — no double-approve race.

    Writes proposed_actions.status
    HITL step 2
  4. 04

    Apply + recompute (auto)

    propose_bulk_cost_basis writes the new cost to vendor_costs, appends to vendor_cost_history, runs recompute_assembly_cost_rollup on each affected assembly, and emits one vendor.cost_changed event plus an assembly.cost_recomputed event per assembly. Bid-line and quote-line flags are written to margin_breach_queue for follow-up workflows.

    Writes vendor_costs, vendor_cost_history, assembly_cost_rollup, margin_breach_queue, events
    Emits vendor.cost_changed
    Time ~3s for typical fanout
Outcomes

What's different after the workflow runs

Vendor cost
Updated
+ audit history
Assemblies
Recomputed
all dependents
Margin flags
Queued
follow-up needed
Event emitted
Yes
downstream subscribers wake
Failure modes

What can go wrong and how to recover

BOM graph has a cycle

Rare but possible if an assembly accidentally references itself. recompute_assembly_cost_rollup uses a recursive CTE with depth limit 6. Beyond that it aborts with BOM_CYCLE_DETECTED. Manual fix: untangle in bom_components.

Margin breach not actioned

If Mike approves the cost but doesn't follow up on the margin-breach queue, customers may see stale (below-floor) prices. The breach queue has a 7-day SLA; daily digest highlights overdue items.

Concurrent cost updates on same vendor

If two proposed actions land on the same vendor_item, the second one's impact simulation may use stale cost from the first. The PushMutexDO Durable Object serializes vendor updates per vendor to prevent this.

Related

Adjacent workflows + diagrams

For developers

Code paths + invariants

ConcernWhere
Workflow contractworkflow_definitions WHERE workflow_type='vendor_cost_review'
Change detectionsrc/chat_tools/impls.ts vendor_last_cost_change
Impact simulationsrc/chat_tools/impls.ts simulate_cost_increase
Bulk applysrc/chat_tools/impls.ts propose_bulk_cost_basis
Assembly recomputerecompute_assembly_cost_rollup (recursive CTE depth 6)
Concurrencysrc/durable_objects.ts PushMutexDO
Event emissionevents table — vendor.cost_changed, assembly.cost_recomputed
HITL invariantADR-031 — vendor cost change never auto-applies