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 · example
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

    Growth Scout walks the discovery lanes — Meta Ads Library, AliExpress supplier signals, competitor catalogs, cross-store revival, search demand, spy tools.

  2. 11:45score

    candidate_scorer ranks by margin × uniqueness × market-fit. supplier_matcher cross-refs via reverse-image search.

  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.

// bring your own AI

Drive your whole store from your own AI.

The tool registry behind Otto isn't locked to the dashboard — the same tools power a remote MCP server and a public REST API. Connect Claude or Cursor and operate the store from wherever you already work.

MCP server

Connect Claude (custom connector), Claude Desktop, Cursor, or any MCP client over Streamable HTTP — OAuth 2.1 with store and scope selection, or an API-key Bearer token.

Public REST API

Every tool is callable as POST /api/v1/tools/{tool} with a scoped API key, documented in an OpenAPI spec. Fine-grained scopes per resource and action.

Approval cards

Write tools emit approval cards in the dashboard before anything executes — your own AI proposes, you approve. Auto-apply is an explicit opt-in.

// 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. The conversations below are illustrative examples, not live store data.
// 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_cosine 0.52 vs the anchor, below the 0.6 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.”

The goal: Otto answers a finance or ops question before it turns into a Slack thread, and every reply comes with the decision_log row attached. An answer you can click through to a row is an answer you can actually trust.

// 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