@agent-e/core 1.1.3 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -31,8 +31,8 @@ var DEFAULT_THRESHOLDS = {
31
31
  maxAdjustmentPercent: 0.15,
32
32
  cooldownTicks: 15,
33
33
  // Currency (P13)
34
- arenaWinRate: 0.65,
35
- arenaHouseCut: 0.1,
34
+ poolWinRate: 0.65,
35
+ poolHouseCut: 0.1,
36
36
  // Population balance (P9)
37
37
  roleSwitchFrictionMax: 0.05,
38
38
  // >5% of population switching in one period = herd
@@ -63,7 +63,7 @@ var DEFAULT_THRESHOLDS = {
63
63
  disposalTradeWeightDiscount: 0.5
64
64
  };
65
65
  var PERSONA_HEALTHY_RANGES = {
66
- Gamer: { min: 0.2, max: 0.4 },
66
+ Active: { min: 0.2, max: 0.4 },
67
67
  Trader: { min: 0.05, max: 0.15 },
68
68
  Collector: { min: 0.05, max: 0.15 },
69
69
  Speculator: { min: 0, max: 0.1 },
@@ -73,14 +73,21 @@ var PERSONA_HEALTHY_RANGES = {
73
73
  Whale: { min: 0, max: 0.05 },
74
74
  Influencer: { min: 0, max: 0.05 }
75
75
  };
76
+ var DEFAULT_TICK_CONFIG = {
77
+ duration: 1,
78
+ unit: "tick",
79
+ mediumWindow: 10,
80
+ coarseWindow: 100
81
+ };
76
82
 
77
83
  // src/Observer.ts
78
84
  var Observer = class {
79
- constructor() {
85
+ constructor(tickConfig) {
80
86
  this.previousMetrics = null;
81
87
  this.previousPrices = {};
82
88
  this.customMetricFns = {};
83
89
  this.anchorBaseline = null;
90
+ this.tickConfig = { ...DEFAULT_TICK_CONFIG, ...tickConfig };
84
91
  }
85
92
  registerCustomMetric(name, fn) {
86
93
  this.customMetricFns[name] = fn;
@@ -184,18 +191,18 @@ var Observer = class {
184
191
  const satisfactions = Object.values(state.agentSatisfaction ?? {});
185
192
  const avgSatisfaction = satisfactions.length > 0 ? satisfactions.reduce((s, v) => s + v, 0) / satisfactions.length : 80;
186
193
  const blockedAgentCount = satisfactions.filter((s) => s < 20).length;
187
- const timeToValue = tick > 0 ? Math.max(0, 20 - tick * 0.1) : 20;
194
+ const timeToValue = totalAgents > 0 ? blockedAgentCount / totalAgents * 100 : 0;
188
195
  const poolSizes = { ...state.poolSizes ?? {} };
189
196
  if (!this.anchorBaseline && tick === 1 && totalSupply > 0) {
190
197
  this.anchorBaseline = {
191
- goldPerHour: totalSupply / Math.max(1, totalAgents),
192
- itemsPerGold: priceIndex > 0 ? 1 / priceIndex : 0
198
+ currencyPerPeriod: totalSupply / Math.max(1, totalAgents),
199
+ itemsPerCurrency: priceIndex > 0 ? 1 / priceIndex : 0
193
200
  };
194
201
  }
195
202
  let anchorRatioDrift = 0;
196
203
  if (this.anchorBaseline && totalAgents > 0) {
197
- const currentGoldPerHour = totalSupply / totalAgents;
198
- anchorRatioDrift = this.anchorBaseline.goldPerHour > 0 ? (currentGoldPerHour - this.anchorBaseline.goldPerHour) / this.anchorBaseline.goldPerHour : 0;
204
+ const currentCurrencyPerPeriod = totalSupply / totalAgents;
205
+ anchorRatioDrift = this.anchorBaseline.currencyPerPeriod > 0 ? (currentCurrencyPerPeriod - this.anchorBaseline.currencyPerPeriod) / this.anchorBaseline.currencyPerPeriod : 0;
199
206
  }
200
207
  let arbitrageIndex = 0;
201
208
  const priceKeys = Object.keys(prices).filter((k) => prices[k] > 0);
@@ -420,7 +427,7 @@ var P1_ProductionMatchesConsumption = {
420
427
  id: "P1",
421
428
  name: "Production Must Match Consumption",
422
429
  category: "supply_chain",
423
- description: "If producer rate < consumer rate, supply deficit kills the economy. 105 ore rotting at Forge (V0.4.6) happened because this was out of balance.",
430
+ description: "If producer rate < consumer rate, supply deficit kills the economy. Raw materials piling at production locations happened because this was out of balance.",
424
431
  check(metrics, _thresholds) {
425
432
  const { supplyByResource, demandSignals, populationByRole } = metrics;
426
433
  const violations = [];
@@ -431,19 +438,22 @@ var P1_ProductionMatchesConsumption = {
431
438
  violations.push(resource);
432
439
  }
433
440
  }
434
- const crafters = (populationByRole["Crafter"] ?? 0) + (populationByRole["Alchemist"] ?? 0);
435
- const consumers = populationByRole["Fighter"] ?? 0;
436
- const productionDeficit = consumers > 0 && crafters / consumers < 0.1;
437
- if (violations.length > 0 || productionDeficit) {
441
+ const roleEntries = Object.entries(populationByRole).sort((a, b) => b[1] - a[1]);
442
+ const totalPop = metrics.totalAgents;
443
+ const dominantRole = roleEntries[0];
444
+ const dominantCount = dominantRole?.[1] ?? 0;
445
+ const dominantShare = totalPop > 0 ? dominantCount / totalPop : 0;
446
+ const populationImbalance = dominantShare > 0.4 && violations.length > 0;
447
+ if (violations.length > 0 || populationImbalance) {
438
448
  return {
439
449
  violated: true,
440
450
  severity: 7,
441
- evidence: { scarceResources: violations, crafters, consumers },
451
+ evidence: { scarceResources: violations, dominantRole: dominantRole?.[0], dominantShare },
442
452
  suggestedAction: {
443
- parameter: "craftingCost",
453
+ parameter: "productionCost",
444
454
  direction: "decrease",
445
455
  magnitude: 0.15,
446
- reasoning: "Lower crafting cost to incentivise more production."
456
+ reasoning: "Lower production cost to incentivise more production."
447
457
  },
448
458
  confidence: violations.length > 0 ? 0.85 : 0.6,
449
459
  estimatedLag: 10
@@ -456,26 +466,28 @@ var P2_ClosedLoopsNeedDirectHandoff = {
456
466
  id: "P2",
457
467
  name: "Closed Loops Need Direct Handoff",
458
468
  category: "supply_chain",
459
- description: "Raw materials listed on an open market create noise and liquidity problems. Gatherers delivering ore directly to Crafters at the Forge is faster and cleaner.",
469
+ description: "Raw materials listed on an open market create noise and liquidity problems. Gatherers delivering raw materials directly to producers at production zones is faster and cleaner.",
460
470
  check(metrics, _thresholds) {
461
- const { supplyByResource, prices, velocity } = metrics;
462
- const ore = supplyByResource["ore"] ?? 0;
463
- const wood = supplyByResource["wood"] ?? 0;
464
- const orePrice = prices["ore"] ?? 0;
465
- const woodPrice = prices["wood"] ?? 0;
466
- const oreBacklog = ore > 50 && orePrice > 0;
467
- const woodBacklog = wood > 50 && woodPrice > 0;
471
+ const { supplyByResource, prices, velocity, totalAgents } = metrics;
472
+ const avgSupplyPerAgent = totalAgents > 0 ? Object.values(supplyByResource).reduce((s, v) => s + v, 0) / totalAgents : 0;
473
+ const backlogResources = [];
474
+ for (const [resource, supply] of Object.entries(supplyByResource)) {
475
+ const price = prices[resource] ?? 0;
476
+ if (supply > avgSupplyPerAgent * 0.5 && price > 0) {
477
+ backlogResources.push(resource);
478
+ }
479
+ }
468
480
  const stagnant = velocity < 3;
469
- if ((oreBacklog || woodBacklog) && stagnant) {
481
+ if (backlogResources.length > 0 && stagnant) {
470
482
  return {
471
483
  violated: true,
472
484
  severity: 5,
473
- evidence: { ore, wood, velocity },
485
+ evidence: { backlogResources, velocity },
474
486
  suggestedAction: {
475
- parameter: "auctionFee",
487
+ parameter: "transactionFee",
476
488
  direction: "increase",
477
489
  magnitude: 0.2,
478
- reasoning: "Raise AH fees to discourage raw material listings. Direct hand-off at production zones is the correct channel."
490
+ reasoning: "Raise market fees to discourage raw material listings. Direct hand-off at production zones is the correct channel."
479
491
  },
480
492
  confidence: 0.7,
481
493
  estimatedLag: 5
@@ -488,29 +500,31 @@ var P3_BootstrapCapitalCoversFirstTransaction = {
488
500
  id: "P3",
489
501
  name: "Bootstrap Capital Covers First Transaction",
490
502
  category: "supply_chain",
491
- description: "A new producer must be able to afford their first transaction without selling anything first. Crafter starting with 15g but needing 30g to accept ore hand-off blocks the entire supply chain from tick 1.",
503
+ description: "A new producer must be able to afford their first transaction without selling anything first. Producer starting with low currency but needing more to accept raw material hand-off blocks the entire supply chain from tick 1.",
492
504
  check(metrics, _thresholds) {
493
- const { populationByRole, supplyByResource, prices } = metrics;
494
- const crafters = populationByRole["Crafter"] ?? 0;
495
- const alchemists = populationByRole["Alchemist"] ?? 0;
496
- const weapons = supplyByResource["weapons"] ?? 0;
497
- const potions = supplyByResource["potions"] ?? 0;
498
- const crafterBootstrapFail = crafters > 0 && weapons === 0 && (prices["ore"] ?? 0) > 0;
499
- const alchemistBootstrapFail = alchemists > 0 && potions === 0 && (prices["wood"] ?? 0) > 0;
500
- if (crafterBootstrapFail || alchemistBootstrapFail) {
501
- return {
502
- violated: true,
503
- severity: 8,
504
- evidence: { crafters, alchemists, weapons, potions },
505
- suggestedAction: {
506
- parameter: "craftingCost",
507
- direction: "decrease",
508
- magnitude: 0.3,
509
- reasoning: "Producers cannot complete first transaction. Lower production cost to unblock bootstrap."
510
- },
511
- confidence: 0.8,
512
- estimatedLag: 3
513
- };
505
+ const { populationByRole, supplyByResource, prices, totalAgents } = metrics;
506
+ const totalProducers = Object.values(populationByRole).reduce((s, v) => s + v, 0);
507
+ if (totalProducers > 0) {
508
+ for (const [resource, supply] of Object.entries(supplyByResource)) {
509
+ if (supply === 0) {
510
+ const anyInputPriced = Object.values(prices).some((p) => p > 0);
511
+ if (anyInputPriced) {
512
+ return {
513
+ violated: true,
514
+ severity: 8,
515
+ evidence: { resource, totalProducers, supply },
516
+ suggestedAction: {
517
+ parameter: "productionCost",
518
+ direction: "decrease",
519
+ magnitude: 0.3,
520
+ reasoning: "Producers cannot complete first transaction. Lower production cost to unblock bootstrap."
521
+ },
522
+ confidence: 0.8,
523
+ estimatedLag: 3
524
+ };
525
+ }
526
+ }
527
+ }
514
528
  }
515
529
  return { violated: false };
516
530
  }
@@ -519,41 +533,38 @@ var P4_MaterialsFlowFasterThanCooldown = {
519
533
  id: "P4",
520
534
  name: "Materials Flow Faster Than Cooldown",
521
535
  category: "supply_chain",
522
- description: "Input delivery rate must exceed or match production cooldown rate. If Crafters craft every 5 ticks but only receive ore every 10 ticks, they starve regardless of supply levels.",
536
+ description: "Input delivery rate must exceed or match production cooldown rate. If producers craft every 5 ticks but only receive raw materials every 10 ticks, they starve regardless of supply levels.",
523
537
  check(metrics, _thresholds) {
524
- const { supplyByResource, populationByRole, velocity } = metrics;
525
- const gatherers = populationByRole["Gatherer"] ?? 0;
526
- const crafters = populationByRole["Crafter"] ?? 0;
527
- const alchemists = populationByRole["Alchemist"] ?? 0;
528
- const producers = crafters + alchemists;
529
- const gathererToProcuderRatio = gatherers / Math.max(1, producers);
530
- if (producers > 0 && gathererToProcuderRatio < 0.5 && velocity < 5) {
538
+ const { supplyByResource, populationByRole, velocity, totalAgents } = metrics;
539
+ const totalSupply = Object.values(supplyByResource).reduce((s, v) => s + v, 0);
540
+ const avgSupplyPerAgent = totalAgents > 0 ? totalSupply / totalAgents : 0;
541
+ const roleEntries = Object.entries(populationByRole);
542
+ const totalRoles = roleEntries.length;
543
+ if (totalRoles >= 2 && velocity < 5 && avgSupplyPerAgent < 0.5) {
531
544
  return {
532
545
  violated: true,
533
546
  severity: 5,
534
- evidence: { gatherers, crafters, alchemists, gathererToProcuderRatio },
547
+ evidence: { avgSupplyPerAgent, velocity, totalRoles },
535
548
  suggestedAction: {
536
- parameter: "miningYield",
549
+ parameter: "yieldRate",
537
550
  direction: "increase",
538
551
  magnitude: 0.15,
539
- reasoning: "Too few gatherers relative to producers. Increase yield to compensate."
552
+ reasoning: "Low supply per agent with stagnant velocity. Increase yield to compensate."
540
553
  },
541
554
  confidence: 0.65,
542
555
  estimatedLag: 8
543
556
  };
544
557
  }
545
- const ore = supplyByResource["ore"] ?? 0;
546
- const wood = supplyByResource["wood"] ?? 0;
547
- if (ore > 80 || wood > 80) {
558
+ if (avgSupplyPerAgent > 2) {
548
559
  return {
549
560
  violated: true,
550
561
  severity: 4,
551
- evidence: { ore, wood, gatherers, producers },
562
+ evidence: { avgSupplyPerAgent, totalSupply, totalAgents },
552
563
  suggestedAction: {
553
- parameter: "miningYield",
564
+ parameter: "yieldRate",
554
565
  direction: "decrease",
555
566
  magnitude: 0.2,
556
- reasoning: "Raw materials piling up. Gatherers outpacing producers."
567
+ reasoning: "Raw materials piling up. Extractors outpacing producers."
557
568
  },
558
569
  confidence: 0.8,
559
570
  estimatedLag: 5
@@ -578,7 +589,7 @@ var P60_SurplusDisposalAsymmetry = {
578
589
  discount: thresholds.disposalTradeWeightDiscount
579
590
  },
580
591
  suggestedAction: {
581
- parameter: "craftingCost",
592
+ parameter: "productionCost",
582
593
  direction: "decrease",
583
594
  magnitude: 0.1,
584
595
  reasoning: `${(disposalTradeRatio * 100).toFixed(0)}% of trades are surplus disposal. Price signals unreliable as demand indicators. Lower production costs to shift balance toward deliberate production-for-sale. ADVISORY: Weight disposal-trade prices at ${thresholds.disposalTradeWeightDiscount}\xD7 in index calculations.`
@@ -603,7 +614,7 @@ var P5_ProfitabilityIsCompetitive = {
603
614
  id: "P5",
604
615
  name: "Profitability Is Competitive, Not Absolute",
605
616
  category: "incentive",
606
- description: "Any profitability formula that returns the same number regardless of how many agents are already in that role will cause stampedes. 97 Traders happened because profit = transactions \xD7 10 with no competition denominator.",
617
+ description: "Any profitability formula that returns the same number regardless of how many agents are already in that role will cause stampedes. 97 intermediaries happened because profit = transactions \xD7 10 with no competition denominator.",
607
618
  check(metrics, thresholds) {
608
619
  const { roleShares, populationByRole } = metrics;
609
620
  const highShareRoles = [];
@@ -621,7 +632,7 @@ var P5_ProfitabilityIsCompetitive = {
621
632
  population: populationByRole[dominantRole]
622
633
  },
623
634
  suggestedAction: {
624
- parameter: "auctionFee",
635
+ parameter: "transactionFee",
625
636
  direction: "increase",
626
637
  magnitude: thresholds.maxAdjustmentPercent,
627
638
  reasoning: `${dominantRole} share at ${((roleShares[dominantRole] ?? 0) * 100).toFixed(0)}%. Likely stampede from non-competitive profitability formula. Raise market friction to slow role accumulation.`
@@ -647,7 +658,7 @@ var P6_CrowdingMultiplierOnAllRoles = {
647
658
  severity: 5,
648
659
  evidence: { role, share },
649
660
  suggestedAction: {
650
- parameter: "craftingCost",
661
+ parameter: "productionCost",
651
662
  direction: "increase",
652
663
  magnitude: 0.1,
653
664
  reasoning: `${role} at ${(share * 100).toFixed(0)}% \u2014 no crowding pressure detected. Apply role-specific cost increase to simulate saturation.`
@@ -664,28 +675,33 @@ var P7_NonSpecialistsSubsidiseSpecialists = {
664
675
  id: "P7",
665
676
  name: "Non-Specialists Subsidise Specialists in Zero-Sum Games",
666
677
  category: "incentive",
667
- description: "In zero-sum pools (arena, staking), the math only works if non-specialists overpay relative to specialists. If the pool is >70% specialists, there is no one left to subsidise and the pot drains.",
678
+ description: "In zero-sum pools (competitive pool, staking), the math only works if non-specialists overpay relative to specialists. If the pool is >70% specialists, there is no one left to subsidise and the pot drains.",
668
679
  check(metrics, _thresholds) {
669
- const { populationByRole, poolSizes } = metrics;
670
- const arenaPot = poolSizes["arena"] ?? poolSizes["arenaPot"] ?? 0;
671
- if (arenaPot <= 0) return { violated: false };
672
- const fighters = populationByRole["Fighter"] ?? 0;
673
- const total = metrics.totalAgents;
674
- const fighterShare = fighters / Math.max(1, total);
675
- if (fighterShare > 0.7 && arenaPot < 100) {
676
- return {
677
- violated: true,
678
- severity: 6,
679
- evidence: { fighterShare, arenaPot },
680
- suggestedAction: {
681
- parameter: "arenaEntryFee",
682
- direction: "decrease",
683
- magnitude: 0.1,
684
- reasoning: "Arena pot draining \u2014 too many specialists, not enough subsidising non-specialists. Lower entry fee to attract diverse participants."
685
- },
686
- confidence: 0.75,
687
- estimatedLag: 5
688
- };
680
+ const { poolSizes } = metrics;
681
+ for (const [poolName, poolSize] of Object.entries(poolSizes)) {
682
+ if (poolSize <= 0) continue;
683
+ const roleEntries = Object.entries(metrics.populationByRole);
684
+ if (roleEntries.length === 0) continue;
685
+ const [dominantRole, dominantPop] = roleEntries.reduce(
686
+ (max, entry) => entry[1] > max[1] ? entry : max
687
+ );
688
+ const total = metrics.totalAgents;
689
+ const dominantShare = dominantPop / Math.max(1, total);
690
+ if (dominantShare > 0.7 && poolSize < 100) {
691
+ return {
692
+ violated: true,
693
+ severity: 6,
694
+ evidence: { poolName, poolSize, dominantRole, dominantShare },
695
+ suggestedAction: {
696
+ parameter: "entryFee",
697
+ direction: "decrease",
698
+ magnitude: 0.1,
699
+ reasoning: `Pool "${poolName}" draining (${poolSize}) \u2014 ${dominantRole} at ${(dominantShare * 100).toFixed(0)}%. Too many specialists, not enough subsidising non-specialists. Lower entry fee to attract diverse participants.`
700
+ },
701
+ confidence: 0.75,
702
+ estimatedLag: 5
703
+ };
704
+ }
689
705
  }
690
706
  return { violated: false };
691
707
  }
@@ -694,7 +710,7 @@ var P8_RegulatorCannotFightDesign = {
694
710
  id: "P8",
695
711
  name: "Regulator Cannot Fight the Design",
696
712
  category: "incentive",
697
- description: "If the economy is designed to have a majority role (e.g. 55% Fighters), the regulator must know this and exempt that role from population suppression. AgentE at tick 1 seeing 55% Fighters and slashing arena rewards is overreach.",
713
+ description: "If the economy is designed to have a majority role (e.g. dominant role exceeds 55%), the regulator must know this and exempt that role from population suppression. AgentE at tick 1 seeing dominant role exceeds 55% and slashing competitive pool rewards is overreach.",
698
714
  check(metrics, _thresholds) {
699
715
  const { roleShares, avgSatisfaction } = metrics;
700
716
  if (avgSatisfaction < 45) {
@@ -705,7 +721,7 @@ var P8_RegulatorCannotFightDesign = {
705
721
  severity: 4,
706
722
  evidence: { dominantRole: dominantRole[0], share: dominantRole[1], avgSatisfaction },
707
723
  suggestedAction: {
708
- parameter: "arenaReward",
724
+ parameter: "rewardRate",
709
725
  direction: "increase",
710
726
  magnitude: 0.1,
711
727
  reasoning: `Low satisfaction with ${dominantRole[0]} dominant. Regulator may be suppressing a structurally necessary role. Ease pressure on dominant role rewards.`
@@ -740,7 +756,7 @@ var P9_RoleSwitchingNeedsFriction = {
740
756
  severity: 5,
741
757
  evidence: { totalChurnRate: totalChurn, churnByRole },
742
758
  suggestedAction: {
743
- parameter: "craftingCost",
759
+ parameter: "productionCost",
744
760
  direction: "increase",
745
761
  magnitude: 0.05,
746
762
  reasoning: `Role switch rate ${(totalChurn * 100).toFixed(1)}% exceeds friction threshold. Increase production costs to slow herd movement.`
@@ -757,7 +773,7 @@ var P10_SpawnWeightingUsesInversePopulation = {
757
773
  id: "P10",
758
774
  name: "Spawn Weighting Uses Inverse Population",
759
775
  category: "population",
760
- description: "New agents should preferentially fill the least-populated roles. Flat spawn probability causes initial imbalances to compound.",
776
+ description: "New agents should preferentially fill the least-populated roles. Flat entry probability causes initial imbalances to compound.",
761
777
  check(metrics, _thresholds) {
762
778
  const { roleShares } = metrics;
763
779
  if (Object.keys(roleShares).length === 0) return { violated: false };
@@ -772,10 +788,10 @@ var P10_SpawnWeightingUsesInversePopulation = {
772
788
  severity: 4,
773
789
  evidence: { roleShares, stdDev, leastPopulatedRole: minRole?.[0] },
774
790
  suggestedAction: {
775
- parameter: "miningYield",
791
+ parameter: "yieldRate",
776
792
  direction: "increase",
777
793
  magnitude: 0.05,
778
- reasoning: `High role share variance (\u03C3=${stdDev.toFixed(2)}). Spawn weighting may not be filling under-populated roles. Increasing yield makes under-populated producer roles more attractive.`
794
+ reasoning: `High role share variance (\u03C3=${stdDev.toFixed(2)}). Entry weighting may not be filling under-populated roles. Increasing yield makes under-populated producer roles more attractive.`
779
795
  },
780
796
  confidence: 0.6,
781
797
  estimatedLag: 20
@@ -798,7 +814,7 @@ var P11_TwoTierPressure = {
798
814
  severity: 6,
799
815
  evidence: { role, share },
800
816
  suggestedAction: {
801
- parameter: "auctionFee",
817
+ parameter: "transactionFee",
802
818
  direction: "increase",
803
819
  magnitude: 0.15,
804
820
  reasoning: `${role} at ${(share * 100).toFixed(0)}% \u2014 continuous pressure was insufficient. Hard intervention needed alongside resumed continuous pressure.`
@@ -826,7 +842,7 @@ var P46_PersonaDiversity = {
826
842
  severity: 5,
827
843
  evidence: { dominantPersona: persona, share, personaDistribution },
828
844
  suggestedAction: {
829
- parameter: "arenaReward",
845
+ parameter: "rewardRate",
830
846
  direction: "increase",
831
847
  magnitude: 0.1,
832
848
  reasoning: `${persona} persona at ${(share * 100).toFixed(0)}% \u2014 behavioral monoculture. Diversify reward structures to attract other persona types.`
@@ -843,7 +859,7 @@ var P46_PersonaDiversity = {
843
859
  severity: 3,
844
860
  evidence: { significantClusters, required: thresholds.personaMinClusters },
845
861
  suggestedAction: {
846
- parameter: "auctionFee",
862
+ parameter: "transactionFee",
847
863
  direction: "decrease",
848
864
  magnitude: 0.05,
849
865
  reasoning: `Only ${significantClusters} significant persona clusters (need ${thresholds.personaMinClusters}). Lower trade barriers to attract non-dominant persona types.`
@@ -876,7 +892,7 @@ var P12_OnePrimaryFaucet = {
876
892
  severity: 5,
877
893
  evidence: { netFlow, faucetVolume, sinkVolume },
878
894
  suggestedAction: {
879
- parameter: "craftingCost",
895
+ parameter: "productionCost",
880
896
  direction: "increase",
881
897
  magnitude: 0.15,
882
898
  reasoning: `Net flow +${netFlow.toFixed(1)} g/t. Inflationary. Increase crafting cost (primary sink) to balance faucet output.`
@@ -891,7 +907,7 @@ var P12_OnePrimaryFaucet = {
891
907
  severity: 4,
892
908
  evidence: { netFlow, faucetVolume, sinkVolume },
893
909
  suggestedAction: {
894
- parameter: "craftingCost",
910
+ parameter: "productionCost",
895
911
  direction: "decrease",
896
912
  magnitude: 0.15,
897
913
  reasoning: `Net flow ${netFlow.toFixed(1)} g/t. Deflationary. Decrease crafting cost to ease sink pressure.`
@@ -907,36 +923,40 @@ var P13_PotsAreZeroSumAndSelfRegulate = {
907
923
  id: "P13",
908
924
  name: "Pots Self-Regulate with Correct Multiplier",
909
925
  category: "currency",
910
- description: "Arena pot math: winRate \xD7 multiplier > (1 - houseCut) drains the pot. At 65% win rate, multiplier must be \u2264 1.38. We use 1.5 for slight surplus buffer.",
926
+ description: "Competitive pot math: winRate \xD7 multiplier > (1 - houseCut) drains the pot. At 65% win rate, multiplier must be \u2264 1.38. We use 1.5 for slight surplus buffer.",
911
927
  check(metrics, thresholds) {
912
- const { poolSizes } = metrics;
913
- const arenaPot = poolSizes["arena"] ?? poolSizes["arenaPot"] ?? 0;
914
- const fighters = metrics.populationByRole["Fighter"] ?? 0;
915
- if (fighters > 5 && arenaPot < 50) {
916
- const { arenaWinRate, arenaHouseCut } = thresholds;
917
- const maxSustainableMultiplier = (1 - arenaHouseCut) / arenaWinRate;
918
- return {
919
- violated: true,
920
- severity: 7,
921
- evidence: { arenaPot, fighters, maxSustainableMultiplier },
922
- suggestedAction: {
923
- parameter: "arenaReward",
924
- direction: "decrease",
925
- magnitude: 0.15,
926
- reasoning: `Arena pot at ${arenaPot.toFixed(0)}g with ${fighters} fighters. Sustainable multiplier \u2264 ${maxSustainableMultiplier.toFixed(2)}. Reduce reward multiplier to prevent pot drain.`
927
- },
928
- confidence: 0.85,
929
- estimatedLag: 3
930
- };
928
+ const { poolSizes, populationByRole } = metrics;
929
+ const totalAgents = metrics.totalAgents;
930
+ const roleEntries = Object.entries(populationByRole).sort((a, b) => b[1] - a[1]);
931
+ const dominantRole = roleEntries[0]?.[0];
932
+ const dominantCount = roleEntries[0]?.[1] ?? 0;
933
+ for (const [poolName, poolSize] of Object.entries(poolSizes)) {
934
+ if (dominantCount > 5 && poolSize < 50) {
935
+ const { poolWinRate, poolHouseCut } = thresholds;
936
+ const maxSustainableMultiplier = (1 - poolHouseCut) / poolWinRate;
937
+ return {
938
+ violated: true,
939
+ severity: 7,
940
+ evidence: { pool: poolName, poolSize, participants: dominantCount, maxSustainableMultiplier },
941
+ suggestedAction: {
942
+ parameter: "rewardRate",
943
+ direction: "decrease",
944
+ magnitude: 0.15,
945
+ reasoning: `${poolName} pool at ${poolSize.toFixed(0)} currency with ${dominantCount} active participants. Sustainable multiplier \u2264 ${maxSustainableMultiplier.toFixed(2)}. Reduce reward multiplier to prevent pool drain.`
946
+ },
947
+ confidence: 0.85,
948
+ estimatedLag: 3
949
+ };
950
+ }
931
951
  }
932
952
  return { violated: false };
933
953
  }
934
954
  };
935
955
  var P14_TrackActualInjection = {
936
956
  id: "P14",
937
- name: "Track Actual Gold Injection, Not Value Creation",
957
+ name: "Track Actual Currency Injection, Not Value Creation",
938
958
  category: "currency",
939
- description: 'Counting resource gathering as "gold injected" is a lie. Gold only enters when Fighters spawn (100-150g each). Fake metrics break every downstream decision.',
959
+ description: 'Counting resource gathering as "currency injected" is misleading. Currency enters through faucet mechanisms (spawning, rewards). Fake metrics break every downstream decision.',
940
960
  check(metrics, _thresholds) {
941
961
  const { faucetVolume, netFlow, totalSupply } = metrics;
942
962
  const supplyGrowthRate = Math.abs(netFlow) / Math.max(1, totalSupply);
@@ -946,7 +966,7 @@ var P14_TrackActualInjection = {
946
966
  severity: 4,
947
967
  evidence: { faucetVolume, netFlow, supplyGrowthRate },
948
968
  suggestedAction: {
949
- parameter: "miningYield",
969
+ parameter: "yieldRate",
950
970
  direction: "decrease",
951
971
  magnitude: 0.1,
952
972
  reasoning: `Supply growing at ${(supplyGrowthRate * 100).toFixed(1)}%/tick. Verify gold injection tracking. Resources should not create gold directly.`
@@ -967,7 +987,6 @@ var P15_PoolsNeedCapAndDecay = {
967
987
  const { poolSizes, totalSupply } = metrics;
968
988
  const { poolCapPercent } = thresholds;
969
989
  for (const [pool, size] of Object.entries(poolSizes)) {
970
- if (pool === "arena" || pool === "arenaPot") continue;
971
990
  const shareOfSupply = size / Math.max(1, totalSupply);
972
991
  if (shareOfSupply > poolCapPercent * 2) {
973
992
  return {
@@ -975,7 +994,7 @@ var P15_PoolsNeedCapAndDecay = {
975
994
  severity: 6,
976
995
  evidence: { pool, size, shareOfSupply, cap: poolCapPercent },
977
996
  suggestedAction: {
978
- parameter: "auctionFee",
997
+ parameter: "transactionFee",
979
998
  direction: "decrease",
980
999
  magnitude: 0.1,
981
1000
  reasoning: `${pool} pool at ${(shareOfSupply * 100).toFixed(1)}% of supply (cap: ${(poolCapPercent * 100).toFixed(0)}%). Gold frozen. Lower fees to encourage circulation over accumulation.`
@@ -995,22 +1014,23 @@ var P16_WithdrawalPenaltyScales = {
995
1014
  description: "A 50-tick lock period with a penalty calculated as /100 means agents can exit after 1 tick and keep 99% of accrued yield. Penalty must scale linearly: (1 - ticksStaked/lockDuration) \xD7 yield.",
996
1015
  check(metrics, _thresholds) {
997
1016
  const { poolSizes, totalSupply } = metrics;
998
- const bankPool = poolSizes["bank"] ?? poolSizes["bankPool"] ?? 0;
999
1017
  const stakedEstimate = totalSupply * 0.15;
1000
- if (bankPool < 10 && stakedEstimate > 100) {
1001
- return {
1002
- violated: true,
1003
- severity: 3,
1004
- evidence: { bankPool, estimatedStaked: stakedEstimate },
1005
- suggestedAction: {
1006
- parameter: "auctionFee",
1007
- direction: "increase",
1008
- magnitude: 0.05,
1009
- reasoning: "Bank pool depleted while significant gold should be staked. Early withdrawals may be draining yield pool. Ensure withdrawal penalty scales with lock duration."
1010
- },
1011
- confidence: 0.45,
1012
- estimatedLag: 10
1013
- };
1018
+ for (const [poolName, poolSize] of Object.entries(poolSizes)) {
1019
+ if (poolSize < 10 && stakedEstimate > 100) {
1020
+ return {
1021
+ violated: true,
1022
+ severity: 3,
1023
+ evidence: { pool: poolName, poolSize, estimatedStaked: stakedEstimate },
1024
+ suggestedAction: {
1025
+ parameter: "transactionFee",
1026
+ direction: "increase",
1027
+ magnitude: 0.05,
1028
+ reasoning: `${poolName} pool depleted while significant currency should be locked. Early withdrawals may be draining the pool. Ensure withdrawal penalty scales with lock duration.`
1029
+ },
1030
+ confidence: 0.45,
1031
+ estimatedLag: 10
1032
+ };
1033
+ }
1014
1034
  }
1015
1035
  return { violated: false };
1016
1036
  }
@@ -1029,7 +1049,7 @@ var P32_VelocityAboveSupply = {
1029
1049
  severity: 4,
1030
1050
  evidence: { velocity, totalSupply, totalResources },
1031
1051
  suggestedAction: {
1032
- parameter: "auctionFee",
1052
+ parameter: "transactionFee",
1033
1053
  direction: "decrease",
1034
1054
  magnitude: 0.2,
1035
1055
  reasoning: `Velocity ${velocity}/t with ${totalResources} resources in system. Economy stagnant despite available supply. Lower trading friction.`
@@ -1065,7 +1085,7 @@ var P58_NoNaturalNumeraire = {
1065
1085
  meanPrice: mean
1066
1086
  },
1067
1087
  suggestedAction: {
1068
- parameter: "craftingCost",
1088
+ parameter: "productionCost",
1069
1089
  direction: "increase",
1070
1090
  magnitude: 0.1,
1071
1091
  reasoning: `Price coefficient of variation ${coeffOfVariation.toFixed(2)} with velocity ${velocity.toFixed(1)}. All items priced similarly in an active economy \u2014 no natural num\xE9raire emerging. If a designated currency exists, increase its sink demand to differentiate it.`
@@ -1092,7 +1112,7 @@ var P17_GracePeriodBeforeIntervention = {
1092
1112
  id: "P17",
1093
1113
  name: "Grace Period Before Intervention",
1094
1114
  category: "bootstrap",
1095
- description: "Any intervention before tick 50 is premature. The economy needs time to bootstrap with designed distributions. AgentE intervening at tick 1 against 55% Fighters (designed) killed the economy instantly.",
1115
+ description: "Any intervention before tick 50 is premature. The economy needs time to bootstrap with designed distributions. AgentE intervening at tick 1 against dominant role exceeds 55% (designed) killed the economy instantly.",
1096
1116
  check(metrics, _thresholds) {
1097
1117
  if (metrics.tick < 30 && metrics.avgSatisfaction < 40) {
1098
1118
  return {
@@ -1100,7 +1120,7 @@ var P17_GracePeriodBeforeIntervention = {
1100
1120
  severity: 7,
1101
1121
  evidence: { tick: metrics.tick, avgSatisfaction: metrics.avgSatisfaction },
1102
1122
  suggestedAction: {
1103
- parameter: "arenaEntryFee",
1123
+ parameter: "entryFee",
1104
1124
  direction: "decrease",
1105
1125
  magnitude: 0.2,
1106
1126
  reasoning: `Very low satisfaction at tick ${metrics.tick}. Intervention may have fired during grace period. Ease all costs to let economy bootstrap.`
@@ -1116,27 +1136,29 @@ var P18_FirstProducerNeedsStartingInventory = {
1116
1136
  id: "P18",
1117
1137
  name: "First Producer Needs Starting Inventory + Capital",
1118
1138
  category: "bootstrap",
1119
- description: "A Crafter with 0 weapons and 0 gold must sell nothing to get gold before they can buy ore. This creates a chicken-and-egg freeze. Starting inventory (2 weapons + 4 ore + 40g) breaks the deadlock.",
1139
+ description: "A producer with 0 resources and 0 currency must sell nothing to get currency before they can buy raw materials. This creates a chicken-and-egg freeze. Starting inventory (2 goods + 4 raw materials + 40 currency) breaks the deadlock.",
1120
1140
  check(metrics, _thresholds) {
1121
1141
  if (metrics.tick > 20) return { violated: false };
1122
- const weapons = metrics.supplyByResource["weapons"] ?? 0;
1123
- const potions = metrics.supplyByResource["potions"] ?? 0;
1124
- const crafters = metrics.populationByRole["Crafter"] ?? 0;
1125
- const alchemists = metrics.populationByRole["Alchemist"] ?? 0;
1126
- if (crafters > 0 && weapons === 0 || alchemists > 0 && potions === 0) {
1127
- return {
1128
- violated: true,
1129
- severity: 8,
1130
- evidence: { tick: metrics.tick, weapons, potions, crafters, alchemists },
1131
- suggestedAction: {
1132
- parameter: "craftingCost",
1133
- direction: "decrease",
1134
- magnitude: 0.5,
1135
- reasoning: "Bootstrap failure: producers have no products on tick 1-20. Drastically reduce production cost to allow immediate output."
1136
- },
1137
- confidence: 0.9,
1138
- estimatedLag: 2
1139
- };
1142
+ for (const [resource, supply] of Object.entries(metrics.supplyByResource)) {
1143
+ if (supply === 0) {
1144
+ for (const [role, population] of Object.entries(metrics.populationByRole)) {
1145
+ if (population > 0) {
1146
+ return {
1147
+ violated: true,
1148
+ severity: 8,
1149
+ evidence: { tick: metrics.tick, resource, supply, role, population },
1150
+ suggestedAction: {
1151
+ parameter: "productionCost",
1152
+ direction: "decrease",
1153
+ magnitude: 0.5,
1154
+ reasoning: `Bootstrap failure: ${role} exists but ${resource} supply is 0 on tick 1-20. Drastically reduce production cost to allow immediate output.`
1155
+ },
1156
+ confidence: 0.9,
1157
+ estimatedLag: 2
1158
+ };
1159
+ }
1160
+ }
1161
+ }
1140
1162
  }
1141
1163
  return { violated: false };
1142
1164
  }
@@ -1145,22 +1167,32 @@ var P19_StartingSupplyExceedsDemand = {
1145
1167
  id: "P19",
1146
1168
  name: "Starting Supply Exceeds Initial Demand",
1147
1169
  category: "bootstrap",
1148
- description: "Launch with more consumables than you think you need. Early scarcity creates an AH gridlock where everyone wants to buy and nobody has anything to sell.",
1170
+ description: "Launch with more consumables than you think you need. Early scarcity creates a market gridlock where everyone wants to buy and nobody has anything to sell.",
1149
1171
  check(metrics, _thresholds) {
1150
1172
  if (metrics.tick > 30) return { violated: false };
1151
- const fighters = metrics.populationByRole["Fighter"] ?? 0;
1152
- const weapons = metrics.supplyByResource["weapons"] ?? 0;
1153
- const potions = metrics.supplyByResource["potions"] ?? 0;
1154
- if (fighters > 5 && weapons < fighters * 0.5) {
1173
+ const roleEntries = Object.entries(metrics.populationByRole);
1174
+ if (roleEntries.length === 0) return { violated: false };
1175
+ const [mostPopulatedRole, population] = roleEntries.reduce(
1176
+ (max, entry) => entry[1] > max[1] ? entry : max
1177
+ );
1178
+ if (population < 5) return { violated: false };
1179
+ const totalResourceSupply = Object.values(metrics.supplyByResource).reduce((sum, s) => sum + s, 0);
1180
+ const resourcesPerAgent = totalResourceSupply / Math.max(1, population);
1181
+ if (resourcesPerAgent < 0.5) {
1155
1182
  return {
1156
1183
  violated: true,
1157
1184
  severity: 6,
1158
- evidence: { fighters, weapons, potions, weaponsPerFighter: weapons / Math.max(1, fighters) },
1185
+ evidence: {
1186
+ mostPopulatedRole,
1187
+ population,
1188
+ totalResourceSupply,
1189
+ resourcesPerAgent
1190
+ },
1159
1191
  suggestedAction: {
1160
- parameter: "arenaReward",
1192
+ parameter: "rewardRate",
1161
1193
  direction: "increase",
1162
1194
  magnitude: 0.2,
1163
- reasoning: `${fighters} fighters but only ${weapons} weapons. Cold-start scarcity. Boost arena reward to attract fighters even without weapons.`
1195
+ reasoning: `${mostPopulatedRole} (${population} agents) has insufficient resources (${resourcesPerAgent.toFixed(2)} per agent). Cold-start scarcity. Boost competitive pool reward to attract participation despite scarcity.`
1164
1196
  },
1165
1197
  confidence: 0.75,
1166
1198
  estimatedLag: 5
@@ -1180,7 +1212,7 @@ var P20_DecayPreventsAccumulation = {
1180
1212
  id: "P20",
1181
1213
  name: "Decay Prevents Accumulation",
1182
1214
  category: "feedback",
1183
- description: "Resources without decay create infinite hoarding. A Gatherer who never sells has 500 ore rotting in their pocket while Crafters starve. 2-10% decay per period forces circulation.",
1215
+ description: "Resources without decay create infinite hoarding. A gatherer who never sells has 500 raw materials rotting in their pocket while producers starve. 2-10% decay per period forces circulation.",
1184
1216
  check(metrics, _thresholds) {
1185
1217
  const { supplyByResource, velocity, totalAgents } = metrics;
1186
1218
  const totalResources = Object.values(supplyByResource).reduce((s, v) => s + v, 0);
@@ -1191,7 +1223,7 @@ var P20_DecayPreventsAccumulation = {
1191
1223
  severity: 4,
1192
1224
  evidence: { totalResources, resourcesPerAgent, velocity },
1193
1225
  suggestedAction: {
1194
- parameter: "miningYield",
1226
+ parameter: "yieldRate",
1195
1227
  direction: "decrease",
1196
1228
  magnitude: 0.1,
1197
1229
  reasoning: `${totalResources.toFixed(0)} resources with velocity ${velocity}/t. Likely hoarding. Reduce yield to increase scarcity and force circulation.`
@@ -1207,7 +1239,7 @@ var P21_PriceFromGlobalSupply = {
1207
1239
  id: "P21",
1208
1240
  name: "Price Reflects Global Supply, Not Just AH Listings",
1209
1241
  category: "feedback",
1210
- description: "If prices only update from Auction House activity, agents with hoarded inventory see artificially high prices and keep gathering when they should stop.",
1242
+ description: "If prices only update from market activity, agents with hoarded inventory see artificially high prices and keep gathering when they should stop.",
1211
1243
  check(metrics, _thresholds) {
1212
1244
  const { priceVolatility, supplyByResource, prices } = metrics;
1213
1245
  for (const resource of Object.keys(prices)) {
@@ -1219,7 +1251,7 @@ var P21_PriceFromGlobalSupply = {
1219
1251
  severity: 3,
1220
1252
  evidence: { resource, volatility, supply, price: prices[resource] },
1221
1253
  suggestedAction: {
1222
- parameter: "auctionFee",
1254
+ parameter: "transactionFee",
1223
1255
  direction: "increase",
1224
1256
  magnitude: 0.05,
1225
1257
  reasoning: `${resource} price volatile (${(volatility * 100).toFixed(0)}%) despite supply ${supply}. Price may not reflect global inventory. Increase trading friction to stabilise.`
@@ -1239,23 +1271,36 @@ var P22_MarketAwarenessPreventsSurplus = {
1239
1271
  description: "Producers who craft without checking market prices will create surpluses that crash prices. Agents need to see prices before deciding to produce.",
1240
1272
  check(metrics, _thresholds) {
1241
1273
  const { supplyByResource, prices, productionIndex } = metrics;
1242
- const weapons = supplyByResource["weapons"] ?? 0;
1243
- const weaponPrice = prices["weapons"] ?? 0;
1244
- const healthyWeaponPrice = 30;
1245
- if (weapons > 100 && weaponPrice < healthyWeaponPrice * 0.5 && productionIndex > 0) {
1246
- return {
1247
- violated: true,
1248
- severity: 4,
1249
- evidence: { weapons, weaponPrice, productionIndex },
1250
- suggestedAction: {
1251
- parameter: "craftingCost",
1252
- direction: "increase",
1253
- magnitude: 0.1,
1254
- reasoning: `${weapons} weapons with price ${weaponPrice.toFixed(0)}g but still producing. Producers appear unaware of market. Raise production cost to slow output.`
1255
- },
1256
- confidence: 0.7,
1257
- estimatedLag: 8
1258
- };
1274
+ const priceValues = Object.values(prices).filter((p) => p > 0);
1275
+ if (priceValues.length === 0) return { violated: false };
1276
+ const sortedPrices = [...priceValues].sort((a, b) => a - b);
1277
+ const medianPrice = sortedPrices[Math.floor(sortedPrices.length / 2)] ?? 0;
1278
+ for (const [resource, price] of Object.entries(prices)) {
1279
+ if (price <= 0) continue;
1280
+ const supply = supplyByResource[resource] ?? 0;
1281
+ const priceDeviation = price / Math.max(1, medianPrice);
1282
+ if (priceDeviation < 0.33 && supply > 100 && productionIndex > 0) {
1283
+ return {
1284
+ violated: true,
1285
+ severity: 4,
1286
+ evidence: {
1287
+ resource,
1288
+ price,
1289
+ medianPrice,
1290
+ priceDeviation,
1291
+ supply,
1292
+ productionIndex
1293
+ },
1294
+ suggestedAction: {
1295
+ parameter: "productionCost",
1296
+ direction: "increase",
1297
+ magnitude: 0.1,
1298
+ reasoning: `${resource} price ${price.toFixed(0)} is ${(priceDeviation * 100).toFixed(0)}% of median (${medianPrice.toFixed(0)}). Supply ${supply} units but still producing. Producers appear unaware of market. Raise production cost to slow output.`
1299
+ },
1300
+ confidence: 0.7,
1301
+ estimatedLag: 8
1302
+ };
1303
+ }
1259
1304
  }
1260
1305
  return { violated: false };
1261
1306
  }
@@ -1264,7 +1309,7 @@ var P23_ProfitabilityFactorsFeasibility = {
1264
1309
  id: "P23",
1265
1310
  name: "Profitability Factors Execution Feasibility",
1266
1311
  category: "feedback",
1267
- description: "An agent who calculates profit = weapon_price - ore_cost but has no gold to buy ore is chasing phantom profit. Feasibility (can I afford the inputs?) must be part of the profitability calc.",
1312
+ description: "An agent who calculates profit = goods_price - materials_cost but has no currency to buy raw materials is chasing phantom profit. Feasibility (can I afford the inputs?) must be part of the profitability calc.",
1268
1313
  check(metrics, _thresholds) {
1269
1314
  const { avgSatisfaction, blockedAgentCount, totalAgents } = metrics;
1270
1315
  const blockedFraction = blockedAgentCount / Math.max(1, totalAgents);
@@ -1274,7 +1319,7 @@ var P23_ProfitabilityFactorsFeasibility = {
1274
1319
  severity: 5,
1275
1320
  evidence: { blockedFraction, blockedAgentCount, avgSatisfaction },
1276
1321
  suggestedAction: {
1277
- parameter: "craftingCost",
1322
+ parameter: "productionCost",
1278
1323
  direction: "decrease",
1279
1324
  magnitude: 0.15,
1280
1325
  reasoning: `${(blockedFraction * 100).toFixed(0)}% of agents blocked with low satisfaction. Agents may have roles they cannot afford to execute. Lower production costs to restore feasibility.`
@@ -1300,7 +1345,7 @@ var P24_BlockedAgentsDecayFaster = {
1300
1345
  severity: 5,
1301
1346
  evidence: { blockedFraction, blockedAgentCount, churnRate },
1302
1347
  suggestedAction: {
1303
- parameter: "auctionFee",
1348
+ parameter: "transactionFee",
1304
1349
  direction: "decrease",
1305
1350
  magnitude: 0.15,
1306
1351
  reasoning: `${(blockedFraction * 100).toFixed(0)}% of agents blocked. Blocked agents churn silently, skewing metrics. Lower fees to unblock market participation.`
@@ -1325,26 +1370,35 @@ var P25_CorrectLeversForCorrectProblems = {
1325
1370
  id: "P25",
1326
1371
  name: "Target the Correct Lever",
1327
1372
  category: "regulator",
1328
- description: "Adjusting sinks for supply-side inflation is wrong. Inflation from too much gathering \u2192 reduce mining yield. Inflation from pot payout \u2192 reduce reward multiplier. Matching lever to cause prevents oscillation.",
1373
+ description: "Adjusting sinks for supply-side inflation is wrong. Inflation from too much gathering \u2192 reduce yield rate. Inflation from pot payout \u2192 reduce reward multiplier. Matching lever to cause prevents oscillation.",
1329
1374
  check(metrics, thresholds) {
1330
1375
  const { netFlow, supplyByResource } = metrics;
1331
- const ore = supplyByResource["ore"] ?? 0;
1332
- const wood = supplyByResource["wood"] ?? 0;
1333
- const resourceFlood = ore + wood > 100;
1334
- if (netFlow > thresholds.netFlowWarnThreshold && resourceFlood) {
1335
- return {
1336
- violated: true,
1337
- severity: 4,
1338
- evidence: { netFlow, ore, wood },
1339
- suggestedAction: {
1340
- parameter: "miningYield",
1341
- direction: "decrease",
1342
- magnitude: 0.15,
1343
- reasoning: `Inflation with raw material backlog (ore ${ore}, wood ${wood}). Root cause is gathering. Correct lever: miningYield, not fees.`
1344
- },
1345
- confidence: 0.75,
1346
- estimatedLag: 8
1347
- };
1376
+ const resourceEntries = Object.entries(supplyByResource);
1377
+ if (resourceEntries.length === 0) return { violated: false };
1378
+ const totalSupply = resourceEntries.reduce((sum, [_, s]) => sum + s, 0);
1379
+ const avgSupply = totalSupply / resourceEntries.length;
1380
+ for (const [resource, supply] of resourceEntries) {
1381
+ if (supply > avgSupply * 3 && netFlow > thresholds.netFlowWarnThreshold) {
1382
+ return {
1383
+ violated: true,
1384
+ severity: 4,
1385
+ evidence: {
1386
+ resource,
1387
+ supply,
1388
+ avgSupply,
1389
+ ratio: supply / Math.max(1, avgSupply),
1390
+ netFlow
1391
+ },
1392
+ suggestedAction: {
1393
+ parameter: "yieldRate",
1394
+ direction: "decrease",
1395
+ magnitude: 0.15,
1396
+ reasoning: `Inflation with ${resource} backlog (${supply} units, ${(supply / Math.max(1, avgSupply)).toFixed(1)}\xD7 average). Root cause is gathering. Correct lever: yieldRate, not fees.`
1397
+ },
1398
+ confidence: 0.75,
1399
+ estimatedLag: 8
1400
+ };
1401
+ }
1348
1402
  }
1349
1403
  return { violated: false };
1350
1404
  }
@@ -1362,7 +1416,7 @@ var P26_ContinuousPressureBeatsThresholdCuts = {
1362
1416
  severity: 4,
1363
1417
  evidence: { inflationRate },
1364
1418
  suggestedAction: {
1365
- parameter: "craftingCost",
1419
+ parameter: "productionCost",
1366
1420
  direction: inflationRate > 0 ? "increase" : "decrease",
1367
1421
  magnitude: Math.min(thresholds.maxAdjustmentPercent, 0.05),
1368
1422
  // force smaller step
@@ -1388,7 +1442,7 @@ var P27_AdjustmentsNeedCooldowns = {
1388
1442
  severity: 4,
1389
1443
  evidence: { churnRate, avgSatisfaction },
1390
1444
  suggestedAction: {
1391
- parameter: "arenaEntryFee",
1445
+ parameter: "entryFee",
1392
1446
  direction: "decrease",
1393
1447
  magnitude: 0.05,
1394
1448
  reasoning: `High churn (${(churnRate * 100).toFixed(1)}%) with low satisfaction. Possible oscillation from rapid adjustments. Apply small correction only.`
@@ -1404,7 +1458,7 @@ var P28_StructuralDominanceIsNotPathological = {
1404
1458
  id: "P28",
1405
1459
  name: "Structural Dominance \u2260 Pathological Monopoly",
1406
1460
  category: "regulator",
1407
- description: 'A designed Fighter majority (55%) should not trigger population suppression. AgentE must distinguish between "this role is dominant BY DESIGN" (configured via dominantRoles) and "this role took over unexpectedly".',
1461
+ description: 'A designed dominant role (majority exceeds 55%) should not trigger population suppression. AgentE must distinguish between "this role is dominant BY DESIGN" (configured via dominantRoles) and "this role took over unexpectedly".',
1408
1462
  check(metrics, _thresholds) {
1409
1463
  const { roleShares, avgSatisfaction } = metrics;
1410
1464
  const dominant = Object.entries(roleShares).sort((a, b) => b[1] - a[1])[0];
@@ -1419,7 +1473,7 @@ var P28_StructuralDominanceIsNotPathological = {
1419
1473
  severity: 5,
1420
1474
  evidence: { dominantRole, dominantShare, avgSatisfaction },
1421
1475
  suggestedAction: {
1422
- parameter: "craftingCost",
1476
+ parameter: "productionCost",
1423
1477
  direction: "decrease",
1424
1478
  magnitude: 0.1,
1425
1479
  reasoning: `${dominantRole} dominant (${(dominantShare * 100).toFixed(0)}%) with low satisfaction. Pathological dominance \u2014 agents trapped, not thriving. Ease costs to allow role switching.`
@@ -1444,7 +1498,7 @@ var P38_CommunicationPreventsRevolt = {
1444
1498
  severity: 3,
1445
1499
  evidence: { churnRate },
1446
1500
  suggestedAction: {
1447
- parameter: "arenaReward",
1501
+ parameter: "rewardRate",
1448
1502
  direction: "increase",
1449
1503
  magnitude: 0.1,
1450
1504
  reasoning: `High churn (${(churnRate * 100).toFixed(1)}%) \u2014 agents leaving. Ensure all recent adjustments are logged with reasoning to diagnose cause.`
@@ -1469,7 +1523,7 @@ var P29_PinchPoint = {
1469
1523
  id: "P29",
1470
1524
  name: "Pinch Point",
1471
1525
  category: "market_dynamics",
1472
- description: "Every economy has a resource that constrains all downstream activity. In AgentE v0: weapons are the pinch point (Fighters need them, Crafters make them). If demand drops \u2192 oversupply. If frustration rises \u2192 undersupply.",
1526
+ description: "Every economy has a resource that constrains all downstream activity. Identify which resource is the pinch point (consumers need them, producers make them). If demand drops \u2192 oversupply. If frustration rises \u2192 undersupply.",
1473
1527
  check(metrics, _thresholds) {
1474
1528
  const { pinchPoints, supplyByResource, demandSignals } = metrics;
1475
1529
  for (const [resource, status] of Object.entries(pinchPoints)) {
@@ -1481,7 +1535,7 @@ var P29_PinchPoint = {
1481
1535
  severity: 7,
1482
1536
  evidence: { resource, supply, demand, status },
1483
1537
  suggestedAction: {
1484
- parameter: "craftingCost",
1538
+ parameter: "productionCost",
1485
1539
  direction: "decrease",
1486
1540
  magnitude: 0.15,
1487
1541
  reasoning: `${resource} is a pinch point and currently SCARCE (supply ${supply}, demand ${demand}). Reduce production cost to increase throughput.`
@@ -1497,7 +1551,7 @@ var P29_PinchPoint = {
1497
1551
  severity: 4,
1498
1552
  evidence: { resource, supply, status },
1499
1553
  suggestedAction: {
1500
- parameter: "craftingCost",
1554
+ parameter: "productionCost",
1501
1555
  direction: "increase",
1502
1556
  magnitude: 0.1,
1503
1557
  reasoning: `${resource} is a pinch point and OVERSUPPLIED (supply ${supply}). Raise production cost to reduce surplus.`
@@ -1514,7 +1568,7 @@ var P30_MovingPinchPoint = {
1514
1568
  id: "P30",
1515
1569
  name: "Moving Pinch Point",
1516
1570
  category: "market_dynamics",
1517
- description: "Player progression shifts the demand curve. A static pinch point that works at level 1 will be cleared at level 10. The pinch point must move with the player to maintain ongoing scarcity and engagement.",
1571
+ description: "Agent progression shifts the demand curve. A static pinch point that works early will be cleared later. The pinch point must move with agent progression to maintain ongoing scarcity and engagement.",
1518
1572
  check(metrics, _thresholds) {
1519
1573
  const { capacityUsage, supplyByResource, avgSatisfaction } = metrics;
1520
1574
  const totalResources = Object.values(supplyByResource).reduce((s, v) => s + v, 0);
@@ -1525,7 +1579,7 @@ var P30_MovingPinchPoint = {
1525
1579
  severity: 3,
1526
1580
  evidence: { capacityUsage, resourcesPerAgent, avgSatisfaction },
1527
1581
  suggestedAction: {
1528
- parameter: "craftingCost",
1582
+ parameter: "productionCost",
1529
1583
  direction: "increase",
1530
1584
  magnitude: 0.1,
1531
1585
  reasoning: "Economy operating at full capacity with abundant resources and high satisfaction. Pinch point may have been cleared. Increase production cost to restore scarcity."
@@ -1571,7 +1625,7 @@ var P57_CombinatorialPriceSpace = {
1571
1625
  target: thresholds.relativePriceConvergenceTarget
1572
1626
  },
1573
1627
  suggestedAction: {
1574
- parameter: "auctionFee",
1628
+ parameter: "transactionFee",
1575
1629
  direction: "decrease",
1576
1630
  magnitude: 0.1,
1577
1631
  reasoning: `Only ${(convergenceRate * 100).toFixed(0)}% of ${relativePriceCount} relative prices have converged (target: ${(thresholds.relativePriceConvergenceTarget * 100).toFixed(0)}%). Price space too complex for distributed discovery. Lower friction to help.`
@@ -1594,7 +1648,7 @@ var P31_AnchorValueTracking = {
1594
1648
  id: "P31",
1595
1649
  name: "Anchor Value Tracking",
1596
1650
  category: "measurement",
1597
- description: "1 hour of play = X gold = Y items. If this ratio drifts, the economy is inflating or deflating in ways that players feel before metrics catch it. Track the ratio constantly.",
1651
+ description: "1 period of activity = X currency = Y resources. If this ratio drifts, the economy is inflating or deflating in ways that participants feel before metrics catch it. Track the ratio constantly.",
1598
1652
  check(metrics, _thresholds) {
1599
1653
  const { anchorRatioDrift, inflationRate } = metrics;
1600
1654
  if (Math.abs(anchorRatioDrift) > 0.25) {
@@ -1603,10 +1657,10 @@ var P31_AnchorValueTracking = {
1603
1657
  severity: 5,
1604
1658
  evidence: { anchorRatioDrift, inflationRate },
1605
1659
  suggestedAction: {
1606
- parameter: "craftingCost",
1660
+ parameter: "productionCost",
1607
1661
  direction: anchorRatioDrift > 0 ? "increase" : "decrease",
1608
1662
  magnitude: 0.1,
1609
- reasoning: `Anchor ratio has drifted ${(anchorRatioDrift * 100).toFixed(0)}% from baseline. Time-to-value for players is changing. Adjust production costs to restore.`
1663
+ reasoning: `Anchor ratio has drifted ${(anchorRatioDrift * 100).toFixed(0)}% from baseline. Time-to-value for participants is changing. Adjust production costs to restore.`
1610
1664
  },
1611
1665
  confidence: 0.65,
1612
1666
  estimatedLag: 10
@@ -1628,7 +1682,7 @@ var P41_MultiResolutionMonitoring = {
1628
1682
  severity: 4,
1629
1683
  evidence: { giniCoefficient, avgSatisfaction },
1630
1684
  suggestedAction: {
1631
- parameter: "auctionFee",
1685
+ parameter: "transactionFee",
1632
1686
  direction: "increase",
1633
1687
  magnitude: 0.1,
1634
1688
  reasoning: `Gini ${giniCoefficient.toFixed(2)} rising despite okay satisfaction. Early warning from fine-resolution monitoring. Raise trading fees to slow wealth concentration before it hurts satisfaction.`
@@ -1657,7 +1711,7 @@ var P55_ArbitrageThermometer = {
1657
1711
  critical: thresholds.arbitrageIndexCritical
1658
1712
  },
1659
1713
  suggestedAction: {
1660
- parameter: "auctionFee",
1714
+ parameter: "transactionFee",
1661
1715
  direction: "decrease",
1662
1716
  magnitude: 0.15,
1663
1717
  reasoning: `Arbitrage index ${arbitrageIndex.toFixed(2)} exceeds critical threshold (${thresholds.arbitrageIndexCritical}). Relative prices are diverging \u2014 economy destabilizing. Lower trading friction to accelerate price convergence.`
@@ -1675,7 +1729,7 @@ var P55_ArbitrageThermometer = {
1675
1729
  warning: thresholds.arbitrageIndexWarning
1676
1730
  },
1677
1731
  suggestedAction: {
1678
- parameter: "auctionFee",
1732
+ parameter: "transactionFee",
1679
1733
  direction: "decrease",
1680
1734
  magnitude: 0.08,
1681
1735
  reasoning: `Arbitrage index ${arbitrageIndex.toFixed(2)} above warning threshold (${thresholds.arbitrageIndexWarning}). Early sign of price divergence. Gently reduce friction to support self-correction.`
@@ -1703,7 +1757,7 @@ var P59_GiftEconomyNoise = {
1703
1757
  threshold: thresholds.giftTradeFilterRatio
1704
1758
  },
1705
1759
  suggestedAction: {
1706
- parameter: "auctionFee",
1760
+ parameter: "transactionFee",
1707
1761
  direction: "increase",
1708
1762
  magnitude: 0.05,
1709
1763
  reasoning: `${(giftTradeRatio * 100).toFixed(0)}% of trades are gift-like (price = 0 or <30% market). Exceeds filter threshold (${(thresholds.giftTradeFilterRatio * 100).toFixed(0)}%). Price signals contaminated. Slightly raise trading fees to discourage zero-value listings. ADVISORY: Consider filtering sub-market trades from price index computation.`
@@ -1727,7 +1781,7 @@ var P42_TheMedianPrinciple = {
1727
1781
  id: "P42",
1728
1782
  name: "The Median Principle",
1729
1783
  category: "statistical",
1730
- description: "When (mean - median) / median > 0.3, mean is a lie. A few whales with 10,000g raise the mean while most agents have 50g. Always balance to median when divergence exceeds 30%.",
1784
+ description: "When (mean - median) / median > 0.3, mean is a lie. A few high-balance agents raise the mean while most agents have low balances. Always balance to median when divergence exceeds 30%.",
1731
1785
  check(metrics, thresholds) {
1732
1786
  const { meanMedianDivergence, giniCoefficient } = metrics;
1733
1787
  if (meanMedianDivergence > thresholds.meanMedianDivergenceMax) {
@@ -1741,7 +1795,7 @@ var P42_TheMedianPrinciple = {
1741
1795
  medianBalance: metrics.medianBalance
1742
1796
  },
1743
1797
  suggestedAction: {
1744
- parameter: "auctionFee",
1798
+ parameter: "transactionFee",
1745
1799
  direction: "increase",
1746
1800
  magnitude: 0.15,
1747
1801
  reasoning: `Mean/median divergence ${(meanMedianDivergence * 100).toFixed(0)}% (threshold: ${(thresholds.meanMedianDivergenceMax * 100).toFixed(0)}%). Economy has outliers skewing metrics. Use median for decisions. Raise auction fees to redistribute wealth.`
@@ -1766,7 +1820,7 @@ var P43_SimulationMinimum = {
1766
1820
  severity: 3,
1767
1821
  evidence: { inflationRate, minIterations: thresholds.simulationMinIterations },
1768
1822
  suggestedAction: {
1769
- parameter: "craftingCost",
1823
+ parameter: "productionCost",
1770
1824
  direction: inflationRate > 0 ? "increase" : "decrease",
1771
1825
  magnitude: 0.05,
1772
1826
  reasoning: `Large inflation rate swing (${(inflationRate * 100).toFixed(0)}%). Ensure all decisions use \u2265${thresholds.simulationMinIterations} simulation iterations. Apply conservative correction.`
@@ -1804,7 +1858,7 @@ var P39_TheLagPrinciple = {
1804
1858
  severity: 5,
1805
1859
  evidence: { inflationRate, netFlow, lagRange: [lagMin, lagMax] },
1806
1860
  suggestedAction: {
1807
- parameter: "craftingCost",
1861
+ parameter: "productionCost",
1808
1862
  direction: "increase",
1809
1863
  magnitude: 0.03,
1810
1864
  // very small — oscillation means over-adjusting
@@ -1831,7 +1885,7 @@ var P44_ComplexityBudget = {
1831
1885
  severity: 3,
1832
1886
  evidence: { customMetricCount, budgetMax: thresholds.complexityBudgetMax },
1833
1887
  suggestedAction: {
1834
- parameter: "auctionFee",
1888
+ parameter: "transactionFee",
1835
1889
  direction: "decrease",
1836
1890
  magnitude: 0.01,
1837
1891
  reasoning: `${customMetricCount} custom metrics tracked (budget: ${thresholds.complexityBudgetMax}). Consider pruning low-impact parameters. Applying minimal correction to avoid adding complexity.`
@@ -1853,25 +1907,25 @@ var P35_DestructionCreatesValue = {
1853
1907
  id: "P35",
1854
1908
  name: "Destruction Creates Value",
1855
1909
  category: "resource",
1856
- description: "If nothing is ever permanently lost, inflation is inevitable. Weapon durability (breaks after 3 fights), potion consumption on use, and ore costs for crafting are all destruction mechanisms. Without them, supply grows without bound.",
1910
+ description: "If nothing is ever permanently lost, inflation is inevitable. Resource durability and consumption mechanisms create destruction. Without them, supply grows without bound.",
1857
1911
  check(metrics, _thresholds) {
1858
1912
  const { supplyByResource, sinkVolume, netFlow } = metrics;
1859
- const weapons = supplyByResource["weapons"] ?? 0;
1860
- const potions = supplyByResource["potions"] ?? 0;
1861
- if ((weapons > 200 || potions > 200) && sinkVolume < 5 && netFlow > 0) {
1862
- return {
1863
- violated: true,
1864
- severity: 6,
1865
- evidence: { weapons, potions, sinkVolume, netFlow },
1866
- suggestedAction: {
1867
- parameter: "arenaEntryFee",
1868
- direction: "decrease",
1869
- magnitude: 0.1,
1870
- reasoning: `${weapons} weapons + ${potions} potions with low destruction (sink ${sinkVolume}/t). Consumables not being consumed. Lower arena entry to increase weapon/potion usage.`
1871
- },
1872
- confidence: 0.7,
1873
- estimatedLag: 5
1874
- };
1913
+ for (const [resource, supply] of Object.entries(supplyByResource)) {
1914
+ if (supply > 200 && sinkVolume < 5 && netFlow > 0) {
1915
+ return {
1916
+ violated: true,
1917
+ severity: 6,
1918
+ evidence: { resource, supply, sinkVolume, netFlow },
1919
+ suggestedAction: {
1920
+ parameter: "entryFee",
1921
+ direction: "decrease",
1922
+ magnitude: 0.1,
1923
+ reasoning: `${resource} supply at ${supply} units with low destruction (sink ${sinkVolume}/t). Resources not being consumed. Lower competitive pool entry to increase resource usage.`
1924
+ },
1925
+ confidence: 0.7,
1926
+ estimatedLag: 5
1927
+ };
1928
+ }
1875
1929
  }
1876
1930
  return { violated: false };
1877
1931
  }
@@ -1891,7 +1945,7 @@ var P40_ReplacementRate = {
1891
1945
  severity: 6,
1892
1946
  evidence: { productionIndex, sinkVolume, replacementRatio },
1893
1947
  suggestedAction: {
1894
- parameter: "miningYield",
1948
+ parameter: "yieldRate",
1895
1949
  direction: "increase",
1896
1950
  magnitude: 0.15,
1897
1951
  reasoning: `Replacement rate ${replacementRatio.toFixed(2)} (need \u2265${thresholds.replacementRateMultiplier}). Production below consumption. Resources will deplete. Increase yield.`
@@ -1905,7 +1959,7 @@ var P40_ReplacementRate = {
1905
1959
  severity: 3,
1906
1960
  evidence: { productionIndex, sinkVolume, replacementRatio },
1907
1961
  suggestedAction: {
1908
- parameter: "miningYield",
1962
+ parameter: "yieldRate",
1909
1963
  direction: "decrease",
1910
1964
  magnitude: 0.1,
1911
1965
  reasoning: `Replacement rate ${replacementRatio.toFixed(2)} \u2014 overproducing. Production far exceeds consumption. Reduce yield to prevent glut.`
@@ -1931,7 +1985,7 @@ var P49_IdleAssetTax = {
1931
1985
  severity: 5,
1932
1986
  evidence: { giniCoefficient, top10PctShare, velocity },
1933
1987
  suggestedAction: {
1934
- parameter: "auctionFee",
1988
+ parameter: "transactionFee",
1935
1989
  direction: "increase",
1936
1990
  magnitude: 0.15,
1937
1991
  reasoning: `Gini ${giniCoefficient.toFixed(2)}, top 10% hold ${(top10PctShare * 100).toFixed(0)}%, velocity ${velocity}. Wealth concentrated in idle assets. Raise trading costs to simulate holding tax.`
@@ -1963,7 +2017,7 @@ var P33_FairNotEqual = {
1963
2017
  severity: 3,
1964
2018
  evidence: { giniCoefficient },
1965
2019
  suggestedAction: {
1966
- parameter: "arenaReward",
2020
+ parameter: "rewardRate",
1967
2021
  direction: "increase",
1968
2022
  magnitude: 0.1,
1969
2023
  reasoning: `Gini ${giniCoefficient.toFixed(2)} \u2014 near-perfect equality. Economy lacks stakes. Increase winner rewards to create meaningful spread.`
@@ -1978,7 +2032,7 @@ var P33_FairNotEqual = {
1978
2032
  severity: 7,
1979
2033
  evidence: { giniCoefficient },
1980
2034
  suggestedAction: {
1981
- parameter: "auctionFee",
2035
+ parameter: "transactionFee",
1982
2036
  direction: "increase",
1983
2037
  magnitude: 0.2,
1984
2038
  reasoning: `Gini ${giniCoefficient.toFixed(2)} \u2014 oligarchy level. Toxic inequality. Raise transaction fees to redistribute wealth from rich to pool.`
@@ -1993,7 +2047,7 @@ var P33_FairNotEqual = {
1993
2047
  severity: 4,
1994
2048
  evidence: { giniCoefficient },
1995
2049
  suggestedAction: {
1996
- parameter: "auctionFee",
2050
+ parameter: "transactionFee",
1997
2051
  direction: "increase",
1998
2052
  magnitude: 0.1,
1999
2053
  reasoning: `Gini ${giniCoefficient.toFixed(2)} \u2014 high inequality warning. Gently raise fees to slow wealth concentration.`
@@ -2018,7 +2072,7 @@ var P36_MechanicFrictionDetector = {
2018
2072
  severity: 5,
2019
2073
  evidence: { churnRate, avgSatisfaction, velocity },
2020
2074
  suggestedAction: {
2021
- parameter: "arenaReward",
2075
+ parameter: "rewardRate",
2022
2076
  direction: "increase",
2023
2077
  magnitude: 0.15,
2024
2078
  reasoning: `Churn ${(churnRate * 100).toFixed(1)}% with satisfaction ${avgSatisfaction.toFixed(0)} despite active economy (velocity ` + velocity.toFixed(1) + "). Suggests mechanic friction (deterministic vs random systems). Increase rewards to compensate for perceived unfairness. ADVISORY: Review if mixing guaranteed and probabilistic mechanics."
@@ -2043,7 +2097,7 @@ var P37_LatecommerProblem = {
2043
2097
  severity: 6,
2044
2098
  evidence: { timeToValue, avgSatisfaction, churnRate },
2045
2099
  suggestedAction: {
2046
- parameter: "craftingCost",
2100
+ parameter: "productionCost",
2047
2101
  direction: "decrease",
2048
2102
  magnitude: 0.15,
2049
2103
  reasoning: `New agents taking ${timeToValue} ticks to reach viability. Churn ${(churnRate * 100).toFixed(1)}%, satisfaction ${avgSatisfaction.toFixed(0)}. Lower production costs to help new participants contribute faster.`
@@ -2070,7 +2124,7 @@ var P45_TimeBudget = {
2070
2124
  severity: 5,
2071
2125
  evidence: { timeToValue, avgSatisfaction, timeBudgetRatio: thresholds.timeBudgetRatio },
2072
2126
  suggestedAction: {
2073
- parameter: "arenaEntryFee",
2127
+ parameter: "entryFee",
2074
2128
  direction: "decrease",
2075
2129
  magnitude: 0.15,
2076
2130
  reasoning: `Time-to-value ${timeToValue} ticks with ${avgSatisfaction.toFixed(0)} satisfaction. Economy requires too much time investment. Lower barriers to participation.`
@@ -2100,7 +2154,7 @@ var P50_PayPowerRatio = {
2100
2154
  threshold: thresholds.payPowerRatioMax
2101
2155
  },
2102
2156
  suggestedAction: {
2103
- parameter: "auctionFee",
2157
+ parameter: "transactionFee",
2104
2158
  direction: "increase",
2105
2159
  magnitude: 0.2,
2106
2160
  reasoning: `Top 10% hold ${(top10PctShare * 100).toFixed(0)}% of wealth (Gini ${giniCoefficient.toFixed(2)}). Wealth advantage may exceed pay-power ratio threshold. Redistribute via higher trading fees.`
@@ -2135,7 +2189,7 @@ var P34_ExtractionRatio = {
2135
2189
  severity: 8,
2136
2190
  evidence: { extractionRatio, threshold: thresholds.extractionRatioRed },
2137
2191
  suggestedAction: {
2138
- parameter: "auctionFee",
2192
+ parameter: "transactionFee",
2139
2193
  direction: "increase",
2140
2194
  magnitude: 0.25,
2141
2195
  reasoning: `Extraction ratio ${(extractionRatio * 100).toFixed(0)}% (critical: ${(thresholds.extractionRatioRed * 100).toFixed(0)}%). Economy is extraction-heavy and subsidy-dependent. Raise fees to increase the cost of extraction.`
@@ -2150,7 +2204,7 @@ var P34_ExtractionRatio = {
2150
2204
  severity: 5,
2151
2205
  evidence: { extractionRatio, threshold: thresholds.extractionRatioYellow },
2152
2206
  suggestedAction: {
2153
- parameter: "auctionFee",
2207
+ parameter: "transactionFee",
2154
2208
  direction: "increase",
2155
2209
  magnitude: 0.1,
2156
2210
  reasoning: `Extraction ratio ${(extractionRatio * 100).toFixed(0)}% (warning: ${(thresholds.extractionRatioYellow * 100).toFixed(0)}%). Economy trending toward extraction-heavy. Apply early pressure.`
@@ -2166,7 +2220,7 @@ var P47_SmokeTest = {
2166
2220
  id: "P47",
2167
2221
  name: "Smoke Test",
2168
2222
  category: "open_economy",
2169
- description: "intrinsic_utility_value / total_market_value < 0.3 = economy is >70% speculation. If utility value drops below 10%, a single bad week can collapse the entire market. Real utility (weapons that help you fight, potions that heal) must anchor value.",
2223
+ description: "intrinsic_utility_value / total_market_value < 0.3 = economy is >70% speculation. If utility value drops below 10%, a single bad week can collapse the entire market. Real utility (resources in the economy serve distinct utility functions) must anchor value.",
2170
2224
  check(metrics, thresholds) {
2171
2225
  const { smokeTestRatio } = metrics;
2172
2226
  if (isNaN(smokeTestRatio)) return { violated: false };
@@ -2176,7 +2230,7 @@ var P47_SmokeTest = {
2176
2230
  severity: 9,
2177
2231
  evidence: { smokeTestRatio, threshold: thresholds.smokeTestCritical },
2178
2232
  suggestedAction: {
2179
- parameter: "arenaReward",
2233
+ parameter: "rewardRate",
2180
2234
  direction: "increase",
2181
2235
  magnitude: 0.2,
2182
2236
  reasoning: `Utility/market ratio ${(smokeTestRatio * 100).toFixed(0)}% (critical). Economy is >90% speculative. Collapse risk is extreme. Increase utility rewards to anchor real value.`
@@ -2191,7 +2245,7 @@ var P47_SmokeTest = {
2191
2245
  severity: 6,
2192
2246
  evidence: { smokeTestRatio, threshold: thresholds.smokeTestWarning },
2193
2247
  suggestedAction: {
2194
- parameter: "arenaReward",
2248
+ parameter: "rewardRate",
2195
2249
  direction: "increase",
2196
2250
  magnitude: 0.1,
2197
2251
  reasoning: `Utility/market ratio ${(smokeTestRatio * 100).toFixed(0)}% (warning). Economy is >70% speculative. Boost utility rewards to restore intrinsic value anchor.`
@@ -2207,7 +2261,7 @@ var P48_CurrencyInsulation = {
2207
2261
  id: "P48",
2208
2262
  name: "Currency Insulation",
2209
2263
  category: "open_economy",
2210
- description: "Gameplay economy correlation with external markets > 0.5 = insulation failure. When your in-game gold price tracks ETH/USD, external market crashes destroy in-game economies. Good design insulates the two.",
2264
+ description: "Gameplay economy correlation with external markets > 0.5 = insulation failure. When your native currency price correlates with external asset, external market crashes destroy internal economies. Good design insulates the two.",
2211
2265
  check(metrics, thresholds) {
2212
2266
  const { currencyInsulation } = metrics;
2213
2267
  if (isNaN(currencyInsulation)) return { violated: false };
@@ -2217,7 +2271,7 @@ var P48_CurrencyInsulation = {
2217
2271
  severity: 6,
2218
2272
  evidence: { currencyInsulation, threshold: thresholds.currencyInsulationMax },
2219
2273
  suggestedAction: {
2220
- parameter: "auctionFee",
2274
+ parameter: "transactionFee",
2221
2275
  direction: "increase",
2222
2276
  magnitude: 0.1,
2223
2277
  reasoning: `Currency correlation with external market: ${(currencyInsulation * 100).toFixed(0)}% (max: ${(thresholds.currencyInsulationMax * 100).toFixed(0)}%). Economy is exposed to external market shocks. Increase internal friction to reduce external correlation.`
@@ -2257,7 +2311,7 @@ var P51_SharkTooth = {
2257
2311
  threshold: thresholds.sharkToothPeakDecay
2258
2312
  },
2259
2313
  suggestedAction: {
2260
- parameter: "arenaReward",
2314
+ parameter: "rewardRate",
2261
2315
  direction: "increase",
2262
2316
  magnitude: 0.1,
2263
2317
  reasoning: `Peak engagement dropped to ${(lastPeak / prevPeak * 100).toFixed(0)}% of previous peak (threshold: ${(thresholds.sharkToothPeakDecay * 100).toFixed(0)}%). Event fatigue detected. Boost event rewards to restore peak engagement.`
@@ -2275,7 +2329,7 @@ var P51_SharkTooth = {
2275
2329
  severity: 4,
2276
2330
  evidence: { lastValley, prevValley, ratio: lastValley / prevValley },
2277
2331
  suggestedAction: {
2278
- parameter: "craftingCost",
2332
+ parameter: "productionCost",
2279
2333
  direction: "decrease",
2280
2334
  magnitude: 0.1,
2281
2335
  reasoning: "Between-event engagement declining (deepening valleys). Base economy not sustaining participants between events. Lower production costs to improve off-event value."
@@ -2292,7 +2346,7 @@ var P52_EndowmentEffect = {
2292
2346
  id: "P52",
2293
2347
  name: "Endowment Effect",
2294
2348
  category: "liveops",
2295
- description: "Players who never owned premium items do not value them. Free trial events that let players experience premium items drive conversions because ownership creates perceived value (endowment effect).",
2349
+ description: "Participants who never owned premium items do not value them. Free trial events that let participants experience premium items drive conversions because ownership creates perceived value (endowment effect).",
2296
2350
  check(metrics, _thresholds) {
2297
2351
  const { avgSatisfaction, churnRate } = metrics;
2298
2352
  const { eventCompletionRate } = metrics;
@@ -2303,7 +2357,7 @@ var P52_EndowmentEffect = {
2303
2357
  severity: 4,
2304
2358
  evidence: { eventCompletionRate, avgSatisfaction, churnRate },
2305
2359
  suggestedAction: {
2306
- parameter: "arenaReward",
2360
+ parameter: "rewardRate",
2307
2361
  direction: "increase",
2308
2362
  magnitude: 0.15,
2309
2363
  reasoning: `${(eventCompletionRate * 100).toFixed(0)}% event completion but satisfaction only ${avgSatisfaction.toFixed(0)}. Events not creating perceived value. Increase reward quality/quantity.`
@@ -2333,10 +2387,10 @@ var P53_EventCompletionRate = {
2333
2387
  max: thresholds.eventCompletionMax
2334
2388
  },
2335
2389
  suggestedAction: {
2336
- parameter: "craftingCost",
2390
+ parameter: "productionCost",
2337
2391
  direction: "decrease",
2338
2392
  magnitude: 0.15,
2339
- reasoning: `Event completion rate ${(eventCompletionRate * 100).toFixed(0)}% \u2014 predatory territory (min: ${(thresholds.eventCompletionMin * 100).toFixed(0)}%). Too hard for free players. Lower barriers to participation.`
2393
+ reasoning: `Event completion rate ${(eventCompletionRate * 100).toFixed(0)}% \u2014 predatory territory (min: ${(thresholds.eventCompletionMin * 100).toFixed(0)}%). Too hard for free participants. Lower barriers to participation.`
2340
2394
  },
2341
2395
  confidence: 0.8,
2342
2396
  estimatedLag: 10
@@ -2348,7 +2402,7 @@ var P53_EventCompletionRate = {
2348
2402
  severity: 3,
2349
2403
  evidence: { eventCompletionRate, max: thresholds.eventCompletionMax },
2350
2404
  suggestedAction: {
2351
- parameter: "arenaEntryFee",
2405
+ parameter: "entryFee",
2352
2406
  direction: "increase",
2353
2407
  magnitude: 0.05,
2354
2408
  reasoning: `Event completion rate ${(eventCompletionRate * 100).toFixed(0)}% \u2014 no monetization pressure (max: ${(thresholds.eventCompletionMax * 100).toFixed(0)}%). Slightly raise costs to create meaningful premium differentiation.`
@@ -2373,7 +2427,7 @@ var P54_LiveOpsCadence = {
2373
2427
  severity: 3,
2374
2428
  evidence: { velocity, avgSatisfaction, tick: metrics.tick },
2375
2429
  suggestedAction: {
2376
- parameter: "arenaReward",
2430
+ parameter: "rewardRate",
2377
2431
  direction: "increase",
2378
2432
  magnitude: 0.1,
2379
2433
  reasoning: "Low velocity and satisfaction after long runtime. Possible content staleness. Increase rewards as bridge while new content is developed (developer action required)."
@@ -2404,7 +2458,7 @@ var P56_ContentDropShock = {
2404
2458
  postDropMax: thresholds.postDropArbitrageMax
2405
2459
  },
2406
2460
  suggestedAction: {
2407
- parameter: "auctionFee",
2461
+ parameter: "transactionFee",
2408
2462
  direction: "decrease",
2409
2463
  magnitude: 0.1,
2410
2464
  reasoning: `Content drop ${contentDropAge} ticks ago \u2014 arbitrage at ${arbitrageIndex.toFixed(2)} exceeds post-drop max (${thresholds.postDropArbitrageMax}). Price discovery struggling. Lower trading friction temporarily.`
@@ -2570,19 +2624,21 @@ var Simulator = class {
2570
2624
  flowEffect(action, metrics) {
2571
2625
  const { parameter, direction } = action;
2572
2626
  const sign = direction === "increase" ? -1 : 1;
2573
- if (parameter === "craftingCost" || parameter === "alchemyCost") {
2627
+ const roleEntries = Object.entries(metrics.populationByRole).sort((a, b) => b[1] - a[1]);
2628
+ const dominantRoleCount = roleEntries[0]?.[1] ?? 0;
2629
+ if (parameter === "productionCost") {
2574
2630
  return sign * metrics.netFlow * 0.2;
2575
2631
  }
2576
- if (parameter === "auctionFee") {
2632
+ if (parameter === "transactionFee") {
2577
2633
  return sign * metrics.velocity * 10 * 0.1;
2578
2634
  }
2579
- if (parameter === "arenaEntryFee") {
2580
- return sign * (metrics.populationByRole["Fighter"] ?? 0) * 0.5;
2635
+ if (parameter === "entryFee") {
2636
+ return sign * dominantRoleCount * 0.5;
2581
2637
  }
2582
- if (parameter === "arenaReward") {
2583
- return -sign * (metrics.populationByRole["Fighter"] ?? 0) * 0.3;
2638
+ if (parameter === "rewardRate") {
2639
+ return -sign * dominantRoleCount * 0.3;
2584
2640
  }
2585
- if (parameter === "miningYield" || parameter === "lumberYield") {
2641
+ if (parameter === "yieldRate") {
2586
2642
  return sign * metrics.faucetVolume * 0.15;
2587
2643
  }
2588
2644
  return sign * metrics.netFlow * 0.1;
@@ -2895,30 +2951,33 @@ var RingBuffer = class {
2895
2951
  }
2896
2952
  };
2897
2953
  var MetricStore = class {
2898
- constructor() {
2954
+ constructor(tickConfig) {
2899
2955
  /** Fine: last 200 ticks, one entry per tick */
2900
2956
  this.fine = new RingBuffer(200);
2901
- /** Medium: last 200 windows of 10 ticks */
2957
+ /** Medium: last 200 windows */
2902
2958
  this.medium = new RingBuffer(200);
2903
- /** Coarse: last 200 epochs of 100 ticks */
2959
+ /** Coarse: last 200 epochs */
2904
2960
  this.coarse = new RingBuffer(200);
2905
2961
  this.ticksSinceLastMedium = 0;
2906
2962
  this.ticksSinceLastCoarse = 0;
2907
2963
  this.mediumAccumulator = [];
2908
2964
  this.coarseAccumulator = [];
2965
+ const config = { ...DEFAULT_TICK_CONFIG, ...tickConfig };
2966
+ this.mediumWindow = config.mediumWindow;
2967
+ this.coarseWindow = config.coarseWindow;
2909
2968
  }
2910
2969
  record(metrics) {
2911
2970
  this.fine.push(metrics);
2912
2971
  this.mediumAccumulator.push(metrics);
2913
2972
  this.ticksSinceLastMedium++;
2914
- if (this.ticksSinceLastMedium >= 10) {
2973
+ if (this.ticksSinceLastMedium >= this.mediumWindow) {
2915
2974
  this.medium.push(this.aggregate(this.mediumAccumulator));
2916
2975
  this.mediumAccumulator = [];
2917
2976
  this.ticksSinceLastMedium = 0;
2918
2977
  }
2919
2978
  this.coarseAccumulator.push(metrics);
2920
2979
  this.ticksSinceLastCoarse++;
2921
- if (this.ticksSinceLastCoarse >= 100) {
2980
+ if (this.ticksSinceLastCoarse >= this.coarseWindow) {
2922
2981
  this.coarse.push(this.aggregate(this.coarseAccumulator));
2923
2982
  this.coarseAccumulator = [];
2924
2983
  this.ticksSinceLastCoarse = 0;
@@ -3018,7 +3077,7 @@ var PersonaTracker = class {
3018
3077
  /** Classify all agents and return persona distribution */
3019
3078
  getDistribution() {
3020
3079
  const counts = {
3021
- Gamer: 0,
3080
+ Active: 0,
3022
3081
  Trader: 0,
3023
3082
  Collector: 0,
3024
3083
  Speculator: 0,
@@ -3042,7 +3101,7 @@ var PersonaTracker = class {
3042
3101
  return distribution;
3043
3102
  }
3044
3103
  classify(history) {
3045
- if (history.length === 0) return "Gamer";
3104
+ if (history.length === 0) return "Active";
3046
3105
  const avg = (key) => {
3047
3106
  const vals = history.map((h) => h[key]);
3048
3107
  return vals.reduce((s, v) => s + v, 0) / vals.length;
@@ -3056,21 +3115,18 @@ var PersonaTracker = class {
3056
3115
  if (uniqueItems > 5 && extraction < 0) return "Collector";
3057
3116
  if (extraction > 100) return "Earner";
3058
3117
  if (extraction > 50) return "Speculator";
3059
- return "Gamer";
3118
+ return "Active";
3060
3119
  }
3061
3120
  };
3062
3121
 
3063
3122
  // src/AgentE.ts
3064
3123
  var AgentE = class {
3065
3124
  constructor(config) {
3066
- // ── Pipeline ──
3067
- this.observer = new Observer();
3068
3125
  this.simulator = new Simulator();
3069
3126
  this.planner = new Planner();
3070
3127
  this.executor = new Executor();
3071
3128
  // ── State ──
3072
3129
  this.log = new DecisionLog();
3073
- this.store = new MetricStore();
3074
3130
  this.personaTracker = new PersonaTracker();
3075
3131
  this.params = {};
3076
3132
  this.eventBuffer = [];
@@ -3089,6 +3145,7 @@ var AgentE = class {
3089
3145
  mode: this.mode,
3090
3146
  dominantRoles: config.dominantRoles ?? [],
3091
3147
  idealDistribution: config.idealDistribution ?? {},
3148
+ tickConfig: config.tickConfig ?? { duration: 1, unit: "tick" },
3092
3149
  gracePeriod: config.gracePeriod ?? 50,
3093
3150
  checkInterval: config.checkInterval ?? 5,
3094
3151
  maxAdjustmentPercent: config.maxAdjustmentPercent ?? 0.15,
@@ -3100,6 +3157,9 @@ var AgentE = class {
3100
3157
  maxAdjustmentPercent: config.maxAdjustmentPercent ?? DEFAULT_THRESHOLDS.maxAdjustmentPercent,
3101
3158
  cooldownTicks: config.cooldownTicks ?? DEFAULT_THRESHOLDS.cooldownTicks
3102
3159
  };
3160
+ const tickConfig = { ...DEFAULT_TICK_CONFIG, ...config.tickConfig };
3161
+ this.observer = new Observer(tickConfig);
3162
+ this.store = new MetricStore(tickConfig);
3103
3163
  this.diagnoser = new Diagnoser(ALL_PRINCIPLES);
3104
3164
  if (config.onDecision) this.on("decision", config.onDecision);
3105
3165
  if (config.onAlert) this.on("alert", config.onAlert);
@@ -3131,7 +3191,7 @@ var AgentE = class {
3131
3191
  this.isRunning = false;
3132
3192
  this.isPaused = false;
3133
3193
  }
3134
- // ── Main cycle (call once per tick from your game loop) ────────────────────
3194
+ // ── Main cycle (call once per tick from your economy loop) ─────────────────
3135
3195
  async tick(state) {
3136
3196
  if (!this.isRunning || this.isPaused) return;
3137
3197
  const currentState = state ?? await Promise.resolve(this.adapter.getState());