Skip to main content

// otto

Your operator's keyboard shortcut.

Otto orchestrates the daily cycle, kicks off jobs, and answers your questions in decision rows — not paragraphs. Morning sync at 06:00, research at 11:00, discount cycle at 16:00. Always within policy.

Orchestrator Conversational Cron-scheduled
otto · 09:14
How did the morning cycle go?
O
Clean run. 142 decision rows, 47 ad moves, judge agreement at 96%. One held row — REVIVE_SEASONAL on SKU-LIN-007, kill switch was flipped during the import. Click to release.
decision_log#84196

// what otto does

One assistant. Three jobs.

Otto is not a chatbot — it's the conductor. Schedules the cycles, calls the worker, reads the rows back to you. The same Otto in the dashboard is the Otto that kicked off the 06:00 sync.

Orchestrator

Calls the /jobs/* endpoints on the FastAPI worker with X-Worker-Token auth. Every call idempotent — re-runs never double-execute, never double-log.

Scheduler

Runs the daily cycle on cron — morning sync, research lane, discount window. Each cycle takes a store_lock so two timers can't stomp each other.

Assistant

Conversational layer reads decision_log + ad_decision_log + cs_thread_evaluations. Answers in citations, not paragraphs.

// daily cycle

Morning. Research. Discount.

Three timed cycles, three different intents — sync the world, find new winners, manage open discount windows. Each cycle is idempotent, each call is rate-limited, each row lands in the audit plane.

Morning Cycle

06:00 daily

  1. 06:00sync

    catalog_sync + performance_sync pull last 24h of Shopify + Google Ads into Supabase.

  2. 06:30calibrate

    self_calibrator recomputes p75 winner floor + thresholds from rolling 28d distribution.

  3. 06:45classify

    classifier reassigns WINNER / MID / LOSER / INSUFFICIENT_DATA per variant.

  4. 07:00decide

    decision_engine emits Action per SKU. Phase 1: dry-run. Phase 2: executor applies.

  5. 07:30campaign plan

    Campaign Specialist runs planner-judge per channel. Creative briefs queue, audiences refresh.

Research Cycle

11:00 daily

  1. 11:00lanes

    Researcher walks 6 discovery lanes — Meta Library, AliExpress, competitor BS, frequency, organic, spy tools.

  2. 11:45score

    candidate_scorer ranks by margin × uniqueness × market-fit. supplier_matcher cross-refs via DataForSEO.

  3. 12:00queue

    research_candidates table populated. Top picks land in /dashboard/candidates for operator review.

Discount Cycle

16:00 daily

  1. 16:00audit

    Catalog discount_executor checks open discount windows. Margin-tier gated, time-bounded.

  2. 16:15revert

    Windows past their TTL fire PRICE_RESTORE — pre-stored reversal op, no improvisation.

  3. 16:30open

    OPTIMIZE_CYCLE_FAILED SKUs from morning cycle get fresh discount tests, capped by rate limit.

// example conversations

Answers in rows, not vibes.

Otto reads the audit plane to answer. Every response cites the row that justifies it — you can click straight through to the decision_log or cs_thread_evaluations entry the answer leans on.
// operator check-in
What moved on the catalog this morning?
O
62 DRAFT, 14 DISCOUNT_TEST, 8 PUBLISH, 3 SCALE_WINNER, 1 REVIVE_SEASONAL.
Largest single move: SKU-LIN-228-SND-L → ON_DISCOUNT (−15%, 14d window). Trigger was OPTIMIZE_CYCLE_FAILED after two cycles without lift. Reversal pre-stored.
One held row — REVIVE_SEASONAL on SKU-LIN-007 — paused because the kill switch was flipped at 09:14 during the manual import. Resolve to release.
decision_log#84193decision_log#84196
// cfo question
Why did we spend €755/day on EU/Linen/Throws yesterday?
O
We raised that cap on the 24th — ADJUST_BUDGET +18% via bid_pacing.
Reasoning: MER 4.1 → 4.8 over the 14d window, 38% search-impr-share headroom, and a −9% competitor-overlap delta.
Rate-limit position was 3 of 8 budget changes that week — well inside policy. Reversal op is pre-stored if you want to roll the cap back; one click.
ad_decision_log#21498
// cs triage
What's escalated today?
O
Five threads. Three are refund requests above €500 (auto-escalated by policy, never auto-resolved at that size).
One is a tone-drift hold — Jane's draft sat at brand_voice 0.78 vs centroid, below the 0.85 floor. Reply Judge bounced it for review.
One is a Dutch-language thread where languages_allowed_for_send is set to en + de only — drafted, judged, but held until you whitelist nl.
cs_thread_evaluations#3142cs_thread_evaluations#3144

// citation discipline

Every answer comes with the row.

Otto won't answer a question about a move without citing the row. If the row doesn't exist, the answer is “I don't have a row for that” — never a hallucinated number.

That's the contract. Operator trust scales with citation fidelity. Otto reads the audit plane the same way an analyst would read a ledger — and outputs answers that link back to it.

// quote from the docs

“Otto's job is to be the keyboard shortcut for the audit plane. If you can't click through from an answer back to a row, the answer wasn't real.”

“Otto answers my CFO's questions before they hit Slack. Every reply comes with the decision_log row attached. That's what 'AI for ecommerce' was supposed to mean from day one.”

Emma Liu
Head of Retention
BREVARD

// otto

Hand the cycle to a teammate.

Otto runs the daily loop, answers the board, kicks off the research lane, and never moves a euro without an audit row to back it up.

Cited answers · Cron-scheduled · Reversible