auction_oracle/
config.py db.py secrets_store.py ingest.py api.py cli.py
connectors/ base.py ahfyi.py everloot.py blizzard.py keyed.py (ForeverDB, wow.gg) local.py (addon, CSV/JSON, manual)
engines/ series.py market.py forecast.py crafting.py economy.py wealth.py intel.py
ai/ strategist.py
ui/ vanilla JS SPA (no build step)
addon/ WoW: Forever addon (Interface 16001)
seed/ sourced events + classic-reference recipe quantities (validated against the game graph)
tests/ pytest: data, engines (synthetic markets), API/security
%USERPROFILE%\.auction-oracle\venv\Scripts\python.exe -m auction_oracle serve --no-browser
%USERPROFILE%\.auction-oracle\venv\Scripts\python.exe -m pytest tests -q
AO_DB_PATH, AO_DATA_DIR, AO_PORT override paths/port. Secrets can also come from AO_<NAME> env vars (CI only).
NULL with flag='not_listed' and is never converted to 0.UNIQUE(source_id, market_id, scanned_at, kind)).≤ i (test_features_have_no_lookahead); LightGBM is trained on origins whose targets are before the evaluation cutoff.UNKNOWN, never priced.redact).Subclass connectors.base.Connector, set id/name/licence/attribution/commercial_ok/per_minute, implement health() (real calls through self.request, which never fakes success) and collect(con) returning a CollectReport. Write through upsert_scan + observations/listings (prices in copper per unit). Register it in ingest.all_connectors(). Add an offline test with fake_request (see tests/test_data.py).
Add m_<name>(series, h) -> log price to engines/forecast.py and to MODELS/SIMPLE_MODELS. evaluate() will compare it walk-forward against naive automatically; it is selected only if it wins.
Append (version, sql) to db.MIGRATIONS. Never edit an applied migration. connect() applies pending migrations; scripts\update.bat backs up first.
The UI is a static SPA talking to http://127.0.0.1:8765/api. To ship a native window, wrap ui/ in Tauri 2 and launch the Python server as a sidecar (PyInstaller one-file build of auction_oracle). No UI code changes required.
item_stats ≈ 2 s cold, cached per scan afterwards; scanner/opportunities < 50 ms warm; crafting table ≈ 0.2 s; full briefing (8 agents in parallel) ≈ 4 s.