Demo mode — all data is synthetic. Explore freely; nothing breaks. Reset demo data

How this is built

CQRS / Event Sourcing on the Ash framework (Elixir / Phoenix LiveView), following the same architecture as the production Dangote portal.

The flow

Command → Event → Projection. Commands are Ash resources with no data layer; on success an after_action change dispatches them through Commanded. The handler builds an immutable, versioned event (*V1) which is the only source of truth. Read models — the discovery board, scouting reports, the event feed — are Ash projections rebuilt by subscribing to that log.

The AI layer

A scout's request emits ScoutingReportRequestedV1. An event-driven automation calls an LLM through an Ash action (ash_ai, structured output). The result passes a grounding guardrail: every number the model cites must trace to a recorded stat, or the report is rejected. The outcome is captured as ScoutingReportGeneratedV1 / …RejectedV1 / …FailedV1 — so every AI decision is replayable and auditable.

Read discipline

No read pipes into Ash.read. Every query is a named read action with its filter logic embedded in the resource and exposed via a code interface. LiveViews call Projection.search!/1 — never a query pipeline.

Why it mirrors Tonsser

  • Consumer-scale discovery domain with mobile-shaped reads.
  • Multi-sport from day one (football + a basketball slice) — a nod to the NBA R&D.
  • AI integrated as an evaluated product feature, not a demo.

Deliberately out of scope

No auth/login wall (reviewer-first), no video pipeline (highlights are metadata), no real PII. These are scope decisions, not omissions — stated plainly because calibrated honesty is the point.

Full ADRs, the legacy-evolution essay and the AI cost/eval notes ship in the repository's /docs folder.