ENGINEERING CASE STUDY / OCTOBER 2026

Shepard AI
Engineering Teardown

A crypto movement-intelligence MVP built around one constraint: expensive ingestion must not sit on the user's request path. I separated market-data storage from account infrastructure, scheduled collection, and served dashboard panels from a database cache.

Shepard AI live product interface captured on October 8, 2026
Actual deployed interface. React + TypeScript / Docker + PostgreSQL / Supabase Auth.
01 / CollectBinance public APIs + RSS
02 / EvaluateValidate, deduplicate, score
03 / PersistPanel cache + run telemetry
04 / ServeCache-first dashboard reads

01 / Scheduling without request storms

The production Docker scheduler runs scanner, market overview, and snapshots every 900 seconds; RSS every 1,800 seconds. Jobs execute sequentially, use monotonic deadlines, and authenticate internal calls with a server-only cron secret. Dashboard loads read stored panels, not the external providers.

Trade-off: this is a single-instance, cron-equivalent scheduler, not a distributed queue. Deadlines reset after restart; a slow job can delay the next one. Failed jobs retry on their next scheduled cycle. Run records distinguish failure from a successful empty scan.

Code reviewed: deploy/app/scheduler.py

02 / RSS deduplication, precisely

Each item receives a fingerprint from provider:title:url: UTF-8 encode the first 500 characters, compute SHA-256, retain the first 12 bytes, and hex-encode them into a 24-character key. Aggregation uses a Map keyed by url || title_hash, so repeated URLs collapse within a sweep.

Trade-off: this is exact-identity deduplication, not semantic clustering. Syndicated copies with different URLs can survive. Provider-specific hashes and truncated input are deliberate MVP limitations; URL canonicalization and normalized-title matching are next.

Code reviewed: _shared/sentiment-engine.ts

03 / PostgreSQL: access paths & boundaries

Heavy snapshots and market cache live in Docker PostgreSQL, exposed through private PostgREST behind an HTTPS gateway. Supabase retains authentication, subscriptions, and usage enforcement. Composite B-tree indexes match symbol/timeframe lookups and latest-panel queries:

(symbol, timeframe, created_at DESC, id DESC)
(panel_type, created_at DESC, id DESC)

PostgREST reuses its built-in database connection pool; the Compose configuration does not pin a custom pool size or deploy PgBouncer. Hourly bounded retention caps historical growth; PostgreSQL can reuse deleted space, but deletion does not immediately shrink files.

Code reviewed: retention_covering_indexes.sql / deploy/app/compose.yaml

04 / Rate limits: bounded recovery

The AI router uses timeouts and per-provider/model cooldowns. On HTTP 429, numeric Retry-After is clamped to 60 seconds–1 hour; other HTTP failures cool down for 30 seconds. It moves to the next eligible free provider instead of immediately hammering the failed one.

The separate Python/Celery engine enables exponential backoff and jitter for database/Redis task failures. That is not a universal API retry policy. Dashboard automation uses scheduled retry and cache serving; a failed mutating request is not blindly replayed. AI usage reservation makes identical retries idempotent.

Code reviewed: _shared/llm-router.ts / shepard_engine/worker.py