The Biergarten Data Pipeline — Activity DiagramThe Biergarten Data Pipeline — Activity DiagramParseArguments(argc, argv)spdlog::erroryesInvalid args?noInit CurlGlobalState & LlamaBackendStateBuild DI injectorOpens SQLite connection.Begins a single transactioncovering all five fixture types.Initialize SqliteExportServiceCreate BoundedChannel<LogEntry> log_chLog worker drains log_ch for theentire pipeline lifetime.All workers emit LogEntry structsvia PipelineLogger — never spdlog directly.Spawn Log Worker threadBiergartenPipelineOrchestrator::Run()COMMIT covers all five fixture types.Finalize SqliteExportServiceClose log_chDrain guarantees no LogEntry isdropped at shutdown.Join Log Workerspdlog::info "Pipeline complete in X ms"JsonLoader::LoadLocations("locations.json")JsonLoader::LoadBeerStyles("beer-styles.json")JsonLoader::LoadPersonas("personas.json")JsonLoader::LoadNamesByCountry("names-by-country.json")Beer styles do not need location context.Wikipedia summaries for the entire palette arefetched and cached globally at startup.EnrichmentService::PreWarmBeerStyleCache(beer_styles)Persona descriptions do not need location context.All persona lookups are resolved and cachedglobally at startup.EnrichmentService::PreWarmPersonaCache(personas)RunUserPhase(sampled_locations)Create BoundedChannels(loc_ch, llm_ch, exp_ch)Loop: Send Locations → loc_chClose loc_chJoin LLM Worker, SQLite WorkerRunBreweryAndBeerPhase(sampled_locations)Create BoundedChannels(loc_ch, llm_ch, exp_ch)Loop: Send Locations → loc_chClose loc_chJoin Enrichment WorkersClose llm_chBoth brewery_pool_ and beer_pool_are now completely populated.Join LLM Worker, SQLite WorkerRunCheckinPhase()Weights seeded from each user'spersona.checkin_weight. J-curve profileemerges from persona distribution.ICheckinDistributionStrategy::AssignActivityWeights(user_pool_)CheckinsForUser(user, brewery_pool_.size())TimestampFor(user, index)Select brewery from brewery_pool_GenerateCheckin(user, brewery, timestamp)via DataGeneratorProcessCheckin(checkin) → sqlite3_int64PipelineLogger::Log(Info, CheckinGeneration,nullopt, checkin_id, "sqlite")Append → checkin_pool_remainingFor each checkin index?doneremainingFor each GeneratedUser in user_pool_?doneBeer selection biased byuser.persona.style_affinities and abv_range.Rating skew modulated per persona.RunRatingPhase()Match brewery_id → select beer from beer_pool_(same brewery_id, biased by persona affinities)Beer exists for brewery?yesnoGenerateRating(user, beer, checkin_id)via DataGeneratorProcessRating(rating)PipelineLogger::Log(Info, RatingGeneration,nullopt, rating_id, "sqlite")PipelineLogger::Log(Warn, RatingGeneration,nullopt, brewery_id, "sqlite")Skip — brewery has no beersremainingFor each GeneratedCheckin in checkin_pool_?doneReceive LocationGuaranteed cache hit from startup.Returns a Persona struct carryingstyle_affinities, abv_range,ibu_preference, checkin_weight.IPersonaSelectionStrategy::SelectPersona(personas_palette_)Deterministic lookup — no LLM involved.Name selected from pre-keyed tableand passed into the generation prompt.NamesByCountry::SampleName(location.iso3166_1)LLM receives: Location fields + personadescription + sampled name. Generatesbio and preference signals groundedin locale and persona.GenerateUser(location, persona, sampled_name)via DataGeneratorPipelineLogger::Log(Info, UserGeneration,city, user_id, "llm")Send GeneratedUser → llm_chyesloc_ch has items?noClose llm_chReceive EnrichedCityGenerateBrewery(location, context)via DataGeneratorIBeerSelectionStrategy::SelectStyles(brewery, beer_style_palette_)Guaranteed cache hit from startup.GetStyleContextFromCache(style)GenerateBeer(brewery, style_context)via DataGeneratorAttach GeneratedBeer to Brewery bundleremainingFor each selected BeerStyle?donePipelineLogger::Log(Info,BreweryAndBeerGeneration,city, brewery_id, "llm")Send BreweryWithBeers Bundle → exp_chyesllm_ch has items?noClose exp_chReceive GeneratedUserProcessUser(user) → sqlite3_int64PipelineLogger::Log(Info, UserGeneration,city, user_id, "sqlite")Append → user_pool_yesllm_ch has items?noReceive BreweryWithBeers BundleProcessBrewery(brewery) → brewery_idAppend → brewery_pool_Set beer.brewery_id = brewery_idProcessBeer(beer) → sqlite3_int64Append → beer_pool_remainingFor each beer in bundle?donePipelineLogger::Log(Info,BreweryAndBeerGeneration,city, brewery_id, "sqlite")yesexp_ch has items?noReceive LocationGetLocationContext(location,BreweryContextStrategy)PipelineLogger::Log(Info,BreweryAndBeerGeneration,city, nullopt, "enrichment")Send EnrichedCity → llm_chyesloc_ch has items?noMainBiergartenPipelineOrchestrator::Run()OrchestratorLLM WorkerSQLite WorkerEnrichment Workers (xN)