Wiki · Workflow companion

Assembly build + BOM review

The catalog-cleanup workhorse for assemblies. Review the current BOM + cost rollup + customer-program usage, propose line-level changes, and after Mike's tap push back to NS, recompute every downstream rollup, regenerate the spec sheet, and bust dependent caches.

Mixed · Some steps live, others stubbed
What this is

Assembly build + BOM review

Assembly build review is the assembly-side counterpart to item_record_review. Where the item review keeps the items catalog clean, this one keeps assemblies — the BOM-bearing parents — honest. Mike (or the weekly cost-drift cron) opens an assembly, sees its full state, and proposes targeted BOM line changes or header field updates.

The cascade is deliberately wide: BOM changes ripple to assembly_cost_rollup, then to pricing_master for any active customer pricing, then to bid_lines for any open bid, then to the spec sheet PDF, then to KV caches that key off the assembly_id.

Risk level 3 (medium). Contract stored as workflow_definitions WHERE workflow_type='assembly_build_review'.

When to use it

Trigger conditions

Heuristic

If any BOM line references a USDA commodity item, step 9 also kicks off usda_drawdown_commit automatically — that ensures the commodity allocation stays in sync without Mike chasing it.

Step-by-step what happens

The 9 beats

  1. 01

    Stage assembly header update (if header_changes)

    Header field changes (description, custitem_brand, custitem_pack_size, custitem_case_count, custitem_yield) stage as one proposed_actions row with action_type=assembly_header_update.

    Writes proposed_actions
    Time ~80ms
    Kind stage_proposed_action
    Status real
    Note conditional · header_changes present
  2. 02

    Stage BOM line updates (if line_changes)

    Each BOM line add/update/remove stages as one (or batched) proposed_actions row with action_type=assembly_bom_update. Cascade policy determines bundling.

    Writes proposed_actions
    Time ~80ms per line
    Kind stage_proposed_action
    Status real
    Note conditional · line_changes present
  3. 03

    Invalidate assembly cache

    Keys assembly:{assembly_code}, assembly_full:{assembly_id}, and bom:{assembly_id} are deleted so the next /assembly/<code> render is fresh.

    Writes HUB_CACHE KV
    Time ~40ms
    Kind kv_invalidate
    Status real
  4. 04

    Recompute cost rollup

    If BOM lines changed, assembly_cost_rollup recomputes raw + packaging + labor + overhead + freight from the new BOM. Persists drift_pct_vs_prior for next cron.

    Writes assembly_cost_rollup
    Time ~200ms
    Kind d1_write
    Status stub
    Note conditional · line_changes present
  5. 05

    Invalidate pricing cache for affected rows

    Each (customer, school_year) pricing cache that touched this assembly gets busted. Keys derive from context.active_pricing_rows.

    Writes HUB_CACHE KV
    Time ~40ms per key
    Kind kv_invalidate
    Status real
  6. 06

    Regenerate spec sheet (conditional)

    If header_changes touched pack_size or case_count, or if line_changes affected yield, the spec sheet PDF regenerates via the existing regenerate_spec_sheet tool.

    Writes R2 bucket specs/
    Time ~6s
    Kind chat_tool
    Status stub
    Note conditional · spec-relevant fields
  7. 07

    Flag dependent bids

    For any open bid_line whose item_code equals the assembly's item_code (status in active or submitted), a flag marks the bid for re-pricing review.

    Writes bid_lines flag column
    Time ~50ms
    Kind flag
    Status stub
  8. 08

    Notify program owners (conditional)

    If customer_programs_using is non-empty, one email per customer with this assembly drafts in outbound_email_log with status=pending_review.

    Writes outbound_email_log
    Time ~80ms per customer
    Kind hitl_email_draft
    Status stub
    Note conditional · customer_programs_using present
  9. 09

    Trigger USDA drawdown check (conditional)

    If a BOM line references a USDA commodity item, the runner invokes usda_drawdown_commit so allocation stays aligned with the new BOM.

    Writes workflow_run_log + usda tables
    Time ~30 min downstream
    Kind workflow_class_invoke
    Status stub
    Note conditional · USDA component
Outcomes

What's different after the workflow runs

BOM lines
Updated
NS via HITL
Cost rollup
Recomputed
≤ 200ms after approval
Spec sheet
Regenerated
R2 versioned
Caches
Busted
~40ms each
Failure modes

What can go wrong and how to recover

Cost rollup compute fails

Retry policy is 2 attempts with backoff. Final failure raises an anomaly; manual recovery is POST /admin/assembly/recompute?assembly_id=<id>.

NS push transient (network)

Retry policy applies. Drainer cron re-attempts within 15 min. D1 holds the proposed state.

BOM line references a deleted item

Precondition catches this before stage; the runner refuses with a block-severity error. Mike fixes the line_key in the proposed payload and re-stages.

Spec sheet regen times out

Browser Rendering can stretch on large BOMs. Old PDF remains in R2; next manual regen catches it.

Related

Adjacent workflows + diagrams

For developers

Code paths + invariants

ConcernWhere
Workflow contractworkflow_definitions WHERE workflow_type='assembly_build_review'
Drift detectorR533 weekly assembly rollup cron
Cost rollup tableassembly_cost_rollup
Spec regen toolregenerate_spec_sheet (executeChatTool branch)
USDA triggerany BOM line where line_key matches usda_commodity_items
Reflexion tagassembly_review,bom_change,data_cleanup
Risk level3
Expected duration~variable (data-driven)
Triggermanual_or_event · event_type_glob=assembly.cost_drift_detected
// Conditional USDA trigger — step 9 if (bomLines.some(l => usdaItems.has(l.line_key))) { await runner.invoke('usda_drawdown_commit', { assembly_id }); } // Cost rollup drift threshold (R533) if (drift_pct > DRIFT_THRESHOLD_PCT) emit('assembly.cost_drift_detected');
Changelog

Dated trail · spot stale claims

Dated trail of when this doc was last touched, what changed, and what to look at if it feels stale.

DateRoundChangeTouched by
2026-05-26R586Added CHANGELOG · SCHEMA · RUNBOOK · BACKLOG sections — wiki became best-in-class operating documentation.Mike + Claude
2026-05-25R584/R585Wiki originally shipped — 8-section structure (hero / what / when / steps / outcomes / failure-modes / related / for-developers).Mike + Claude
If today is more than 60 days past the latest changelog row, treat live system behavior as the source of truth. The doc may have drifted — verify against the workflow contract in workflow_definitions WHERE workflow_type='assembly_build_review' before acting on these claims.
Schema · data contract

The machine-readable spec

Canonical fields, table names, endpoint signatures. What code should match, what tests should assert. workflow_type · assembly_build_review · risk_level · 3.

Inputs (required + optional)

FieldTypeDescription
assembly_item_idintegerNS assembly item ID. Required.
proposed_qtydecimalHow many to build. Required.
component_availabilityjsonBOM availability snapshot.

D1 tables written

TableOperationTrigger
proposed_actionsINSERTBuild proposal — Mike approves
assembly_build_auditINSERTPre/post BOM snapshot
ns_pending_pushesINSERT (status=queued)Build to NS

Endpoints called

MethodPathPurpose
POST/api/workflow/executeRunner entrypoint
POST/api/proposed-actions/:id/decideHITL approval
GET/api/assembly/:id/bomLive BOM availability

Events fired

event_typeWhenSubscribers
assembly.build.proposedOn staginginventory_reconciliation
assembly.build.committedPost-HITL + NS pushdownstream demand-planning
Runbook · when it breaks

It broke at 2am — what now

Different from "how do I use this." This is the page Mike pulls up when something is wrong: logs to check, recovery steps, who to escalate to.

Scenario · Build proposed but BOM components are short

component_availability snapshot was stale.

  1. Re-snapshot: GET /api/assembly/<id>/bom?refresh=1
  2. If still short: Reject the proposal; create POs for components first.
  3. Investigate: Why was the snapshot stale? Inventory sync delay?

Scenario · Build approved but NS work order never created

ns_pending_pushes row stuck.

  1. Check: SELECT * FROM ns_pending_pushes WHERE related_action_id=<id>
  2. Retry: POST /admin/ns-push/retry?action_id=<id>
  3. Manual: Create the WO in NS directly; reconcile D1 afterward.

Scenario · Build committed but inventory didn't reflect

NS push went through but D1 mirror lagged.

  1. Force sync: POST /api/sync/run?tier=hot&table=assemblyitem
  2. Verify: Compare D1 quantityonhand vs NS via ns_suiteql.

Logs to check

Kill switch · emergency stop

If this workflow is misbehaving in a high-impact way (creating bad proposed_actions in volume, pushing wrong things to NS), flip a kill switch:

See kill-switches-state-machine.html for the full state machine + recovery procedure.

Escalation

Primary: Mike Levine (single-admin) · mikelevine@globalfoodsolutions.co. For prolonged outage during business hours, notify warehouse lead + accounting lead so they can defer dependent work.

Backlog · open questions

What's not done · what's uncertain

What's not done, what's uncertain, what we punted. Captured so it survives context switches and doesn't die in someone's head.