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'.
Trigger conditions
- Cost drift cron fires when an assembly's
drift_pct_vs_priorexceeds the R533 threshold. - Mike opens
/assembly/<code>and notices a BOM line that doesn't match reality. - Vendor failover means a BOM line's sourcing changed and rates need to follow.
- USDA substitution swaps a commodity component — triggers a follow-on drawdown check.
- Spec audit flags a yield mismatch between assembly header and BOM math.
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.
The 9 beats
-
01
Stage assembly header update (if header_changes)
Header field changes (
description,custitem_brand,custitem_pack_size,custitem_case_count,custitem_yield) stage as oneproposed_actionsrow withaction_type=assembly_header_update. -
02
Stage BOM line updates (if line_changes)
Each BOM line add/update/remove stages as one (or batched)
proposed_actionsrow withaction_type=assembly_bom_update. Cascade policy determines bundling. -
03
Invalidate assembly cache
Keys
assembly:{assembly_code},assembly_full:{assembly_id}, andbom:{assembly_id}are deleted so the next /assembly/<code> render is fresh. -
04
Recompute cost rollup
If BOM lines changed,
assembly_cost_rolluprecomputes raw + packaging + labor + overhead + freight from the new BOM. Persistsdrift_pct_vs_priorfor next cron. -
05
Invalidate pricing cache for affected rows
Each
(customer, school_year)pricing cache that touched this assembly gets busted. Keys derive fromcontext.active_pricing_rows. -
06
Regenerate spec sheet (conditional)
If
header_changestouched pack_size or case_count, or ifline_changesaffected yield, the spec sheet PDF regenerates via the existingregenerate_spec_sheettool. -
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.
-
08
Notify program owners (conditional)
If
customer_programs_usingis non-empty, one email per customer with this assembly drafts inoutbound_email_logwithstatus=pending_review. -
09
Trigger USDA drawdown check (conditional)
If a BOM line references a USDA commodity item, the runner invokes
usda_drawdown_commitso allocation stays aligned with the new BOM.
What's different after the workflow runs
- NS assembly record + BOM rows are updated within the NS push drain window.
assembly_cost_rollupreflects the new costs with a freshlast_computed.- Open pricing_master rows touching this assembly are flagged for review.
- Spec sheet PDF is regenerated if user-facing fields moved.
- Customer program owners have a draft email waiting if they're affected.
- A USDA drawdown is triggered if commodity allocation moved.
What can go wrong and how to recover
Retry policy is 2 attempts with backoff. Final failure raises an anomaly; manual recovery is POST /admin/assembly/recompute?assembly_id=<id>.
Retry policy applies. Drainer cron re-attempts within 15 min. D1 holds the proposed state.
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.
Browser Rendering can stretch on large BOMs. Old PDF remains in R2; next manual regen catches it.
Adjacent workflows + diagrams
Code paths + invariants
| Concern | Where |
|---|---|
| Workflow contract | workflow_definitions WHERE workflow_type='assembly_build_review' |
| Drift detector | R533 weekly assembly rollup cron |
| Cost rollup table | assembly_cost_rollup |
| Spec regen tool | regenerate_spec_sheet (executeChatTool branch) |
| USDA trigger | any BOM line where line_key matches usda_commodity_items |
| Reflexion tag | assembly_review,bom_change,data_cleanup |
| Risk level | 3 |
| Expected duration | ~variable (data-driven) |
| Trigger | manual_or_event · event_type_glob=assembly.cost_drift_detected |
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.
| Date | Round | Change | Touched by |
|---|---|---|---|
2026-05-26 | R586 | Added CHANGELOG · SCHEMA · RUNBOOK · BACKLOG sections — wiki became best-in-class operating documentation. | Mike + Claude |
2026-05-25 | R584/R585 | Wiki originally shipped — 8-section structure (hero / what / when / steps / outcomes / failure-modes / related / for-developers). | Mike + Claude |
workflow_definitions WHERE workflow_type='assembly_build_review' before acting on these claims.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)
| Field | Type | Description |
|---|---|---|
assembly_item_id | integer | NS assembly item ID. Required. |
proposed_qty | decimal | How many to build. Required. |
component_availability | json | BOM availability snapshot. |
D1 tables written
| Table | Operation | Trigger |
|---|---|---|
proposed_actions | INSERT | Build proposal — Mike approves |
assembly_build_audit | INSERT | Pre/post BOM snapshot |
ns_pending_pushes | INSERT (status=queued) | Build to NS |
Endpoints called
| Method | Path | Purpose |
|---|---|---|
POST | /api/workflow/execute | Runner entrypoint |
POST | /api/proposed-actions/:id/decide | HITL approval |
GET | /api/assembly/:id/bom | Live BOM availability |
Events fired
| event_type | When | Subscribers |
|---|---|---|
assembly.build.proposed | On staging | inventory_reconciliation |
assembly.build.committed | Post-HITL + NS push | downstream demand-planning |
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.
- Re-snapshot:
GET /api/assembly/<id>/bom?refresh=1 - If still short: Reject the proposal; create POs for components first.
- Investigate: Why was the snapshot stale? Inventory sync delay?
Scenario · Build approved but NS work order never created
ns_pending_pushes row stuck.
- Check:
SELECT * FROM ns_pending_pushes WHERE related_action_id=<id> - Retry:
POST /admin/ns-push/retry?action_id=<id> - 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.
- Force sync:
POST /api/sync/run?tier=hot&table=assemblyitem - Verify: Compare D1 quantityonhand vs NS via
ns_suiteql.
Logs to check
workflow_run_log· top-level run auditworkflow_step_log· per-step traceworkflow_verify_results· post-window verify outcomescron_locks· stuck cron lock detectionevents· workflow.completed / workflow.failed event trailreflexion_log· per-run narrative (if reflexion_enabled)npx wrangler tail· live Worker logs
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:
kill:ns_writes· stops every NS push platform-widekill:proposed_apply· stops HITL approvals from executing fan-outkill:high_risk_ops· stops risk_level >= 4 fan-out
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.
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.
-
STUB
Auto-build trigger from low inventory
Today Mike initiates builds. Could auto-stage when on-hand drops below reorder point.
-
OPEN
Multi-step assemblies (sub-assemblies)
Today flat BOM. Multi-tier BOMs need recursive availability check.
-
DEFER
Yield variance tracking
Build proposed for 1000 units, actual yield was 980. No table for variance today.