Wiki · Workflow companion

Item record review + push-back

The items catalog cleanup workhorse. Query an item record across every surface (NS fields, spec sheet, pricing, vendor costs, assembly memberships, recent transactions), propose targeted field updates, and after Mike's tap push back to NS and invalidate every dependent cache.

Mixed · Some steps live, others stubbed
What this is

Item record review + push-back

Item record review is the items-side counterpart to assembly_build_review. It's the workhorse Mike uses on the data-cleanup loop — open an item, see its full live state, propose specific field updates, and push the cascade.

The cascade is deliberately wide because items touch a lot: NS fields, spec sheets, pricing_master rows, vendor_costs, assembly memberships, open bid lines, recent transactions. Changes ripple to all of them; the workflow makes the ripple deterministic.

Risk level 3 (medium). Contract at workflow_definitions WHERE workflow_type='item_record_review'.

When to use it

Trigger conditions

Heuristic

The not_in_active_bid precondition warns (not blocks) — items in open bid lines can still be updated, but the workflow drafts a notification to the bid owner so the customer hears about the change first. That's the standing rule: never let a customer find a spec change in the bid PDF before we tell them.

Step-by-step what happens

The 7 beats

  1. 01

    Stage NS field update

    A proposed_actions row stages with action_type=item_field_update, entity_ref=item_code, fields from inputs.proposed_changes. Allowed fields are whitelisted (displayname, salesdescription, custitem_brand, etc) — anything else is rejected at precondition.

    Writes proposed_actions
    Time ~80ms
    Kind stage_proposed_action
    Status real
  2. 02

    Invalidate item cache

    Keys item:{item_code}, item_full:{item_code}, ns:item:{item_code} are deleted so the live /item/<code> render is fresh.

    Writes HUB_CACHE KV
    Time ~40ms
    Kind kv_invalidate
    Status real
  3. 03

    Invalidate pricing cache (pattern)

    Pattern delete on pricing:{item_code}:* via KV list scan. Busts every customer-pricing cache keyed off this item.

    Writes HUB_CACHE KV
    Time ~80ms (list scan)
    Kind kv_invalidate
    Status real
  4. 04

    Regenerate spec sheet (conditional)

    If proposed_changes touched spec-relevant fields (claims, pack_size, allergens), the spec sheet PDF regenerates via the regenerate_spec_sheet chat tool.

    Writes R2 bucket specs/
    Time ~6s
    Kind chat_tool
    Status stub
    Note conditional · spec fields touched
  5. 05

    Recompute assembly cost rollups

    For each assembly that has this item in its BOM, assembly_cost_rollup recomputes. Keeps margin math current across the assembly catalog.

    Writes assembly_cost_rollup
    Time ~200ms per assembly
    Kind d1_write
    Status stub
    Note scope · context.assembly_memberships
  6. 06

    Flag dependent pricing for review

    If item-level changes could affect customer pricing (pack_size ripples to case_price), pricing_master rows touching this item flag for the next pricing review cycle.

    Writes pricing_master flag column
    Time ~50ms
    Kind flag
    Status stub
    Note scope · context.pricing_master_row
  7. 07

    Notify sales if active bid (conditional)

    If open_bid_lines is non-empty, a notification email per bid owner drafts in outbound_email_log with status=pending_review.

    Writes outbound_email_log
    Time ~80ms per owner
    Kind hitl_email_draft
    Status stub
    Note conditional · open_bid_lines present
Outcomes

What's different after the workflow runs

NS field
Updated
via HITL
Caches
Busted
item + pricing
Assembly rollups
Recomputed
per BOM membership
Sales notified
Conditional
if in active bid
Failure modes

What can go wrong and how to recover

Item not found

Precondition item_exists blocks. Mike checks the item_code (case-insensitive lookup) and either fixes the code or runs new_assembly_item if it's genuinely new.

Item inactive

Precondition item_active warns. Mike can still proceed — sometimes reactivating an item is part of the cleanup.

Field name not whitelisted

Precondition rejects with severity=block. Adding a new allowed field requires updating the contract's precondition list (and ideally an ADR).

NS push transient

Retry policy is 2 attempts with [300s, 1800s] backoff. Drainer cron picks up persistent failures within 30 minutes.

Related

Adjacent workflows + diagrams

For developers

Code paths + invariants

ConcernWhere
Workflow contractworkflow_definitions WHERE workflow_type='item_record_review'
Allowed field listprecondition: displayname, salesdescription, custitem_brand, custitem_pack_size, custitem_case_count, custitem_buyamerican, custitem_kosher, custitem_allergens, custitem_country_of_origin, custitem_storage_temp, reorderpoint, taxschedule
Data-quality triggerdata_quality.item_flagged event
Spec regen toolregenerate_spec_sheet
Reflexion tagitem_review,data_cleanup
Risk level3
Expected duration~variable (data-driven)
Triggermanual_or_event · event_type_glob=data_quality.item_flagged
// Whitelist enforcement (precondition) const allowed = new Set([ 'displayname', 'salesdescription', 'custitem_brand', 'custitem_pack_size', 'custitem_case_count', 'custitem_kosher', 'custitem_allergens' ]); if (![...Object.keys(changes)].every(k => allowed.has(k))) { throw new Error('field_not_whitelisted'); }
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='item_record_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 · item_record_review · risk_level · 3.

Inputs (required + optional)

FieldTypeDescription
item_idintegerNS item ID. Required.
review_reasonstringWhy this item is being reviewed.
review_scopestring'pricing' | 'spec' | 'metadata' | 'all'

D1 tables written

TableOperationTrigger
proposed_actionsINSERTEach finding becomes a proposal
item_review_runsINSERTRun record
itemsUPDATE (post-HITL)Apply approved fixes
eventsINSERT (item.reviewed)audit

Endpoints called

MethodPathPurpose
POST/api/workflow/executeRunner entrypoint
GET/api/item/:id/review-historyPast reviews

Events fired

event_typeWhenSubscribers
item.reviewedPost-runaudit
item.metadata_changedIf metadata fields updatedkb_rebuild trigger candidate
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 · Review flagged a spec mismatch but spec_items wasn't updated

spec_items writes are gated on Mike's approval; the flag stays open until then.

  1. Check: SELECT * FROM proposed_actions WHERE entity_ref='item:<id>' AND action_type='spec_update'
  2. Approve in queue: Mike taps approve in /proposed-actions.html

Scenario · Item review shows no findings but Mike knows fields are wrong

Review scope may have excluded the field.

  1. Re-run with scope=all: POST /api/workflow/execute workflow_type=item_record_review scope=all
  2. Manual fix: Use propose_item_update chat tool.

Scenario · Item review committed wrong value (typo in NS)

Item review wrote bad data back to NS.

  1. Revert: Find prior value in items_history; manually set in NS.
  2. Investigate: Why did Mike approve? Review the proposed_action diff before approving.

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.