Wiki · Workflow companion

Anomaly remediation

Every 15 minutes the anomaly scanner sweeps margin compression, vendor cost spikes, and customer order pattern shifts. Findings land in anomaly_investigations, remediations stage in proposed_actions, and the admin dashboard surfaces them for Mike's next review pass.

Mixed · Some steps live, others stubbed
What this is

Anomaly remediation

Anomaly remediation is the substrate workflow that closes the loop between "the AI noticed something weird" and "Mike has a one-tap remediation in his queue." It runs every 15 minutes on the existing R174 cron, so the latency between an anomaly being detectable and Mike seeing the prompt is bounded.

The scan reuses the find_anomalies_today chat tool internals, then classifies findings by category and severity. For each anomaly, a remediation row stages in proposed_actions with action_type=anomaly_response.

Risk is 2 (low) — nothing pushes to NS from this workflow. Worst case is a noisy queue, which is recoverable.

When to use it

Trigger conditions

Heuristic

Anomalies are de-duplicated against rows already open in anomaly_investigations within the last 2 hours. Repeated detections of the same anomaly do not generate duplicate proposed_actions.

Step-by-step what happens

The 2 beats

  1. 01

    Propose one remediation per anomaly

    For each fresh anomaly the runner stages a proposed_actions row with action_type=anomaly_response, the structured payload, and a suggested next step. Mike can approve, ignore, or escalate.

    Writes proposed_actions
    Time ~50ms per anomaly
    Kind stage_proposed_action
    Status real
    Note one_per anomaly
  2. 02

    Surface to admin dashboard

    New rows insert into anomaly_investigations (the persistent record) and the weekly aggregate weekly_anomalies is updated. The admin-dashboard pulls live from both.

    Writes anomaly_investigations, weekly_anomalies
    Time ~80ms
    Kind d1_write
    Status stub
Outcomes

What's different after the workflow runs

Detection latency
≤ 15 min
cron window
Proposals
Staged
one per anomaly
Duplicates
Suppressed
2h dedupe window
Risk
Read-only push
no NS writes
Failure modes

What can go wrong and how to recover

Anomaly scanner times out

The cron tick logs a failure in workflow_runs but doesn't retry — next tick (15 min) picks it up. Acceptable for risk-2 work.

Duplicate anomaly rows

If the 2h dedupe window is too tight for a slow-moving anomaly, manual cleanup is DELETE FROM anomaly_investigations WHERE .... Rare.

No anomalies found

A successful run that produces zero rows is normal. The reflexion entry still records the no-op tick.

Related

Adjacent workflows + diagrams

For developers

Code paths + invariants

ConcernWhere
Workflow contractworkflow_definitions WHERE workflow_type='anomaly_remediation'
Cron schedule22,37,52 * * * * (R174)
Detectorfind_anomalies_today chat tool internals
Output tablesanomaly_investigations, weekly_anomalies
Reflexion taganomaly_remediation
Risk level2
Expected duration~15 min
Triggercron · 22,37,52 * * * * (R174 cron)
// Dedupe key — anomaly_type + entity_ref + 2h window const existing = await db.prepare( "SELECT id FROM anomaly_investigations WHERE anomaly_type=? AND entity_ref=? AND created_at > datetime('now','-2 hours') AND status='open'" ).bind(type, ref).first(); if (existing) return { skipped: 'dedupe_hit' };
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='anomaly_remediation' 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 · anomaly_remediation · risk_level · 3.

Inputs (required + optional)

FieldTypeDescription
anomaly_idintegeranomalies.id — the row triggering this. Required.
anomaly_typestring'cost_spike' | 'margin_drop' | 'sync_gap' | 'volume_outlier'.
severityinteger1-5; controls escalation tier.

D1 tables written

TableOperationTrigger
proposed_actionsINSERTRecommended remediation
anomaliesUPDATE (status=remediating)Lock so it's not picked up twice
anomaly_remediation_logINSERTWhat was proposed, when, by whom
eventsINSERT (anomaly.remediation_proposed)audit + dashboard

Endpoints called

MethodPathPurpose
POST/api/workflow/executeRunner entrypoint
GET/api/anomalies/openOpen anomalies dashboard feed
POST/api/anomalies/:id/acknowledgeMike marks no-action-needed

Events fired

event_typeWhenSubscribers
anomaly.remediation_proposedPost-fan-outaudit + chat notification
anomaly.resolvedWhen status moves to closedmonthly report
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 · Anomaly flagged but no proposed_action was created

Likely the remediation kind for that anomaly_type isn't wired (most are stubs).

  1. Check the log: SELECT * FROM anomaly_remediation_log WHERE anomaly_id=<id>
  2. Inspect status: SELECT status, last_error FROM anomalies WHERE id=<id>
  3. Manual proposal: Use the appropriate propose_* chat tool directly.

Scenario · Same anomaly flagged 5x in one hour

anomaly detector not deduping.

  1. Find dupes: SELECT entity_ref, COUNT(*) FROM anomalies WHERE created_at > datetime('now','-1 hour') GROUP BY entity_ref HAVING COUNT(*) > 1
  2. Mark dupes as closed: Keep oldest; close newer with status=duplicate.
  3. Fix detector: Add a 'last_flagged_at' check per entity_ref to suppress re-flagging within 24h.

Scenario · Anomaly closed but actually got worse

Closure was premature; remediation didn't actually fix.

  1. Re-open: UPDATE anomalies SET status='open', reopened_at=datetime('now') WHERE id=<id>
  2. Escalate severity: Bump to severity=5 to force Mike attention.
  3. Postmortem: Log a reflexion note on why closure was premature.

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.