New assembly item (full spec → BOM → NS) risk 4 · highItem & vendorreflexion on

workflow_type: new_assembly_item · owner: — · contract v2

New assembly product enters the system. Cascade: BOM cost rollup, NS item record, NS BOM record, items index, spec_items entry, spec sheet generation, brand routing, bid eligibility flag, customer programs that should price it, pricing pipeline registration.

0 · Visual flow archify workflow graph (6 lanes: trigger → context → HITL → fan-out → post → verify)

Part 1 of 2
01 / Trigger 02 / Context + preconditions 03 / HITL gate (Mike approves) 04 / Fan-out (Part 1 of 2) 05 / Post actions (log_run + reflexion + events) 06 / Verify (SQL / R2 / HTTP checks) How this workflow gets kicked off. Could be a chat-tool invocation, a cron tick, an inbound event (e.g. price.changed), or Mike clicking 'execute' from an admin page. TRIGGER kind: manual sources: spec_sheet_upload, intake_form invoker: Mike (single-admin) risk_level: 4i Manual Mike invokes risk 4 Before deciding anything, pull related data from D1 (the local mirror of NetSuite). Each query loads a slice of the entity's current state so the AI and Mike can review before any writes happen. LOAD CONTEXT (D1 queries before fan-out) existing_item_by_upc: SELECT id, itemid FROM items WHERE upccode=? LIMIT 1 existing_spec: SELECT id FROM spec_items WHERE item_code=? brand_rules: SELECT * FROM brand_routing_rules WHERE brand=? shared_brand_customers: SELECT DISTINCT customer_id FROM pricing_master WHERE item_code IN (SELECT item…i Load context 4 D1 queries Safety checks that must pass before any writes. 'block' severity halts the run; 'warn' surfaces a warning but continues. Without these, a bad input could cascade into NetSuite. PRECONDITIONS (checked before fan-out) [block] item_code_unique: existing_item_by_upc IS NULL AND existing_spec IS NULL [warn] allergens_present: allergen_facts IS NOT NULL [warn] vendor_cost_provided: vendor_costs.length > 0 [block] bom_complete_if_assembly: NOT is_assembly OR bom_components.length > 0i Preconditions 4 checks The HITL (Human-In-The-Loop) gate. The workflow stages a proposed_action and waits for Mike to approve in /proposed-actions.html. Only fires when risk_level >= 3. This is the invariant: no NS write happens without Mike's go-ahead. HITL GATE (Mike approves before fan-out) action_type: workflow_new_assembly_item entity_ref: workflow:new_assembly_item:run_<run_id> approver: mike (single-admin) risk_gate: >= 3 (this workflow = 4) approval window: typical <= 60 min envelope: proposed_actions row staged by runneri Mike approves stage_proposed_action risk ≥ 3 gate Write to the local D1 mirror (the read-side cache of NetSuite). Used for derived data and platform state. STUB today - per-tool d1_write logic lives in chat_tools/impls.ts. FAN-OUT #write_spec_items -> D1: spec_items kind: d1_write op: INSERT INTO spec_items (...) op: INSERT INTO spec_versions (spec_item_id, changed_field=created, new_value=ful… status: REAL (src/lib/workflow_runner.ts)i write_spec_items d1_write STUB Write to the local D1 mirror (the read-side cache of NetSuite). Used for derived data and platform state. STUB today - per-tool d1_write logic lives in chat_tools/impls.ts. FAN-OUT #write_items -> D1: items kind: d1_write parallel_with: write_spec_items op: INSERT INTO items shell status: REAL (src/lib/workflow_runner.ts)i write_items d1_write STUB Invoke an existing chat tool (one of 50+ registered in tool_registry) as part of the cascade. STUB today - runner doesn't yet dispatch. FAN-OUT #recompute_assembly -> tool: recompute_assembly_cost_rollup kind: chat_tool if: is_assembly tool: recompute_assembly_cost_rollup status: REAL (src/lib/workflow_runner.ts)i recompute_assembly chat_tool STUB Pushes a write back to NetSuite via the NS_PUSH_QUEUE. The push is staged in ns_pending_pushes first and only flips to 'applied' after NS confirms. STUB today - the runner logs intent but doesn't enqueue yet. FAN-OUT #write_ns_item -> NS_PUSH_QUEUE: item kind: ns_push queue: NS_PUSH_QUEUE on_failure: retry_3_then_alert record_type: item status: REAL (src/lib/workflow_runner.ts)i write_ns_item ns_push STUB Pushes a write back to NetSuite via the NS_PUSH_QUEUE. The push is staged in ns_pending_pushes first and only flips to 'applied' after NS confirms. STUB today - the runner logs intent but doesn't enqueue yet. FAN-OUT #write_ns_bom -> NS_PUSH_QUEUE: bom kind: ns_push if: is_assembly queue: NS_PUSH_QUEUE record_type: bom status: REAL (src/lib/workflow_runner.ts)i write_ns_bom ns_push STUB Fire an HTTP request to another endpoint (often a self-fetch to /api/). STUB today - runner doesn't yet make the call. FAN-OUT #generate_spec_sheet -> HTTP POST /api/spec/generate kind: http_call status: REAL (src/lib/workflow_runner.ts)i generate_spec_sheet http_call STUB Always-runs at the end of every workflow execution: writes a row to workflow_run_log with status, duration, step counts, and errors. Real implementation. POST ACTION: log_run -> D1: workflow_run_log fields: run_id, workflow_type, status, started_at, completed_at, summary_json source: runner automatic (always)i log_run workflow_run_log REAL Writes an entry to reflexion_log so the AI 'remembers' what happened. Only fires if the contract has reflexion_enabled=1. Future workflows can search this log for prior context. Real implementation. POST ACTION: reflexion -> D1: reflexion_log tags: new_assembly_item reflexion_enabled: True fields: run_id, narrative, tags source: runner automatic (when reflexion_enabled=1)i reflexion reflexion_log REAL Fires a workflow.completed / workflow.partial / workflow.failed event into the event ledger so downstream subscriptions can react. Uses an idempotency_key so producer retries collapse. Real implementation (R564). POST ACTION: event -> Event ledger (recordEvent) types: workflow.completed | workflow.partial | workflow.failed idempotency_key per run_id source: runner automatici event workflow.completed REAL A post-execution sanity check. The runner stages this with status='pending'; the verify-scheduler cron (every :08 and :38 hourly) wakes up after the configured window (e.g. +24h) and executes the sql_check, then flips status to pass/fail/timeout. Real implementation (R564). VERIFY: spec_persisted window: 5s sql_check: SELECT id FROM spec_items WHERE item_code=? scheduler: verify cron @ :08/:38 (R563) result row: workflow_verify_resultsi spec_persisted ≤60m A post-execution sanity check. The runner stages this with status='pending'; the verify-scheduler cron (every :08 and :38 hourly) wakes up after the configured window (e.g. +24h) and executes the sql_check, then flips status to pass/fail/timeout. Real implementation (R564). VERIFY: items_shell_created window: 5s sql_check: SELECT id FROM items WHERE itemid=? scheduler: verify cron @ :08/:38 (R563) result row: workflow_verify_resultsi items_shell_created ≤60m A post-execution sanity check. The runner stages this with status='pending'; the verify-scheduler cron (every :08 and :38 hourly) wakes up after the configured window (e.g. +24h) and executes the sql_check, then flips status to pass/fail/timeout. Real implementation (R564). VERIFY: ns_item_reflects window: 15m sql_check: SELECT id FROM items WHERE itemid=? AND lastmodifieddate > ? scheduler: verify cron @ :08/:38 (R563) result row: workflow_verify_resultsi ns_item_reflects ≤60m A post-execution sanity check. The runner stages this with status='pending'; the verify-scheduler cron (every :08 and :38 hourly) wakes up after the configured window (e.g. +24h) and executes the sql_check, then flips status to pass/fail/timeout. Real implementation (R564). VERIFY: assembly_rollup_prese… scheduler: verify cron @ :08/:38 (R563)i assembly_rollup_prese… ≤60m A post-execution sanity check. The runner stages this with status='pending'; the verify-scheduler cron (every :08 and :38 hourly) wakes up after the configured window (e.g. +24h) and executes the sql_check, then flips status to pass/fail/timeout. Real implementation (R564). VERIFY: spec_pdf_present window: 60s scheduler: verify cron @ :08/:38 (R563) result row: workflow_verify_resultsi spec_pdf_present ≤60m Legend User UI Agent logic Policy Tool action Context / trace
Part 2 of 2
01 / Trigger 02 / Context + preconditions 03 / HITL gate (Mike approves) 04 / Fan-out (Part 2 of 2) 05 / Post actions (log_run + reflexion + events) 06 / Verify (SQL / R2 / HTTP checks) How this workflow gets kicked off. Could be a chat-tool invocation, a cron tick, an inbound event (e.g. price.changed), or Mike clicking 'execute' from an admin page. TRIGGER kind: manual sources: spec_sheet_upload, intake_form invoker: Mike (single-admin) risk_level: 4i Manual Mike invokes risk 4 Before deciding anything, pull related data from D1 (the local mirror of NetSuite). Each query loads a slice of the entity's current state so the AI and Mike can review before any writes happen. LOAD CONTEXT (D1 queries before fan-out) existing_item_by_upc: SELECT id, itemid FROM items WHERE upccode=? LIMIT 1 existing_spec: SELECT id FROM spec_items WHERE item_code=? brand_rules: SELECT * FROM brand_routing_rules WHERE brand=? shared_brand_customers: SELECT DISTINCT customer_id FROM pricing_master WHERE item_code IN (SELECT item…i Load context 4 D1 queries Safety checks that must pass before any writes. 'block' severity halts the run; 'warn' surfaces a warning but continues. Without these, a bad input could cascade into NetSuite. PRECONDITIONS (checked before fan-out) [block] item_code_unique: existing_item_by_upc IS NULL AND existing_spec IS NULL [warn] allergens_present: allergen_facts IS NOT NULL [warn] vendor_cost_provided: vendor_costs.length > 0 [block] bom_complete_if_assembly: NOT is_assembly OR bom_components.length > 0i Preconditions 4 checks The HITL (Human-In-The-Loop) gate. The workflow stages a proposed_action and waits for Mike to approve in /proposed-actions.html. Only fires when risk_level >= 3. This is the invariant: no NS write happens without Mike's go-ahead. HITL GATE (Mike approves before fan-out) action_type: workflow_new_assembly_item entity_ref: workflow:new_assembly_item:run_<run_id> approver: mike (single-admin) risk_gate: >= 3 (this workflow = 4) approval window: typical <= 60 min envelope: proposed_actions row staged by runneri Mike approves stage_proposed_action risk ≥ 3 gate Write to the local D1 mirror (the read-side cache of NetSuite). Used for derived data and platform state. STUB today - per-tool d1_write logic lives in chat_tools/impls.ts. FAN-OUT #pricing_eligibility -> D1: multiple kind: d1_write op: INSERT pricing_master rows for matching customer_programs status: REAL (src/lib/workflow_runner.ts)i pricing_eligibility d1_write STUB Draft an email and stage it as a proposed_action for Mike's review. The email is NOT sent - only drafted + queued. STUB today. FAN-OUT #customer_notification -> email draft kind: hitl_email_draft if: shared_brand_customers.length > 0 tool: propose_email_to_customer status: STUB (src/lib/workflow_runner.ts)i customer_notification hitl_email_draft STUB Always-runs at the end of every workflow execution: writes a row to workflow_run_log with status, duration, step counts, and errors. Real implementation. POST ACTION: log_run -> D1: workflow_run_log fields: run_id, workflow_type, status, started_at, completed_at, summary_json source: runner automatic (always)i log_run workflow_run_log REAL Writes an entry to reflexion_log so the AI 'remembers' what happened. Only fires if the contract has reflexion_enabled=1. Future workflows can search this log for prior context. Real implementation. POST ACTION: reflexion -> D1: reflexion_log tags: new_assembly_item reflexion_enabled: True fields: run_id, narrative, tags source: runner automatic (when reflexion_enabled=1)i reflexion reflexion_log REAL Fires a workflow.completed / workflow.partial / workflow.failed event into the event ledger so downstream subscriptions can react. Uses an idempotency_key so producer retries collapse. Real implementation (R564). POST ACTION: event -> Event ledger (recordEvent) types: workflow.completed | workflow.partial | workflow.failed idempotency_key per run_id source: runner automatici event workflow.completed REAL A post-execution sanity check. The runner stages this with status='pending'; the verify-scheduler cron (every :08 and :38 hourly) wakes up after the configured window (e.g. +24h) and executes the sql_check, then flips status to pass/fail/timeout. Real implementation (R564). VERIFY: spec_persisted window: 5s sql_check: SELECT id FROM spec_items WHERE item_code=? scheduler: verify cron @ :08/:38 (R563) result row: workflow_verify_resultsi spec_persisted ≤60m A post-execution sanity check. The runner stages this with status='pending'; the verify-scheduler cron (every :08 and :38 hourly) wakes up after the configured window (e.g. +24h) and executes the sql_check, then flips status to pass/fail/timeout. Real implementation (R564). VERIFY: items_shell_created window: 5s sql_check: SELECT id FROM items WHERE itemid=? scheduler: verify cron @ :08/:38 (R563) result row: workflow_verify_resultsi items_shell_created ≤60m A post-execution sanity check. The runner stages this with status='pending'; the verify-scheduler cron (every :08 and :38 hourly) wakes up after the configured window (e.g. +24h) and executes the sql_check, then flips status to pass/fail/timeout. Real implementation (R564). VERIFY: ns_item_reflects window: 15m sql_check: SELECT id FROM items WHERE itemid=? AND lastmodifieddate > ? scheduler: verify cron @ :08/:38 (R563) result row: workflow_verify_resultsi ns_item_reflects ≤60m A post-execution sanity check. The runner stages this with status='pending'; the verify-scheduler cron (every :08 and :38 hourly) wakes up after the configured window (e.g. +24h) and executes the sql_check, then flips status to pass/fail/timeout. Real implementation (R564). VERIFY: assembly_rollup_prese… scheduler: verify cron @ :08/:38 (R563)i assembly_rollup_prese… ≤60m A post-execution sanity check. The runner stages this with status='pending'; the verify-scheduler cron (every :08 and :38 hourly) wakes up after the configured window (e.g. +24h) and executes the sql_check, then flips status to pass/fail/timeout. Real implementation (R564). VERIFY: spec_pdf_present window: 60s scheduler: verify cron @ :08/:38 (R563) result row: workflow_verify_resultsi spec_pdf_present ≤60m Legend User UI Agent logic Policy Tool action Context / trace

1 · Trigger FIRES WHEN…

kind
manual
sources
["spec_sheet_upload","intake_form"]

2 · Inputs required

namerequiredtype / hint
item_coderequired
brandrequired
item_namerequired
descriptionrequired
categoryrequired
sub_categoryrequired
upcrequired
mpnoptional
allergen_factsoptional
usda_k12_infooptional
weightoptional
cases_per_palletoptional
ingredientsoptional
vendor_costs[]required
bom_components[]optional
is_assemblyoptional

3 · Context loaded D1 queries run before fan-out

name
existing_item_by_upc
tables
items
SELECT
  id, itemid
  FROM items
  WHERE upccode=?
  LIMIT 1
name
existing_spec
tables
spec_items
SELECT
  id
  FROM spec_items
  WHERE item_code=?
name
brand_rules
tables
brand_routing_rules
SELECT
  *
  FROM brand_routing_rules
  WHERE brand=?
name
shared_brand_customers
tables
pricing_master · spec_items
SELECT
  DISTINCT customer_id
  FROM pricing_master
  WHERE item_code IN (SELECT item_code
  FROM spec_items
  WHERE brand=?)

4 · Preconditions checked before any fan-out

checkruleseverity
item_code_uniqueexisting_item_by_upc IS NULL AND existing_spec IS NULLblock
allergens_presentallergen_facts IS NOT NULLwarn
vendor_cost_providedvendor_costs.length > 0warn
bom_complete_if_assemblyNOT is_assembly OR bom_components.length > 0block

5 · HITL gate

Risk level 4 ≥ 3 — runner stages a proposed_actions row before fan-out runs. Mike must approve in proposed-actions.html before any side-effect step executes (real or stub).

action_type
workflow_new_assembly_item (proposal envelope)
entity_ref
workflow:new_assembly_item:run_<run_id>
approver
mike (single-admin)

6 · Fan-out targets 8 total · 0 real · 8 stub

#1write_spec_items d1_write STUB

operations
["INSERT INTO spec_items (...)","INSERT INTO spec_versions (spec_item_id, changed_field=created, new_value=full_json)"]
stub — not yet implemented in src/lib/workflow_runner.ts (kind d1_write hits the placeholder branch at line ~340 and emits step status 'stub'). Documented intent only.

#2write_items d1_write STUB

parallel_with
write_spec_items
operations
["INSERT INTO items shell"]
stub — not yet implemented in src/lib/workflow_runner.ts (kind d1_write hits the placeholder branch at line ~340 and emits step status 'stub'). Documented intent only.

#3recompute_assembly chat_tool STUB

tool
recompute_assembly_cost_rollup
side_effects
(unknown)
condition (if)
is_assembly
stub — not yet implemented in src/lib/workflow_runner.ts (kind chat_tool hits the placeholder branch at line ~340 and emits step status 'stub'). Documented intent only.

#4write_ns_item ns_push STUB

NS record_type
item
queue
NS_PUSH_QUEUE
on_failure
retry_3_then_alert
stub — not yet implemented in src/lib/workflow_runner.ts (kind ns_push hits the placeholder branch at line ~340 and emits step status 'stub'). Documented intent only.

#5write_ns_bom ns_push STUB

NS record_type
bom
queue
NS_PUSH_QUEUE
condition (if)
is_assembly
stub — not yet implemented in src/lib/workflow_runner.ts (kind ns_push hits the placeholder branch at line ~340 and emits step status 'stub'). Documented intent only.

#6generate_spec_sheet http_call STUB

method
POST
path
/api/spec/generate
produces
["R2 artifact at specs/{item_code}.pdf"]
stub — not yet implemented in src/lib/workflow_runner.ts (kind http_call hits the placeholder branch at line ~340 and emits step status 'stub'). Documented intent only.

#7pricing_eligibility d1_write STUB

operations
["INSERT pricing_master rows for matching customer_programs"]
stub — not yet implemented in src/lib/workflow_runner.ts (kind d1_write hits the placeholder branch at line ~340 and emits step status 'stub'). Documented intent only.

#8customer_notification hitl_email_draft STUB

tool
propose_email_to_customer
condition (if)
shared_brand_customers.length > 0
stub — not yet implemented in src/lib/workflow_runner.ts (kind hitl_email_draft hits the placeholder branch at line ~340 and emits step status 'stub'). Documented intent only.

7 · Post actions declared + runner-automatic

idactionsource
runner_log_runINSERT into workflow_run_log (run_id, workflow_type, status, started_at, completed_at, summary_json)runner automatic
runner_reflexionINSERT into reflexion_log (tags=new_assembly_item, run_id, narrative)runner automatic (reflexion_enabled=1)
log_runINSERT workflow_runsdeclared in contract
reflexionINSERT reflexion_log with tags=new_spec,brand:?,category:?declared in contract
corpus_reindexqueue: catalog/reindexdeclared in contract

8 · Verify checks written to workflow_verify_results (pending — verify cron not yet wired)

name
spec_persisted
SELECT
  id
  FROM spec_items
  WHERE item_code=?
name
items_shell_created
SELECT
  id
  FROM items
  WHERE itemid=?
name
ns_item_reflects
when_to_run
15 min after run
SELECT
  id
  FROM items
  WHERE itemid=?
  AND lastmodifieddate > ?
name
assembly_rollup_present
when_to_run
5 min after run
SELECT
  id
  FROM assembly_cost_rollup
  WHERE assembly_id=(SELECT id
  FROM spec_items
  WHERE item_code=?)
  AND status='active'
name
spec_pdf_present

9 · Retry policy

max_attempts
2
backoff
linear
base_ms
2000
alert_on_final_failure
true

10 · What changes when this workflow runs aggregated side effects

systemtable / resourceactionstatussource
D1workflow_run_logINSERT (run summary)REALrunner automatic
D1reflexion_logINSERT (tags=new_assembly_item)REALrunner automatic
Eventworkflow.completed (or workflow.failed)fireREALrunner automatic
D1workflow_verify_resultsINSERT pending × 5REALrunner verify staging
D1proposed_actionsINSERT (HITL gate envelope)REALrunner HITL gate
D1unknownwriteSTUBfan-out #1 (write_spec_items)
D1unknownwriteSTUBfan-out #2 (write_items)
Chat toolrecompute_assembly_cost_rollupinvoke (side_effects=unknown)STUBfan-out #3 (recompute_assembly)
NetSuite (via NS_PUSH_QUEUE)itempushSTUBfan-out #4 (write_ns_item)
NetSuite (via NS_PUSH_QUEUE)bompushSTUBfan-out #5 (write_ns_bom)
Worker HTTPPOST /api/spec/generateinvokeSTUBfan-out #6 (generate_spec_sheet)
D1unknownwriteSTUBfan-out #7 (pricing_eligibility)
D1proposed_actionsINSERT (email draft via propose_email_to_customer)STUBfan-out #8 (customer_notification)