Vendor cost update risk 3 · mediumItem & vendorreflexion on

workflow_type: vendor_cost_update · owner: — · contract v2

Vendor email or manual entry indicates a cost change. Cascade: vendor_costs row, recompute assembly_cost_rollup, flag open quotes, NS vendor cost update, flag dependent specs needing review.

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

Workflow flow
01 / Trigger 02 / Context + preconditions 03 / HITL gate (Mike approves) 04 / Fan-out cascade 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: event sources: vendor_email, manual_entry, scheduled_review invoker: Mike (single-admin) risk_level: 3i Manual Mike invokes risk 3 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) current_vendor_cost: SELECT unit_cost, effective_from FROM vendor_costs WHERE vendor_item_id=(SELECT… impacted_pricing: SELECT COUNT(*) AS n FROM pricing_master WHERE item_code=? AND cost_basis IS NO… impacted_assemblies: SELECT COUNT(DISTINCT s.id) AS n FROM spec_items s WHERE s.item_code=? OR EXIST… impacted_quotes: SELECT COUNT(DISTINCT q.id) AS n FROM quotes q JOIN quote_lines ql ON ql.quote_…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] positive_cost: new_unit_cost > 0 [warn] reasonable_change: abs((new_unit_cost - current_unit_cost) / current_unit_cost) <= 0.5 [warn] effective_not_future_beyond_30d: effective_from <= now + 30 daysi Preconditions 3 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_vendor_cost_update entity_ref: workflow:vendor_cost_update:run_<run_id> approver: mike (single-admin) risk_gate: >= 3 (this workflow = 3) 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_d1 -> D1: vendor_costs kind: d1_write op: INSERT INTO vendor_costs (vendor_item_id, unit_cost, effective_from, source, … status: REAL (src/lib/workflow_runner.ts)i write_d1 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_assemblies -> tool: recompute_assembly_cost_rollup kind: chat_tool parallel_with: write_d1 tool: recompute_assembly_cost_rollup status: REAL (src/lib/workflow_runner.ts)i recompute_assemblies chat_tool STUB Queue a write for Mike's HITL approval - staged in the proposed_actions table with status='pending'. Doesn't touch NetSuite directly. Real implementation. FAN-OUT #flag_pricing_recheck -> proposed_actions kind: stage_proposed_action action_type: margin_recheck_request status: REAL (src/lib/workflow_runner.ts)i flag_pricing_recheck stage_proposed_action REAL Queue a write for Mike's HITL approval - staged in the proposed_actions table with status='pending'. Doesn't touch NetSuite directly. Real implementation. FAN-OUT #flag_open_quotes -> proposed_actions kind: stage_proposed_action if: impacted_quotes.n > 0 action_type: flag_stale_quote status: REAL (src/lib/workflow_runner.ts)i flag_open_quotes stage_proposed_action REAL 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 #ns_push -> NS_PUSH_QUEUE: vendor kind: ns_push queue: NS_PUSH_QUEUE on_failure: retry_3_then_alert record_type: vendor status: REAL (src/lib/workflow_runner.ts)i ns_push ns_push 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 #notify_vendor_owner -> email draft kind: hitl_email_draft if: pct_change > 5 tool: propose_email_to_customer status: STUB (src/lib/workflow_runner.ts)i notify_vendor_owner 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: vendor_cost_update 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: vendor_cost_recorded window: 5s sql_check: SELECT unit_cost FROM vendor_costs WHERE effective_from = ? ORDER BY id DESC LI… scheduler: verify cron @ :08/:38 (R563) result row: workflow_verify_resultsi vendor_cost_recorded ≤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_refre… scheduler: verify cron @ :08/:38 (R563)i assembly_rollup_refre… ≤60m Legend User UI Agent logic Policy Tool action Context / trace

1 · Trigger FIRES WHEN…

kind
event
sources
["vendor_email","manual_entry","scheduled_review"]

2 · Inputs required

namerequiredtype / hint
vendor_idrequired
skurequired
new_unit_costrequired
effective_fromrequired
sourcerequired
notesoptional

3 · Context loaded D1 queries run before fan-out

name
current_vendor_cost
tables
vendor_costs · vendor_items
SELECT
  unit_cost, effective_from
  FROM vendor_costs
  WHERE vendor_item_id=(SELECT id
  FROM vendor_items
  WHERE vendor_name=?
  AND vendor_item_code=?)
  ORDER BY effective_from DESC
  LIMIT 1
name
impacted_pricing
tables
pricing_master
SELECT
  COUNT(*) AS n
  FROM pricing_master
  WHERE item_code=?
  AND cost_basis IS NOT NULL
name
impacted_assemblies
tables
spec_items · bom_components · items
SELECT
  COUNT(DISTINCT s.id) AS n
  FROM spec_items s
  WHERE s.item_code=?
  OR EXISTS (SELECT 1
  FROM bom_components c
  JOIN items i ON i.id=c.item
  WHERE i.itemid=?)
name
impacted_quotes
tables
quotes · quote_lines
SELECT
  COUNT(DISTINCT q.id) AS n
  FROM quotes q
  JOIN quote_lines ql ON ql.quote_id=q.id
  WHERE ql.item_code=?
  AND q.lifecycle_status IN ('draft','proposed','sent')

4 · Preconditions checked before any fan-out

checkruleseverity
positive_costnew_unit_cost > 0block
reasonable_changeabs((new_unit_cost - current_unit_cost) / current_unit_cost) <= 0.5warn
effective_not_future_beyond_30deffective_from <= now + 30 dayswarn

5 · HITL gate

Risk level 3 ≥ 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_vendor_cost_update (proposal envelope)
entity_ref
workflow:vendor_cost_update:run_<run_id>
approver
mike (single-admin)

6 · Fan-out targets 6 total · 2 real · 4 stub

#1write_d1 d1_write STUB

operations
["INSERT INTO vendor_costs (vendor_item_id, unit_cost, effective_from, source, status) VALUES (?,?,?,?,'active')"]
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.

#2recompute_assemblies chat_tool STUB

tool
recompute_assembly_cost_rollup
side_effects
(unknown)
parallel_with
write_d1
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.

#3flag_pricing_recheck stage_proposed_action REAL

action_type
margin_recheck_request

#4flag_open_quotes stage_proposed_action REAL

action_type
flag_stale_quote
condition (if)
impacted_quotes.n > 0

#5ns_push ns_push STUB

NS record_type
vendor
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.

#6notify_vendor_owner hitl_email_draft STUB

tool
propose_email_to_customer
condition (if)
pct_change > 5
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=vendor_cost_update, run_id, narrative)runner automatic (reflexion_enabled=1)
log_runINSERT workflow_runsdeclared in contract
reflexionINSERT reflexion_log with tags=vendor_cost_changedeclared in contract

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

name
vendor_cost_recorded
SELECT
  unit_cost
  FROM vendor_costs
  WHERE effective_from = ?
  ORDER BY id DESC
  LIMIT 1
name
assembly_rollup_refreshed
when_to_run
5 min after run
SELECT
  MAX(computed_at)
  FROM assembly_cost_rollup
  WHERE status='active'

9 · Retry policy

max_attempts
3
backoff
exponential
base_ms
1000
max_ms
30000
alert_on_final_failure
true

10 · What changes when this workflow runs aggregated side effects

systemtable / resourceactionstatussource
D1proposed_actionsINSERT (action_type=margin_recheck_request, entity_type=workflow_run)REALfan-out #3 (flag_pricing_recheck)
D1proposed_actionsINSERT (action_type=flag_stale_quote, entity_type=workflow_run)REALfan-out #4 (flag_open_quotes)
D1workflow_run_logINSERT (run summary)REALrunner automatic
D1reflexion_logINSERT (tags=vendor_cost_update)REALrunner automatic
Eventworkflow.completed (or workflow.failed)fireREALrunner automatic
D1workflow_verify_resultsINSERT pending × 2REALrunner verify staging
D1proposed_actionsINSERT (HITL gate envelope)REALrunner HITL gate
D1unknownwriteSTUBfan-out #1 (write_d1)
Chat toolrecompute_assembly_cost_rollupinvoke (side_effects=unknown)STUBfan-out #2 (recompute_assemblies)
NetSuite (via NS_PUSH_QUEUE)vendorpushSTUBfan-out #5 (ns_push)
D1proposed_actionsINSERT (email draft via propose_email_to_customer)STUBfan-out #6 (notify_vendor_owner)