Skip to content

0038 — ARIA Learning Model: Observation Log + SQL Aggregates (Trade DNA Genetic Algorithm Retired)

Status

Accepted

Context

The original ARIA learning surface was framed as a per-player genetic algorithm — every ARIATradingPattern row was a "DNA" genome with a pattern_dna JSONB carrying mutable parameters (commodity, time_preference, quantity_range, risk_tolerance, sector_affinity, minimum_margin), tracked across generation numbers, parent_pattern lineage, and mutations history. Patterns competed under a fitness function (0.4 × success_rate + 0.4 × normalized_profit + 0.2 × risk_component) with quadratically-weighted random selection. After 10 uses, fitness < 0.3 mutated the pattern in place; fitness > 0.7 spawned offspring with ±5% parameter variation. A weekly GC pass pruned low-survival rows. A separate anti-gaming layer detected acceptance-rate anomalies, outcome-vs-prediction skew, wash trades, and pattern-fitness velocity bounds, slowing learning when manipulation was suspected.

The full design lived in SYSTEMS/trade-dna-evolution.md (~250 lines). It was internally consistent and cited a real evolutionary-algorithms research lineage. It also produced a complexity load that didn't pay for itself.

The forces in tension:

  • The player-facing feature is a recommendation surface — ARIA tells you what to trade, where to go, what to expect at the next port. The feature does not depend on any specific learning algorithm; it depends on having a per-player history to mine.
  • The genetic algorithm produced opaque recommendations — players couldn't easily understand "why did ARIA suggest this trade over that one?" because the answer involved fitness scoring, mutation history, and quadratic selection probability.
  • The genetic algorithm created an adversarial surface: fitness gaming. The anti-gaming layer was substantial design weight that existed only because the underlying algorithm had a fitness-gaming surface to defend against.
  • Implementation complexity ran across _mutate_pattern, _create_pattern_offspring, _calculate_pattern_fitness, the GC pass, the convergence detector, and the anti-gaming detector — each requiring its own tuning, testing, and ops support.
  • The same player-facing recommendation quality is achievable by mining the existing observation tables (ARIAPersonalMemory, ARIAMarketIntelligence, ARIAExplorationMap) with SQL aggregates and explicit heuristics. No genome required.

The privacy-first per-player isolation rule from ADR-0016 stays canonical and is unaffected by this change. Whatever ARIA learns, it learns from one player's data scope only. This ADR changes only the mechanism of that learning, not its scope.

Decision

The ARIA learning mechanism is an immutable per-trade observation log mined by SQL aggregates and explicit heuristics. The genetic-algorithm framing is retired. The recommendation engine produces explanations every player can read.

Schema

ARIATradingPattern (with pattern_dna, generation, parent_pattern, mutations, fitness_score, survival_probability columns) is dropped. Replaced by a much simpler ARIATradingObservation row per completed trade:

ARIATradingObservation
  id                       UUID PK
  player_id                UUID FK (partition key, per-player isolation enforced)
  trade_id                 UUID FK (the underlying trading-service event)
  commodity                String
  action                   Enum: buy | sell
  source_station_id        UUID FK
  dest_station_id          UUID FK (nullable for buy-only events)
  source_sector_id         Integer
  dest_sector_id           Integer (nullable for buy-only events)
  quantity                 Integer
  unit_price               Integer
  total_credits            Integer
  profit                   Integer (positive on sell-leg; cross-trip sum tracked separately)
  hours_held               Float (nullable; populated on sell-leg)
  observed_at              DateTime
  matched_market_intel_id  UUID FK ARIAMarketIntelligence.id (nullable)
  recommendation_id        UUID FK (nullable; populated when this trade fulfilled a prior ARIA recommendation)
  outcome_classification   Enum: profit | break_even | loss

The observation log is append-only. Trade reversals at the trading-service layer do not retroactively edit observations; they fire a follow-up reversal observation. This makes the log auditable and gives the recommendation engine a stable historical truth.

Recommendation engine

A small set of explicit SQL aggregates and heuristics over the observation log + ARIAMarketIntelligence + ARIAExplorationMap. No fitness, no mutation, no selection pressure. Examples (each runs as a documented SQL query against the player's observation log):

  • Top profitable repeated routes: GROUP BY (commodity, source_station_id, dest_station_id) HAVING count ≥ 3 ORDER BY avg(profit) DESC LIMIT 5.
  • Reliable commodities by station: GROUP BY (commodity, source_station_id) HAVING count ≥ 5 AND success_rate ≥ 0.7.
  • Routes within explored space: filter top-profitable routes by dest_sector_idARIAExplorationMap.visited_sectors AND distance ≤ max_jumps.
  • Watch-out commodities: GROUP BY (commodity) HAVING count ≥ 5 AND success_rate ≤ 0.3 — surfaced as a warning, not a recommendation.
  • Off-peak buy windows: GROUP BY (commodity, source_station_id, hour_of_day(observed_at)) to detect time-of-day pricing patterns.

Each recommendation carries a plain-English explanation sourced from the aggregate that produced it: "I'm suggesting this because you've made an average of 4,200 cr buying organics at Auriga and selling at Caracol over your last 7 trades, with a 6 of 7 success rate. Last trade was 2 days ago." The player can dismiss, accept, or click through to see the underlying observations.

Caching

ARIAQuantumCache (a vestigial table from the dropped Ghost Trading feature) is repurposed as the recommendation-aggregate cache. Aggregates are computed on a 4-hour-stale-while-revalidate cadence per player; new observations invalidate cache entries that touch the same (commodity, station_id) pair. This keeps aggregate queries off the hot path while preserving recency.

Anti-gaming layer

Dropped. The simpler model has no fitness-gaming surface to defend against. A player feeding fake observations into their own log only hurts their own recommendations — there's no fitness for them to game, no quadratic selection weighting to bias, no offspring to spawn from manipulated parents. Wash trades show up as ~zero-margin observations and naturally get filtered by the "ignore observations with profit < 100 cr" heuristic in the aggregate queries.

The original anti-gaming detectors (acceptance-rate anomalies, outcome-vs-prediction skew, wash-trade signals, pattern-fitness velocity) are retired. The basic input-validation security gate (rate limits, prompt injection detection, cost caps) covers what remains.

Migration

The ARIATradingPattern table is 📐 Design-only and never shipped. Dropping it from the schema target is a doc-only change; no production data exists to migrate. The new ARIATradingObservation table is added at the next ARIA-related migration.

SYSTEMS/trade-dna-evolution.md is removed. A much shorter recommendation-engine spec lives inline in OPERATIONS/aria.md and the player-facing description in FEATURES/gameplay/aria-companion.md.

Consequences

Positive:

  • Recommendations are explainable. Every recommendation cites the aggregate that generated it; players can audit ARIA's reasoning. "Because you've done this 7 times for an average of +4,200 cr" beats "because pattern X has a fitness of 0.62".
  • No anti-gaming layer required. Dropping the genetic algorithm drops the fitness-gaming surface and the entire defense system that came with it.
  • Implementation simpler. Eight SQL aggregate queries and a recommendation surface beat a genetic algorithm + GC pass + anti-gaming detectors.
  • Observation log is auditable and append-only. A trade event produces exactly one (or two — buy + sell) observation rows. No mutation, no offspring, no row-deletion (except per-player data-export delete).
  • Aggregate cache is straightforward. Standard stale-while-revalidate; invalidation on observation insert is by (commodity, station_id) tuple match.
  • Privacy-first isolation preserved. ADR-0016's hard rule is unaffected; observations are partitioned by player_id like every other ARIA table.

Neutral:

  • No "evolutionary" narrative. Players who would have enjoyed reading "Trade DNA" lore lose that flavor. Acceptable trade for clarity. The "Adaptive" in ARIA's name still applies — recommendations adapt as observations accumulate.
  • No "trading style" convergence label. The old convergence detector emitted a Player.settings.aria_trading_style label when pattern lineages narrowed. Replaced by a heuristic: "your most-frequent action over the last 30 days is {commodity_class} arbitrage" — same outcome, simpler derivation.

Negative:

  • No automatic exploration of new strategies. The genetic algorithm could propose novel parameter combinations via mutation that the player had never tried; the simpler aggregate engine only surfaces recommendations the player has already done. This is accepted: novel-strategy proposals are an explicit feature gap that can be added later via a small "exploration-bias heuristic" (📐 future) without bringing back the full algorithm.
  • No quantitative pattern-fitness ranking across the player's history. The aggregate engine can rank by avg-profit-per-trip but doesn't produce a calibrated 0–1 fitness score. Acceptable — players don't need a calibrated score; they need a clear "this is your best repeated trade" answer.

Alternatives considered

Keep the genetic algorithm but drop only the anti-gaming layer (rejected). Considered this as a partial simplification. Rejected because the anti-gaming layer exists because the genetic algorithm has a fitness-gaming surface; you can't drop the layer without leaving the surface exposed. The anti-gaming layer's complexity is downstream of the algorithm's, so the right cut is upstream.

Keep the genetic algorithm but ship without the anti-gaming layer at launch (rejected). Considered shipping the algorithm undefended. Rejected because the algorithm's own learning loop directly biases recommendations; an undefended fitness surface is a meaningful exploit vector for any player willing to wash-trade or feed the model fabricated outcomes. Either keep both or drop both.

Drop the algorithm and don't replace it with anything (rejected). Considered cutting the recommendation surface entirely and leaving ARIA as memory-recall + dialogue only. Rejected because recommendations are a load-bearing feature — the trading-pattern evolution doc was complex but the output (suggested trades) is what players actually use. The simpler aggregate engine preserves the output.

Move recommendation logic into the multi-provider LLM (rejected). Considered letting the LLM read the player's observation log and generate recommendations directly via tool calls. Rejected because (a) it pushes unbounded prompt cost into every recommendation request; (b) the aggregates are deterministic and fast, no LLM-cost-per-recommendation overhead; (c) the LLM still adds value via narration of recommendations the aggregate engine generates — not by generating them itself.