Bid price update (canonical) risk 3 · mediumBid & specreflexion on

workflow_type: bid_price_update · owner: — · contract v2

When Mike approves a new bid line price: cascade to NS pricing record, pricing_master, pricing_history, bid_lines, bid artifact regen, hub cache, spec_sheet review flag, customer notification.

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: hitl_approval event: proposed_actions approved with action_type=bid_price_update 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_pricing: SELECT * FROM pricing_master WHERE customer_id=? AND item_code=? bid_line: SELECT * FROM bid_lines WHERE bid_id=? AND item_code=? vendor_cost: SELECT unit_cost, effective_from FROM vendor_costs WHERE item_code=? ORDER BY e…i Load context 3 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) [warn] vendor_cost_recency: effective_from > datetime('now','-7 days') [block] new_price_positive: new_price > 0 [warn] margin_floor: computed_margin_pct >= customer_program.margin_floori 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_bid_price_update entity_ref: workflow:bid_price_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 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 -> NS_PUSH_QUEUE: itempricing kind: ns_push queue: NS_PUSH_QUEUE on_failure: retry_3_then_alert record_type: itempricing status: REAL (src/lib/workflow_runner.ts)i write_ns ns_push 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_d1 -> D1: multiple kind: d1_write parallel_with: write_ns status: REAL (src/lib/workflow_runner.ts)i write_d1 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 #update_bid_lines -> D1: multiple kind: d1_write if: bid_id present status: REAL (src/lib/workflow_runner.ts)i update_bid_lines d1_write 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 #regen_bid_artifact -> HTTP POST <unset> kind: http_call if: bid_id AND artifact_r2_key status: REAL (src/lib/workflow_runner.ts)i regen_bid_artifact http_call STUB Wipe one or more KV cache keys so the next read pulls fresh data instead of stale cache. Real implementation - runs synchronously. FAN-OUT #invalidate_hub_cache -> KV: 4 keys kind: kv_invalidate if: bid_id IN active_hub_bids key: bid:{{bid_id}} key: bid_lines:{{bid_id}} key: hub:bid:{{bid_id}} key: pricing:{{item_code}}:* status: REAL (src/lib/workflow_runner.ts)i invalidate_hub_cache kv_invalidate REAL Set a follow-up flag on a row (e.g., 'needs_review') so a downstream cron or human picks it up later. STUB today. FAN-OUT #spec_sheet_check -> flag: review kind: flag if: abs(price_change_pct) > 5 status: STUB (src/lib/workflow_runner.ts)i spec_sheet_check flag 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: bid_price_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: ns_reflects_new_price window: 15m scheduler: verify cron @ :08/:38 (R563) result row: workflow_verify_resultsi ns_reflects_new_price ≤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: hub_cache_busted window: 60s scheduler: verify cron @ :08/:38 (R563) result row: workflow_verify_resultsi hub_cache_busted ≤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: d1_pricing_master_upd… scheduler: verify cron @ :08/:38 (R563)i d1_pricing_master_upd… ≤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: hitl_approval event: proposed_actions approved with action_type=bid_price_update 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_pricing: SELECT * FROM pricing_master WHERE customer_id=? AND item_code=? bid_line: SELECT * FROM bid_lines WHERE bid_id=? AND item_code=? vendor_cost: SELECT unit_cost, effective_from FROM vendor_costs WHERE item_code=? ORDER BY e…i Load context 3 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) [warn] vendor_cost_recency: effective_from > datetime('now','-7 days') [block] new_price_positive: new_price > 0 [warn] margin_floor: computed_margin_pct >= customer_program.margin_floori 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_bid_price_update entity_ref: workflow:bid_price_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 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_notify -> email draft kind: hitl_email_draft if: customer_has_open_quote AND price_increase status: STUB (src/lib/workflow_runner.ts)i customer_notify 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: bid_price_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: ns_reflects_new_price window: 15m scheduler: verify cron @ :08/:38 (R563) result row: workflow_verify_resultsi ns_reflects_new_price ≤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: hub_cache_busted window: 60s scheduler: verify cron @ :08/:38 (R563) result row: workflow_verify_resultsi hub_cache_busted ≤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: d1_pricing_master_upd… scheduler: verify cron @ :08/:38 (R563)i d1_pricing_master_upd… ≤60m Legend User UI Agent logic Policy Tool action Context / trace

1 · Trigger FIRES WHEN…

kind
hitl_approval
event
"proposed_actions approved with action_type=bid_price_update"

2 · Inputs required

namerequiredtype / hint
customer_idrequired
skurequired
new_pricerequired
effective_daterequired
reasoning_textrequired
bid_idoptional

3 · Context loaded D1 queries run before fan-out

name
current_pricing
tables
pricing_master
SELECT
  *
  FROM pricing_master
  WHERE customer_id=?
  AND item_code=?
name
bid_line
tables
bid_lines
SELECT
  *
  FROM bid_lines
  WHERE bid_id=?
  AND item_code=?
name
vendor_cost
tables
vendor_costs
SELECT
  unit_cost, effective_from
  FROM vendor_costs
  WHERE item_code=?
  ORDER BY effective_from DESC
  LIMIT 1

4 · Preconditions checked before any fan-out

checkruleseverity
vendor_cost_recencyeffective_from > datetime('now','-7 days')warn
new_price_positivenew_price > 0block
margin_floorcomputed_margin_pct >= customer_program.margin_floorwarn

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_bid_price_update (proposal envelope)
entity_ref
workflow:bid_price_update:run_<run_id>
approver
mike (single-admin)

6 · Fan-out targets 7 total · 1 real · 6 stub

#1write_ns ns_push STUB

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

#2write_d1 d1_write STUB

parallel_with
write_ns
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.

#3update_bid_lines d1_write STUB

condition (if)
bid_id present
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.

#4regen_bid_artifact http_call STUB

condition (if)
bid_id AND artifact_r2_key
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.

#5invalidate_hub_cache kv_invalidate REAL

keys
bid:{{bid_id}}
bid_lines:{{bid_id}}
hub:bid:{{bid_id}}
pricing:{{item_code}}:*
condition (if)
bid_id IN active_hub_bids

#6spec_sheet_check flag STUB

condition (if)
abs(price_change_pct) > 5
stub — not yet implemented in src/lib/workflow_runner.ts (kind flag hits the placeholder branch at line ~340 and emits step status 'stub'). Documented intent only.

#7customer_notify hitl_email_draft STUB

condition (if)
customer_has_open_quote AND price_increase
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=bid_price_update, run_id, narrative)runner automatic (reflexion_enabled=1)
log_runINSERT workflow_runsdeclared in contract
reflexionINSERT reflexion_logdeclared in contract
customer_profile_touchUPDATE customer_profiles.last_pricing_eventdeclared in contract

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

name
ns_reflects_new_price
when_to_run
15 min after run
name
hub_cache_busted
name
d1_pricing_master_updated

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
KV (CACHE)bid:{{bid_id}}invalidateREALfan-out #5 (invalidate_hub_cache)
KV (CACHE)bid_lines:{{bid_id}}invalidateREALfan-out #5 (invalidate_hub_cache)
KV (CACHE)hub:bid:{{bid_id}}invalidateREALfan-out #5 (invalidate_hub_cache)
KV (CACHE)pricing:{{item_code}}:*invalidateREALfan-out #5 (invalidate_hub_cache)
D1workflow_run_logINSERT (run summary)REALrunner automatic
D1reflexion_logINSERT (tags=bid_price_update)REALrunner automatic
Eventworkflow.completed (or workflow.failed)fireREALrunner automatic
D1workflow_verify_resultsINSERT pending × 3REALrunner verify staging
D1proposed_actionsINSERT (HITL gate envelope)REALrunner HITL gate
NetSuite (via NS_PUSH_QUEUE)itempricingpushSTUBfan-out #1 (write_ns)
D1unknownwriteSTUBfan-out #2 (write_d1)
D1unknownwriteSTUBfan-out #3 (update_bid_lines)
Worker HTTPGET invokeSTUBfan-out #4 (regen_bid_artifact)
D1flags / status fieldset =STUBfan-out #6 (spec_sheet_check)
D1proposed_actionsINSERT (email draft via tool)STUBfan-out #7 (customer_notify)