@agent-e/core 1.1.3 → 1.2.1

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;
@@ -95,6 +102,7 @@ var Observer = class {
95
102
  const tradeEvents = [];
96
103
  const roleChangeEvents = [];
97
104
  let churnCount = 0;
105
+ let productionAmount = 0;
98
106
  for (const e of recentEvents) {
99
107
  switch (e.type) {
100
108
  case "mint":
@@ -105,6 +113,9 @@ var Observer = class {
105
113
  case "consume":
106
114
  sinkVolume += e.amount ?? 0;
107
115
  break;
116
+ case "produce":
117
+ productionAmount += e.amount ?? 1;
118
+ break;
108
119
  case "trade":
109
120
  tradeEvents.push(e);
110
121
  break;
@@ -178,24 +189,24 @@ var Observer = class {
178
189
  pinchPoints[resource] = "optimal";
179
190
  }
180
191
  }
181
- const productionIndex = recentEvents.filter((e) => e.type === "produce").reduce((s, e) => s + (e.amount ?? 1), 0);
192
+ const productionIndex = productionAmount;
182
193
  const maxPossibleProduction = productionIndex + sinkVolume;
183
194
  const capacityUsage = maxPossibleProduction > 0 ? productionIndex / maxPossibleProduction : 0;
184
195
  const satisfactions = Object.values(state.agentSatisfaction ?? {});
185
196
  const avgSatisfaction = satisfactions.length > 0 ? satisfactions.reduce((s, v) => s + v, 0) / satisfactions.length : 80;
186
197
  const blockedAgentCount = satisfactions.filter((s) => s < 20).length;
187
- const timeToValue = tick > 0 ? Math.max(0, 20 - tick * 0.1) : 20;
198
+ const timeToValue = totalAgents > 0 ? blockedAgentCount / totalAgents * 100 : 0;
188
199
  const poolSizes = { ...state.poolSizes ?? {} };
189
200
  if (!this.anchorBaseline && tick === 1 && totalSupply > 0) {
190
201
  this.anchorBaseline = {
191
- goldPerHour: totalSupply / Math.max(1, totalAgents),
192
- itemsPerGold: priceIndex > 0 ? 1 / priceIndex : 0
202
+ currencyPerPeriod: totalSupply / Math.max(1, totalAgents),
203
+ itemsPerCurrency: priceIndex > 0 ? 1 / priceIndex : 0
193
204
  };
194
205
  }
195
206
  let anchorRatioDrift = 0;
196
207
  if (this.anchorBaseline && totalAgents > 0) {
197
- const currentGoldPerHour = totalSupply / totalAgents;
198
- anchorRatioDrift = this.anchorBaseline.goldPerHour > 0 ? (currentGoldPerHour - this.anchorBaseline.goldPerHour) / this.anchorBaseline.goldPerHour : 0;
208
+ const currentCurrencyPerPeriod = totalSupply / totalAgents;
209
+ anchorRatioDrift = this.anchorBaseline.currencyPerPeriod > 0 ? (currentCurrencyPerPeriod - this.anchorBaseline.currencyPerPeriod) / this.anchorBaseline.currencyPerPeriod : 0;
199
210
  }
200
211
  let arbitrageIndex = 0;
201
212
  const priceKeys = Object.keys(prices).filter((k) => prices[k] > 0);
@@ -420,7 +431,7 @@ var P1_ProductionMatchesConsumption = {
420
431
  id: "P1",
421
432
  name: "Production Must Match Consumption",
422
433
  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.",
434
+ 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
435
  check(metrics, _thresholds) {
425
436
  const { supplyByResource, demandSignals, populationByRole } = metrics;
426
437
  const violations = [];
@@ -431,19 +442,22 @@ var P1_ProductionMatchesConsumption = {
431
442
  violations.push(resource);
432
443
  }
433
444
  }
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) {
445
+ const roleEntries = Object.entries(populationByRole).sort((a, b) => b[1] - a[1]);
446
+ const totalPop = metrics.totalAgents;
447
+ const dominantRole = roleEntries[0];
448
+ const dominantCount = dominantRole?.[1] ?? 0;
449
+ const dominantShare = totalPop > 0 ? dominantCount / totalPop : 0;
450
+ const populationImbalance = dominantShare > 0.4 && violations.length > 0;
451
+ if (violations.length > 0 || populationImbalance) {
438
452
  return {
439
453
  violated: true,
440
454
  severity: 7,
441
- evidence: { scarceResources: violations, crafters, consumers },
455
+ evidence: { scarceResources: violations, dominantRole: dominantRole?.[0], dominantShare },
442
456
  suggestedAction: {
443
- parameter: "craftingCost",
457
+ parameter: "productionCost",
444
458
  direction: "decrease",
445
459
  magnitude: 0.15,
446
- reasoning: "Lower crafting cost to incentivise more production."
460
+ reasoning: "Lower production cost to incentivise more production."
447
461
  },
448
462
  confidence: violations.length > 0 ? 0.85 : 0.6,
449
463
  estimatedLag: 10
@@ -456,26 +470,28 @@ var P2_ClosedLoopsNeedDirectHandoff = {
456
470
  id: "P2",
457
471
  name: "Closed Loops Need Direct Handoff",
458
472
  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.",
473
+ 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
474
  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;
475
+ const { supplyByResource, prices, velocity, totalAgents } = metrics;
476
+ const avgSupplyPerAgent = totalAgents > 0 ? Object.values(supplyByResource).reduce((s, v) => s + v, 0) / totalAgents : 0;
477
+ const backlogResources = [];
478
+ for (const [resource, supply] of Object.entries(supplyByResource)) {
479
+ const price = prices[resource] ?? 0;
480
+ if (supply > avgSupplyPerAgent * 0.5 && price > 0) {
481
+ backlogResources.push(resource);
482
+ }
483
+ }
468
484
  const stagnant = velocity < 3;
469
- if ((oreBacklog || woodBacklog) && stagnant) {
485
+ if (backlogResources.length > 0 && stagnant) {
470
486
  return {
471
487
  violated: true,
472
488
  severity: 5,
473
- evidence: { ore, wood, velocity },
489
+ evidence: { backlogResources, velocity },
474
490
  suggestedAction: {
475
- parameter: "auctionFee",
491
+ parameter: "transactionFee",
476
492
  direction: "increase",
477
493
  magnitude: 0.2,
478
- reasoning: "Raise AH fees to discourage raw material listings. Direct hand-off at production zones is the correct channel."
494
+ reasoning: "Raise market fees to discourage raw material listings. Direct hand-off at production zones is the correct channel."
479
495
  },
480
496
  confidence: 0.7,
481
497
  estimatedLag: 5
@@ -488,29 +504,31 @@ var P3_BootstrapCapitalCoversFirstTransaction = {
488
504
  id: "P3",
489
505
  name: "Bootstrap Capital Covers First Transaction",
490
506
  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.",
507
+ 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
508
  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
- };
509
+ const { populationByRole, supplyByResource, prices, totalAgents } = metrics;
510
+ const totalProducers = Object.values(populationByRole).reduce((s, v) => s + v, 0);
511
+ if (totalProducers > 0) {
512
+ for (const [resource, supply] of Object.entries(supplyByResource)) {
513
+ if (supply === 0) {
514
+ const anyInputPriced = Object.values(prices).some((p) => p > 0);
515
+ if (anyInputPriced) {
516
+ return {
517
+ violated: true,
518
+ severity: 8,
519
+ evidence: { resource, totalProducers, supply },
520
+ suggestedAction: {
521
+ parameter: "productionCost",
522
+ direction: "decrease",
523
+ magnitude: 0.3,
524
+ reasoning: "Producers cannot complete first transaction. Lower production cost to unblock bootstrap."
525
+ },
526
+ confidence: 0.8,
527
+ estimatedLag: 3
528
+ };
529
+ }
530
+ }
531
+ }
514
532
  }
515
533
  return { violated: false };
516
534
  }
@@ -519,41 +537,38 @@ var P4_MaterialsFlowFasterThanCooldown = {
519
537
  id: "P4",
520
538
  name: "Materials Flow Faster Than Cooldown",
521
539
  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.",
540
+ 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
541
  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) {
542
+ const { supplyByResource, populationByRole, velocity, totalAgents } = metrics;
543
+ const totalSupply = Object.values(supplyByResource).reduce((s, v) => s + v, 0);
544
+ const avgSupplyPerAgent = totalAgents > 0 ? totalSupply / totalAgents : 0;
545
+ const roleEntries = Object.entries(populationByRole);
546
+ const totalRoles = roleEntries.length;
547
+ if (totalRoles >= 2 && velocity < 5 && avgSupplyPerAgent < 0.5) {
531
548
  return {
532
549
  violated: true,
533
550
  severity: 5,
534
- evidence: { gatherers, crafters, alchemists, gathererToProcuderRatio },
551
+ evidence: { avgSupplyPerAgent, velocity, totalRoles },
535
552
  suggestedAction: {
536
- parameter: "miningYield",
553
+ parameter: "yieldRate",
537
554
  direction: "increase",
538
555
  magnitude: 0.15,
539
- reasoning: "Too few gatherers relative to producers. Increase yield to compensate."
556
+ reasoning: "Low supply per agent with stagnant velocity. Increase yield to compensate."
540
557
  },
541
558
  confidence: 0.65,
542
559
  estimatedLag: 8
543
560
  };
544
561
  }
545
- const ore = supplyByResource["ore"] ?? 0;
546
- const wood = supplyByResource["wood"] ?? 0;
547
- if (ore > 80 || wood > 80) {
562
+ if (avgSupplyPerAgent > 2) {
548
563
  return {
549
564
  violated: true,
550
565
  severity: 4,
551
- evidence: { ore, wood, gatherers, producers },
566
+ evidence: { avgSupplyPerAgent, totalSupply, totalAgents },
552
567
  suggestedAction: {
553
- parameter: "miningYield",
568
+ parameter: "yieldRate",
554
569
  direction: "decrease",
555
570
  magnitude: 0.2,
556
- reasoning: "Raw materials piling up. Gatherers outpacing producers."
571
+ reasoning: "Raw materials piling up. Extractors outpacing producers."
557
572
  },
558
573
  confidence: 0.8,
559
574
  estimatedLag: 5
@@ -578,7 +593,7 @@ var P60_SurplusDisposalAsymmetry = {
578
593
  discount: thresholds.disposalTradeWeightDiscount
579
594
  },
580
595
  suggestedAction: {
581
- parameter: "craftingCost",
596
+ parameter: "productionCost",
582
597
  direction: "decrease",
583
598
  magnitude: 0.1,
584
599
  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 +618,7 @@ var P5_ProfitabilityIsCompetitive = {
603
618
  id: "P5",
604
619
  name: "Profitability Is Competitive, Not Absolute",
605
620
  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.",
621
+ 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
622
  check(metrics, thresholds) {
608
623
  const { roleShares, populationByRole } = metrics;
609
624
  const highShareRoles = [];
@@ -621,7 +636,7 @@ var P5_ProfitabilityIsCompetitive = {
621
636
  population: populationByRole[dominantRole]
622
637
  },
623
638
  suggestedAction: {
624
- parameter: "auctionFee",
639
+ parameter: "transactionFee",
625
640
  direction: "increase",
626
641
  magnitude: thresholds.maxAdjustmentPercent,
627
642
  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 +662,7 @@ var P6_CrowdingMultiplierOnAllRoles = {
647
662
  severity: 5,
648
663
  evidence: { role, share },
649
664
  suggestedAction: {
650
- parameter: "craftingCost",
665
+ parameter: "productionCost",
651
666
  direction: "increase",
652
667
  magnitude: 0.1,
653
668
  reasoning: `${role} at ${(share * 100).toFixed(0)}% \u2014 no crowding pressure detected. Apply role-specific cost increase to simulate saturation.`
@@ -664,28 +679,33 @@ var P7_NonSpecialistsSubsidiseSpecialists = {
664
679
  id: "P7",
665
680
  name: "Non-Specialists Subsidise Specialists in Zero-Sum Games",
666
681
  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.",
682
+ 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
683
  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
- };
684
+ const { poolSizes } = metrics;
685
+ for (const [poolName, poolSize] of Object.entries(poolSizes)) {
686
+ if (poolSize <= 0) continue;
687
+ const roleEntries = Object.entries(metrics.populationByRole);
688
+ if (roleEntries.length === 0) continue;
689
+ const [dominantRole, dominantPop] = roleEntries.reduce(
690
+ (max, entry) => entry[1] > max[1] ? entry : max
691
+ );
692
+ const total = metrics.totalAgents;
693
+ const dominantShare = dominantPop / Math.max(1, total);
694
+ if (dominantShare > 0.7 && poolSize < 100) {
695
+ return {
696
+ violated: true,
697
+ severity: 6,
698
+ evidence: { poolName, poolSize, dominantRole, dominantShare },
699
+ suggestedAction: {
700
+ parameter: "entryFee",
701
+ direction: "decrease",
702
+ magnitude: 0.1,
703
+ 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.`
704
+ },
705
+ confidence: 0.75,
706
+ estimatedLag: 5
707
+ };
708
+ }
689
709
  }
690
710
  return { violated: false };
691
711
  }
@@ -694,7 +714,7 @@ var P8_RegulatorCannotFightDesign = {
694
714
  id: "P8",
695
715
  name: "Regulator Cannot Fight the Design",
696
716
  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.",
717
+ 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
718
  check(metrics, _thresholds) {
699
719
  const { roleShares, avgSatisfaction } = metrics;
700
720
  if (avgSatisfaction < 45) {
@@ -705,7 +725,7 @@ var P8_RegulatorCannotFightDesign = {
705
725
  severity: 4,
706
726
  evidence: { dominantRole: dominantRole[0], share: dominantRole[1], avgSatisfaction },
707
727
  suggestedAction: {
708
- parameter: "arenaReward",
728
+ parameter: "rewardRate",
709
729
  direction: "increase",
710
730
  magnitude: 0.1,
711
731
  reasoning: `Low satisfaction with ${dominantRole[0]} dominant. Regulator may be suppressing a structurally necessary role. Ease pressure on dominant role rewards.`
@@ -740,7 +760,7 @@ var P9_RoleSwitchingNeedsFriction = {
740
760
  severity: 5,
741
761
  evidence: { totalChurnRate: totalChurn, churnByRole },
742
762
  suggestedAction: {
743
- parameter: "craftingCost",
763
+ parameter: "productionCost",
744
764
  direction: "increase",
745
765
  magnitude: 0.05,
746
766
  reasoning: `Role switch rate ${(totalChurn * 100).toFixed(1)}% exceeds friction threshold. Increase production costs to slow herd movement.`
@@ -757,7 +777,7 @@ var P10_SpawnWeightingUsesInversePopulation = {
757
777
  id: "P10",
758
778
  name: "Spawn Weighting Uses Inverse Population",
759
779
  category: "population",
760
- description: "New agents should preferentially fill the least-populated roles. Flat spawn probability causes initial imbalances to compound.",
780
+ description: "New agents should preferentially fill the least-populated roles. Flat entry probability causes initial imbalances to compound.",
761
781
  check(metrics, _thresholds) {
762
782
  const { roleShares } = metrics;
763
783
  if (Object.keys(roleShares).length === 0) return { violated: false };
@@ -772,10 +792,10 @@ var P10_SpawnWeightingUsesInversePopulation = {
772
792
  severity: 4,
773
793
  evidence: { roleShares, stdDev, leastPopulatedRole: minRole?.[0] },
774
794
  suggestedAction: {
775
- parameter: "miningYield",
795
+ parameter: "yieldRate",
776
796
  direction: "increase",
777
797
  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.`
798
+ 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
799
  },
780
800
  confidence: 0.6,
781
801
  estimatedLag: 20
@@ -798,7 +818,7 @@ var P11_TwoTierPressure = {
798
818
  severity: 6,
799
819
  evidence: { role, share },
800
820
  suggestedAction: {
801
- parameter: "auctionFee",
821
+ parameter: "transactionFee",
802
822
  direction: "increase",
803
823
  magnitude: 0.15,
804
824
  reasoning: `${role} at ${(share * 100).toFixed(0)}% \u2014 continuous pressure was insufficient. Hard intervention needed alongside resumed continuous pressure.`
@@ -826,7 +846,7 @@ var P46_PersonaDiversity = {
826
846
  severity: 5,
827
847
  evidence: { dominantPersona: persona, share, personaDistribution },
828
848
  suggestedAction: {
829
- parameter: "arenaReward",
849
+ parameter: "rewardRate",
830
850
  direction: "increase",
831
851
  magnitude: 0.1,
832
852
  reasoning: `${persona} persona at ${(share * 100).toFixed(0)}% \u2014 behavioral monoculture. Diversify reward structures to attract other persona types.`
@@ -843,7 +863,7 @@ var P46_PersonaDiversity = {
843
863
  severity: 3,
844
864
  evidence: { significantClusters, required: thresholds.personaMinClusters },
845
865
  suggestedAction: {
846
- parameter: "auctionFee",
866
+ parameter: "transactionFee",
847
867
  direction: "decrease",
848
868
  magnitude: 0.05,
849
869
  reasoning: `Only ${significantClusters} significant persona clusters (need ${thresholds.personaMinClusters}). Lower trade barriers to attract non-dominant persona types.`
@@ -867,7 +887,7 @@ var P12_OnePrimaryFaucet = {
867
887
  id: "P12",
868
888
  name: "One Primary Faucet",
869
889
  category: "currency",
870
- description: "Multiple independent currency sources (gathering + crafting + quests) each creating gold causes uncontrolled inflation. One clear primary faucet makes the economy predictable and auditable.",
890
+ description: "Multiple independent currency sources (gathering + production + quests) each creating currency causes uncontrolled inflation. One clear primary faucet makes the economy predictable and auditable.",
871
891
  check(metrics, thresholds) {
872
892
  const { netFlow, faucetVolume, sinkVolume } = metrics;
873
893
  if (netFlow > thresholds.netFlowWarnThreshold) {
@@ -876,10 +896,10 @@ var P12_OnePrimaryFaucet = {
876
896
  severity: 5,
877
897
  evidence: { netFlow, faucetVolume, sinkVolume },
878
898
  suggestedAction: {
879
- parameter: "craftingCost",
899
+ parameter: "productionCost",
880
900
  direction: "increase",
881
901
  magnitude: 0.15,
882
- reasoning: `Net flow +${netFlow.toFixed(1)} g/t. Inflationary. Increase crafting cost (primary sink) to balance faucet output.`
902
+ reasoning: `Net flow +${netFlow.toFixed(1)}/tick. Inflationary. Increase production cost (primary sink) to balance faucet output.`
883
903
  },
884
904
  confidence: 0.8,
885
905
  estimatedLag: 8
@@ -891,10 +911,10 @@ var P12_OnePrimaryFaucet = {
891
911
  severity: 4,
892
912
  evidence: { netFlow, faucetVolume, sinkVolume },
893
913
  suggestedAction: {
894
- parameter: "craftingCost",
914
+ parameter: "productionCost",
895
915
  direction: "decrease",
896
916
  magnitude: 0.15,
897
- reasoning: `Net flow ${netFlow.toFixed(1)} g/t. Deflationary. Decrease crafting cost to ease sink pressure.`
917
+ reasoning: `Net flow ${netFlow.toFixed(1)}/tick. Deflationary. Decrease production cost to ease sink pressure.`
898
918
  },
899
919
  confidence: 0.8,
900
920
  estimatedLag: 8
@@ -907,36 +927,40 @@ var P13_PotsAreZeroSumAndSelfRegulate = {
907
927
  id: "P13",
908
928
  name: "Pots Self-Regulate with Correct Multiplier",
909
929
  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.",
930
+ 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
931
  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
- };
932
+ const { poolSizes, populationByRole } = metrics;
933
+ const totalAgents = metrics.totalAgents;
934
+ const roleEntries = Object.entries(populationByRole).sort((a, b) => b[1] - a[1]);
935
+ const dominantRole = roleEntries[0]?.[0];
936
+ const dominantCount = roleEntries[0]?.[1] ?? 0;
937
+ for (const [poolName, poolSize] of Object.entries(poolSizes)) {
938
+ if (dominantCount > 5 && poolSize < 50) {
939
+ const { poolWinRate, poolHouseCut } = thresholds;
940
+ const maxSustainableMultiplier = (1 - poolHouseCut) / poolWinRate;
941
+ return {
942
+ violated: true,
943
+ severity: 7,
944
+ evidence: { pool: poolName, poolSize, participants: dominantCount, maxSustainableMultiplier },
945
+ suggestedAction: {
946
+ parameter: "rewardRate",
947
+ direction: "decrease",
948
+ magnitude: 0.15,
949
+ 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.`
950
+ },
951
+ confidence: 0.85,
952
+ estimatedLag: 3
953
+ };
954
+ }
931
955
  }
932
956
  return { violated: false };
933
957
  }
934
958
  };
935
959
  var P14_TrackActualInjection = {
936
960
  id: "P14",
937
- name: "Track Actual Gold Injection, Not Value Creation",
961
+ name: "Track Actual Currency Injection, Not Value Creation",
938
962
  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.',
963
+ description: 'Counting resource gathering as "currency injected" is misleading. Currency enters through faucet mechanisms (spawning, rewards). Fake metrics break every downstream decision.',
940
964
  check(metrics, _thresholds) {
941
965
  const { faucetVolume, netFlow, totalSupply } = metrics;
942
966
  const supplyGrowthRate = Math.abs(netFlow) / Math.max(1, totalSupply);
@@ -946,10 +970,10 @@ var P14_TrackActualInjection = {
946
970
  severity: 4,
947
971
  evidence: { faucetVolume, netFlow, supplyGrowthRate },
948
972
  suggestedAction: {
949
- parameter: "miningYield",
973
+ parameter: "yieldRate",
950
974
  direction: "decrease",
951
975
  magnitude: 0.1,
952
- reasoning: `Supply growing at ${(supplyGrowthRate * 100).toFixed(1)}%/tick. Verify gold injection tracking. Resources should not create gold directly.`
976
+ reasoning: `Supply growing at ${(supplyGrowthRate * 100).toFixed(1)}%/tick. Verify currency injection tracking. Resources should not create currency directly.`
953
977
  },
954
978
  confidence: 0.55,
955
979
  estimatedLag: 5
@@ -962,12 +986,11 @@ var P15_PoolsNeedCapAndDecay = {
962
986
  id: "P15",
963
987
  name: "Pools Need Cap + Decay",
964
988
  category: "currency",
965
- description: "Any pool (bank, reward pool) without a cap accumulates infinitely. Bank pool at 42% of gold supply means 42% of the economy is frozen. Cap at 5%, decay at 2%/tick.",
989
+ description: "Any pool (bank, reward pool) without a cap accumulates infinitely. A pool at 42% of total supply means 42% of the economy is frozen. Cap at 5%, decay at 2%/tick.",
966
990
  check(metrics, thresholds) {
967
991
  const { poolSizes, totalSupply } = metrics;
968
992
  const { poolCapPercent } = thresholds;
969
993
  for (const [pool, size] of Object.entries(poolSizes)) {
970
- if (pool === "arena" || pool === "arenaPot") continue;
971
994
  const shareOfSupply = size / Math.max(1, totalSupply);
972
995
  if (shareOfSupply > poolCapPercent * 2) {
973
996
  return {
@@ -975,10 +998,10 @@ var P15_PoolsNeedCapAndDecay = {
975
998
  severity: 6,
976
999
  evidence: { pool, size, shareOfSupply, cap: poolCapPercent },
977
1000
  suggestedAction: {
978
- parameter: "auctionFee",
1001
+ parameter: "transactionFee",
979
1002
  direction: "decrease",
980
1003
  magnitude: 0.1,
981
- reasoning: `${pool} pool at ${(shareOfSupply * 100).toFixed(1)}% of supply (cap: ${(poolCapPercent * 100).toFixed(0)}%). Gold frozen. Lower fees to encourage circulation over accumulation.`
1004
+ reasoning: `${pool} pool at ${(shareOfSupply * 100).toFixed(1)}% of supply (cap: ${(poolCapPercent * 100).toFixed(0)}%). Currency frozen. Lower fees to encourage circulation over accumulation.`
982
1005
  },
983
1006
  confidence: 0.85,
984
1007
  estimatedLag: 5
@@ -995,22 +1018,23 @@ var P16_WithdrawalPenaltyScales = {
995
1018
  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
1019
  check(metrics, _thresholds) {
997
1020
  const { poolSizes, totalSupply } = metrics;
998
- const bankPool = poolSizes["bank"] ?? poolSizes["bankPool"] ?? 0;
999
1021
  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
- };
1022
+ for (const [poolName, poolSize] of Object.entries(poolSizes)) {
1023
+ if (poolSize < 10 && stakedEstimate > 100) {
1024
+ return {
1025
+ violated: true,
1026
+ severity: 3,
1027
+ evidence: { pool: poolName, poolSize, estimatedStaked: stakedEstimate },
1028
+ suggestedAction: {
1029
+ parameter: "transactionFee",
1030
+ direction: "increase",
1031
+ magnitude: 0.05,
1032
+ reasoning: `${poolName} pool depleted while significant currency should be locked. Early withdrawals may be draining the pool. Ensure withdrawal penalty scales with lock duration.`
1033
+ },
1034
+ confidence: 0.45,
1035
+ estimatedLag: 10
1036
+ };
1037
+ }
1014
1038
  }
1015
1039
  return { violated: false };
1016
1040
  }
@@ -1029,7 +1053,7 @@ var P32_VelocityAboveSupply = {
1029
1053
  severity: 4,
1030
1054
  evidence: { velocity, totalSupply, totalResources },
1031
1055
  suggestedAction: {
1032
- parameter: "auctionFee",
1056
+ parameter: "transactionFee",
1033
1057
  direction: "decrease",
1034
1058
  magnitude: 0.2,
1035
1059
  reasoning: `Velocity ${velocity}/t with ${totalResources} resources in system. Economy stagnant despite available supply. Lower trading friction.`
@@ -1065,7 +1089,7 @@ var P58_NoNaturalNumeraire = {
1065
1089
  meanPrice: mean
1066
1090
  },
1067
1091
  suggestedAction: {
1068
- parameter: "craftingCost",
1092
+ parameter: "productionCost",
1069
1093
  direction: "increase",
1070
1094
  magnitude: 0.1,
1071
1095
  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 +1116,7 @@ var P17_GracePeriodBeforeIntervention = {
1092
1116
  id: "P17",
1093
1117
  name: "Grace Period Before Intervention",
1094
1118
  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.",
1119
+ 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
1120
  check(metrics, _thresholds) {
1097
1121
  if (metrics.tick < 30 && metrics.avgSatisfaction < 40) {
1098
1122
  return {
@@ -1100,7 +1124,7 @@ var P17_GracePeriodBeforeIntervention = {
1100
1124
  severity: 7,
1101
1125
  evidence: { tick: metrics.tick, avgSatisfaction: metrics.avgSatisfaction },
1102
1126
  suggestedAction: {
1103
- parameter: "arenaEntryFee",
1127
+ parameter: "entryFee",
1104
1128
  direction: "decrease",
1105
1129
  magnitude: 0.2,
1106
1130
  reasoning: `Very low satisfaction at tick ${metrics.tick}. Intervention may have fired during grace period. Ease all costs to let economy bootstrap.`
@@ -1116,27 +1140,26 @@ var P18_FirstProducerNeedsStartingInventory = {
1116
1140
  id: "P18",
1117
1141
  name: "First Producer Needs Starting Inventory + Capital",
1118
1142
  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.",
1143
+ 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
1144
  check(metrics, _thresholds) {
1121
1145
  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
- };
1146
+ const hasAgents = metrics.totalAgents > 0;
1147
+ for (const [resource, supply] of Object.entries(metrics.supplyByResource)) {
1148
+ if (supply === 0 && hasAgents) {
1149
+ return {
1150
+ violated: true,
1151
+ severity: 8,
1152
+ evidence: { tick: metrics.tick, resource, supply, totalAgents: metrics.totalAgents },
1153
+ suggestedAction: {
1154
+ parameter: "productionCost",
1155
+ direction: "decrease",
1156
+ magnitude: 0.5,
1157
+ reasoning: `Bootstrap failure: ${resource} supply is 0 at tick ${metrics.tick} with ${metrics.totalAgents} agents. Drastically reduce production cost to allow immediate output.`
1158
+ },
1159
+ confidence: 0.9,
1160
+ estimatedLag: 2
1161
+ };
1162
+ }
1140
1163
  }
1141
1164
  return { violated: false };
1142
1165
  }
@@ -1145,22 +1168,32 @@ var P19_StartingSupplyExceedsDemand = {
1145
1168
  id: "P19",
1146
1169
  name: "Starting Supply Exceeds Initial Demand",
1147
1170
  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.",
1171
+ 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
1172
  check(metrics, _thresholds) {
1150
1173
  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) {
1174
+ const roleEntries = Object.entries(metrics.populationByRole);
1175
+ if (roleEntries.length === 0) return { violated: false };
1176
+ const [mostPopulatedRole, population] = roleEntries.reduce(
1177
+ (max, entry) => entry[1] > max[1] ? entry : max
1178
+ );
1179
+ if (population < 5) return { violated: false };
1180
+ const totalResourceSupply = Object.values(metrics.supplyByResource).reduce((sum, s) => sum + s, 0);
1181
+ const resourcesPerAgent = totalResourceSupply / Math.max(1, population);
1182
+ if (resourcesPerAgent < 0.5) {
1155
1183
  return {
1156
1184
  violated: true,
1157
1185
  severity: 6,
1158
- evidence: { fighters, weapons, potions, weaponsPerFighter: weapons / Math.max(1, fighters) },
1186
+ evidence: {
1187
+ mostPopulatedRole,
1188
+ population,
1189
+ totalResourceSupply,
1190
+ resourcesPerAgent
1191
+ },
1159
1192
  suggestedAction: {
1160
- parameter: "arenaReward",
1193
+ parameter: "rewardRate",
1161
1194
  direction: "increase",
1162
1195
  magnitude: 0.2,
1163
- reasoning: `${fighters} fighters but only ${weapons} weapons. Cold-start scarcity. Boost arena reward to attract fighters even without weapons.`
1196
+ 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
1197
  },
1165
1198
  confidence: 0.75,
1166
1199
  estimatedLag: 5
@@ -1180,7 +1213,7 @@ var P20_DecayPreventsAccumulation = {
1180
1213
  id: "P20",
1181
1214
  name: "Decay Prevents Accumulation",
1182
1215
  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.",
1216
+ 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
1217
  check(metrics, _thresholds) {
1185
1218
  const { supplyByResource, velocity, totalAgents } = metrics;
1186
1219
  const totalResources = Object.values(supplyByResource).reduce((s, v) => s + v, 0);
@@ -1191,7 +1224,7 @@ var P20_DecayPreventsAccumulation = {
1191
1224
  severity: 4,
1192
1225
  evidence: { totalResources, resourcesPerAgent, velocity },
1193
1226
  suggestedAction: {
1194
- parameter: "miningYield",
1227
+ parameter: "yieldRate",
1195
1228
  direction: "decrease",
1196
1229
  magnitude: 0.1,
1197
1230
  reasoning: `${totalResources.toFixed(0)} resources with velocity ${velocity}/t. Likely hoarding. Reduce yield to increase scarcity and force circulation.`
@@ -1207,7 +1240,7 @@ var P21_PriceFromGlobalSupply = {
1207
1240
  id: "P21",
1208
1241
  name: "Price Reflects Global Supply, Not Just AH Listings",
1209
1242
  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.",
1243
+ description: "If prices only update from market activity, agents with hoarded inventory see artificially high prices and keep gathering when they should stop.",
1211
1244
  check(metrics, _thresholds) {
1212
1245
  const { priceVolatility, supplyByResource, prices } = metrics;
1213
1246
  for (const resource of Object.keys(prices)) {
@@ -1219,7 +1252,7 @@ var P21_PriceFromGlobalSupply = {
1219
1252
  severity: 3,
1220
1253
  evidence: { resource, volatility, supply, price: prices[resource] },
1221
1254
  suggestedAction: {
1222
- parameter: "auctionFee",
1255
+ parameter: "transactionFee",
1223
1256
  direction: "increase",
1224
1257
  magnitude: 0.05,
1225
1258
  reasoning: `${resource} price volatile (${(volatility * 100).toFixed(0)}%) despite supply ${supply}. Price may not reflect global inventory. Increase trading friction to stabilise.`
@@ -1239,23 +1272,36 @@ var P22_MarketAwarenessPreventsSurplus = {
1239
1272
  description: "Producers who craft without checking market prices will create surpluses that crash prices. Agents need to see prices before deciding to produce.",
1240
1273
  check(metrics, _thresholds) {
1241
1274
  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
- };
1275
+ const priceValues = Object.values(prices).filter((p) => p > 0);
1276
+ if (priceValues.length === 0) return { violated: false };
1277
+ const sortedPrices = [...priceValues].sort((a, b) => a - b);
1278
+ const medianPrice = sortedPrices[Math.floor(sortedPrices.length / 2)] ?? 0;
1279
+ for (const [resource, price] of Object.entries(prices)) {
1280
+ if (price <= 0) continue;
1281
+ const supply = supplyByResource[resource] ?? 0;
1282
+ const priceDeviation = price / Math.max(1, medianPrice);
1283
+ if (priceDeviation < 0.33 && supply > 100 && productionIndex > 0) {
1284
+ return {
1285
+ violated: true,
1286
+ severity: 4,
1287
+ evidence: {
1288
+ resource,
1289
+ price,
1290
+ medianPrice,
1291
+ priceDeviation,
1292
+ supply,
1293
+ productionIndex
1294
+ },
1295
+ suggestedAction: {
1296
+ parameter: "productionCost",
1297
+ direction: "increase",
1298
+ magnitude: 0.1,
1299
+ 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.`
1300
+ },
1301
+ confidence: 0.7,
1302
+ estimatedLag: 8
1303
+ };
1304
+ }
1259
1305
  }
1260
1306
  return { violated: false };
1261
1307
  }
@@ -1264,7 +1310,7 @@ var P23_ProfitabilityFactorsFeasibility = {
1264
1310
  id: "P23",
1265
1311
  name: "Profitability Factors Execution Feasibility",
1266
1312
  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.",
1313
+ 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
1314
  check(metrics, _thresholds) {
1269
1315
  const { avgSatisfaction, blockedAgentCount, totalAgents } = metrics;
1270
1316
  const blockedFraction = blockedAgentCount / Math.max(1, totalAgents);
@@ -1274,7 +1320,7 @@ var P23_ProfitabilityFactorsFeasibility = {
1274
1320
  severity: 5,
1275
1321
  evidence: { blockedFraction, blockedAgentCount, avgSatisfaction },
1276
1322
  suggestedAction: {
1277
- parameter: "craftingCost",
1323
+ parameter: "productionCost",
1278
1324
  direction: "decrease",
1279
1325
  magnitude: 0.15,
1280
1326
  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 +1346,7 @@ var P24_BlockedAgentsDecayFaster = {
1300
1346
  severity: 5,
1301
1347
  evidence: { blockedFraction, blockedAgentCount, churnRate },
1302
1348
  suggestedAction: {
1303
- parameter: "auctionFee",
1349
+ parameter: "transactionFee",
1304
1350
  direction: "decrease",
1305
1351
  magnitude: 0.15,
1306
1352
  reasoning: `${(blockedFraction * 100).toFixed(0)}% of agents blocked. Blocked agents churn silently, skewing metrics. Lower fees to unblock market participation.`
@@ -1325,26 +1371,35 @@ var P25_CorrectLeversForCorrectProblems = {
1325
1371
  id: "P25",
1326
1372
  name: "Target the Correct Lever",
1327
1373
  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.",
1374
+ 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
1375
  check(metrics, thresholds) {
1330
1376
  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
- };
1377
+ const resourceEntries = Object.entries(supplyByResource);
1378
+ if (resourceEntries.length === 0) return { violated: false };
1379
+ const totalSupply = resourceEntries.reduce((sum, [_, s]) => sum + s, 0);
1380
+ const avgSupply = totalSupply / resourceEntries.length;
1381
+ for (const [resource, supply] of resourceEntries) {
1382
+ if (supply > avgSupply * 3 && netFlow > thresholds.netFlowWarnThreshold) {
1383
+ return {
1384
+ violated: true,
1385
+ severity: 4,
1386
+ evidence: {
1387
+ resource,
1388
+ supply,
1389
+ avgSupply,
1390
+ ratio: supply / Math.max(1, avgSupply),
1391
+ netFlow
1392
+ },
1393
+ suggestedAction: {
1394
+ parameter: "yieldRate",
1395
+ direction: "decrease",
1396
+ magnitude: 0.15,
1397
+ 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.`
1398
+ },
1399
+ confidence: 0.75,
1400
+ estimatedLag: 8
1401
+ };
1402
+ }
1348
1403
  }
1349
1404
  return { violated: false };
1350
1405
  }
@@ -1362,7 +1417,7 @@ var P26_ContinuousPressureBeatsThresholdCuts = {
1362
1417
  severity: 4,
1363
1418
  evidence: { inflationRate },
1364
1419
  suggestedAction: {
1365
- parameter: "craftingCost",
1420
+ parameter: "productionCost",
1366
1421
  direction: inflationRate > 0 ? "increase" : "decrease",
1367
1422
  magnitude: Math.min(thresholds.maxAdjustmentPercent, 0.05),
1368
1423
  // force smaller step
@@ -1388,7 +1443,7 @@ var P27_AdjustmentsNeedCooldowns = {
1388
1443
  severity: 4,
1389
1444
  evidence: { churnRate, avgSatisfaction },
1390
1445
  suggestedAction: {
1391
- parameter: "arenaEntryFee",
1446
+ parameter: "entryFee",
1392
1447
  direction: "decrease",
1393
1448
  magnitude: 0.05,
1394
1449
  reasoning: `High churn (${(churnRate * 100).toFixed(1)}%) with low satisfaction. Possible oscillation from rapid adjustments. Apply small correction only.`
@@ -1404,7 +1459,7 @@ var P28_StructuralDominanceIsNotPathological = {
1404
1459
  id: "P28",
1405
1460
  name: "Structural Dominance \u2260 Pathological Monopoly",
1406
1461
  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".',
1462
+ 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
1463
  check(metrics, _thresholds) {
1409
1464
  const { roleShares, avgSatisfaction } = metrics;
1410
1465
  const dominant = Object.entries(roleShares).sort((a, b) => b[1] - a[1])[0];
@@ -1419,7 +1474,7 @@ var P28_StructuralDominanceIsNotPathological = {
1419
1474
  severity: 5,
1420
1475
  evidence: { dominantRole, dominantShare, avgSatisfaction },
1421
1476
  suggestedAction: {
1422
- parameter: "craftingCost",
1477
+ parameter: "productionCost",
1423
1478
  direction: "decrease",
1424
1479
  magnitude: 0.1,
1425
1480
  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 +1499,7 @@ var P38_CommunicationPreventsRevolt = {
1444
1499
  severity: 3,
1445
1500
  evidence: { churnRate },
1446
1501
  suggestedAction: {
1447
- parameter: "arenaReward",
1502
+ parameter: "rewardRate",
1448
1503
  direction: "increase",
1449
1504
  magnitude: 0.1,
1450
1505
  reasoning: `High churn (${(churnRate * 100).toFixed(1)}%) \u2014 agents leaving. Ensure all recent adjustments are logged with reasoning to diagnose cause.`
@@ -1469,7 +1524,7 @@ var P29_PinchPoint = {
1469
1524
  id: "P29",
1470
1525
  name: "Pinch Point",
1471
1526
  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.",
1527
+ 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
1528
  check(metrics, _thresholds) {
1474
1529
  const { pinchPoints, supplyByResource, demandSignals } = metrics;
1475
1530
  for (const [resource, status] of Object.entries(pinchPoints)) {
@@ -1481,7 +1536,7 @@ var P29_PinchPoint = {
1481
1536
  severity: 7,
1482
1537
  evidence: { resource, supply, demand, status },
1483
1538
  suggestedAction: {
1484
- parameter: "craftingCost",
1539
+ parameter: "productionCost",
1485
1540
  direction: "decrease",
1486
1541
  magnitude: 0.15,
1487
1542
  reasoning: `${resource} is a pinch point and currently SCARCE (supply ${supply}, demand ${demand}). Reduce production cost to increase throughput.`
@@ -1497,7 +1552,7 @@ var P29_PinchPoint = {
1497
1552
  severity: 4,
1498
1553
  evidence: { resource, supply, status },
1499
1554
  suggestedAction: {
1500
- parameter: "craftingCost",
1555
+ parameter: "productionCost",
1501
1556
  direction: "increase",
1502
1557
  magnitude: 0.1,
1503
1558
  reasoning: `${resource} is a pinch point and OVERSUPPLIED (supply ${supply}). Raise production cost to reduce surplus.`
@@ -1514,7 +1569,7 @@ var P30_MovingPinchPoint = {
1514
1569
  id: "P30",
1515
1570
  name: "Moving Pinch Point",
1516
1571
  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.",
1572
+ 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
1573
  check(metrics, _thresholds) {
1519
1574
  const { capacityUsage, supplyByResource, avgSatisfaction } = metrics;
1520
1575
  const totalResources = Object.values(supplyByResource).reduce((s, v) => s + v, 0);
@@ -1525,7 +1580,7 @@ var P30_MovingPinchPoint = {
1525
1580
  severity: 3,
1526
1581
  evidence: { capacityUsage, resourcesPerAgent, avgSatisfaction },
1527
1582
  suggestedAction: {
1528
- parameter: "craftingCost",
1583
+ parameter: "productionCost",
1529
1584
  direction: "increase",
1530
1585
  magnitude: 0.1,
1531
1586
  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 +1626,7 @@ var P57_CombinatorialPriceSpace = {
1571
1626
  target: thresholds.relativePriceConvergenceTarget
1572
1627
  },
1573
1628
  suggestedAction: {
1574
- parameter: "auctionFee",
1629
+ parameter: "transactionFee",
1575
1630
  direction: "decrease",
1576
1631
  magnitude: 0.1,
1577
1632
  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 +1649,7 @@ var P31_AnchorValueTracking = {
1594
1649
  id: "P31",
1595
1650
  name: "Anchor Value Tracking",
1596
1651
  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.",
1652
+ 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
1653
  check(metrics, _thresholds) {
1599
1654
  const { anchorRatioDrift, inflationRate } = metrics;
1600
1655
  if (Math.abs(anchorRatioDrift) > 0.25) {
@@ -1603,10 +1658,10 @@ var P31_AnchorValueTracking = {
1603
1658
  severity: 5,
1604
1659
  evidence: { anchorRatioDrift, inflationRate },
1605
1660
  suggestedAction: {
1606
- parameter: "craftingCost",
1661
+ parameter: "productionCost",
1607
1662
  direction: anchorRatioDrift > 0 ? "increase" : "decrease",
1608
1663
  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.`
1664
+ reasoning: `Anchor ratio has drifted ${(anchorRatioDrift * 100).toFixed(0)}% from baseline. Time-to-value for participants is changing. Adjust production costs to restore.`
1610
1665
  },
1611
1666
  confidence: 0.65,
1612
1667
  estimatedLag: 10
@@ -1628,7 +1683,7 @@ var P41_MultiResolutionMonitoring = {
1628
1683
  severity: 4,
1629
1684
  evidence: { giniCoefficient, avgSatisfaction },
1630
1685
  suggestedAction: {
1631
- parameter: "auctionFee",
1686
+ parameter: "transactionFee",
1632
1687
  direction: "increase",
1633
1688
  magnitude: 0.1,
1634
1689
  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 +1712,7 @@ var P55_ArbitrageThermometer = {
1657
1712
  critical: thresholds.arbitrageIndexCritical
1658
1713
  },
1659
1714
  suggestedAction: {
1660
- parameter: "auctionFee",
1715
+ parameter: "transactionFee",
1661
1716
  direction: "decrease",
1662
1717
  magnitude: 0.15,
1663
1718
  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 +1730,7 @@ var P55_ArbitrageThermometer = {
1675
1730
  warning: thresholds.arbitrageIndexWarning
1676
1731
  },
1677
1732
  suggestedAction: {
1678
- parameter: "auctionFee",
1733
+ parameter: "transactionFee",
1679
1734
  direction: "decrease",
1680
1735
  magnitude: 0.08,
1681
1736
  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 +1758,7 @@ var P59_GiftEconomyNoise = {
1703
1758
  threshold: thresholds.giftTradeFilterRatio
1704
1759
  },
1705
1760
  suggestedAction: {
1706
- parameter: "auctionFee",
1761
+ parameter: "transactionFee",
1707
1762
  direction: "increase",
1708
1763
  magnitude: 0.05,
1709
1764
  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 +1782,7 @@ var P42_TheMedianPrinciple = {
1727
1782
  id: "P42",
1728
1783
  name: "The Median Principle",
1729
1784
  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%.",
1785
+ 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
1786
  check(metrics, thresholds) {
1732
1787
  const { meanMedianDivergence, giniCoefficient } = metrics;
1733
1788
  if (meanMedianDivergence > thresholds.meanMedianDivergenceMax) {
@@ -1741,10 +1796,10 @@ var P42_TheMedianPrinciple = {
1741
1796
  medianBalance: metrics.medianBalance
1742
1797
  },
1743
1798
  suggestedAction: {
1744
- parameter: "auctionFee",
1799
+ parameter: "transactionFee",
1745
1800
  direction: "increase",
1746
1801
  magnitude: 0.15,
1747
- 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.`
1802
+ reasoning: `Mean/median divergence ${(meanMedianDivergence * 100).toFixed(0)}% (threshold: ${(thresholds.meanMedianDivergenceMax * 100).toFixed(0)}%). Economy has outliers skewing metrics. Use median for decisions. Raise transaction fees to redistribute wealth.`
1748
1803
  },
1749
1804
  confidence: 0.85,
1750
1805
  estimatedLag: 15
@@ -1766,7 +1821,7 @@ var P43_SimulationMinimum = {
1766
1821
  severity: 3,
1767
1822
  evidence: { inflationRate, minIterations: thresholds.simulationMinIterations },
1768
1823
  suggestedAction: {
1769
- parameter: "craftingCost",
1824
+ parameter: "productionCost",
1770
1825
  direction: inflationRate > 0 ? "increase" : "decrease",
1771
1826
  magnitude: 0.05,
1772
1827
  reasoning: `Large inflation rate swing (${(inflationRate * 100).toFixed(0)}%). Ensure all decisions use \u2265${thresholds.simulationMinIterations} simulation iterations. Apply conservative correction.`
@@ -1804,7 +1859,7 @@ var P39_TheLagPrinciple = {
1804
1859
  severity: 5,
1805
1860
  evidence: { inflationRate, netFlow, lagRange: [lagMin, lagMax] },
1806
1861
  suggestedAction: {
1807
- parameter: "craftingCost",
1862
+ parameter: "productionCost",
1808
1863
  direction: "increase",
1809
1864
  magnitude: 0.03,
1810
1865
  // very small — oscillation means over-adjusting
@@ -1831,7 +1886,7 @@ var P44_ComplexityBudget = {
1831
1886
  severity: 3,
1832
1887
  evidence: { customMetricCount, budgetMax: thresholds.complexityBudgetMax },
1833
1888
  suggestedAction: {
1834
- parameter: "auctionFee",
1889
+ parameter: "transactionFee",
1835
1890
  direction: "decrease",
1836
1891
  magnitude: 0.01,
1837
1892
  reasoning: `${customMetricCount} custom metrics tracked (budget: ${thresholds.complexityBudgetMax}). Consider pruning low-impact parameters. Applying minimal correction to avoid adding complexity.`
@@ -1853,25 +1908,25 @@ var P35_DestructionCreatesValue = {
1853
1908
  id: "P35",
1854
1909
  name: "Destruction Creates Value",
1855
1910
  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.",
1911
+ description: "If nothing is ever permanently lost, inflation is inevitable. Resource durability and consumption mechanisms create destruction. Without them, supply grows without bound.",
1857
1912
  check(metrics, _thresholds) {
1858
1913
  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
- };
1914
+ for (const [resource, supply] of Object.entries(supplyByResource)) {
1915
+ if (supply > 200 && sinkVolume < 5 && netFlow > 0) {
1916
+ return {
1917
+ violated: true,
1918
+ severity: 6,
1919
+ evidence: { resource, supply, sinkVolume, netFlow },
1920
+ suggestedAction: {
1921
+ parameter: "entryFee",
1922
+ direction: "decrease",
1923
+ magnitude: 0.1,
1924
+ reasoning: `${resource} supply at ${supply} units with low destruction (sink ${sinkVolume}/t). Resources not being consumed. Lower competitive pool entry to increase resource usage.`
1925
+ },
1926
+ confidence: 0.7,
1927
+ estimatedLag: 5
1928
+ };
1929
+ }
1875
1930
  }
1876
1931
  return { violated: false };
1877
1932
  }
@@ -1891,7 +1946,7 @@ var P40_ReplacementRate = {
1891
1946
  severity: 6,
1892
1947
  evidence: { productionIndex, sinkVolume, replacementRatio },
1893
1948
  suggestedAction: {
1894
- parameter: "miningYield",
1949
+ parameter: "yieldRate",
1895
1950
  direction: "increase",
1896
1951
  magnitude: 0.15,
1897
1952
  reasoning: `Replacement rate ${replacementRatio.toFixed(2)} (need \u2265${thresholds.replacementRateMultiplier}). Production below consumption. Resources will deplete. Increase yield.`
@@ -1905,7 +1960,7 @@ var P40_ReplacementRate = {
1905
1960
  severity: 3,
1906
1961
  evidence: { productionIndex, sinkVolume, replacementRatio },
1907
1962
  suggestedAction: {
1908
- parameter: "miningYield",
1963
+ parameter: "yieldRate",
1909
1964
  direction: "decrease",
1910
1965
  magnitude: 0.1,
1911
1966
  reasoning: `Replacement rate ${replacementRatio.toFixed(2)} \u2014 overproducing. Production far exceeds consumption. Reduce yield to prevent glut.`
@@ -1931,7 +1986,7 @@ var P49_IdleAssetTax = {
1931
1986
  severity: 5,
1932
1987
  evidence: { giniCoefficient, top10PctShare, velocity },
1933
1988
  suggestedAction: {
1934
- parameter: "auctionFee",
1989
+ parameter: "transactionFee",
1935
1990
  direction: "increase",
1936
1991
  magnitude: 0.15,
1937
1992
  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 +2018,7 @@ var P33_FairNotEqual = {
1963
2018
  severity: 3,
1964
2019
  evidence: { giniCoefficient },
1965
2020
  suggestedAction: {
1966
- parameter: "arenaReward",
2021
+ parameter: "rewardRate",
1967
2022
  direction: "increase",
1968
2023
  magnitude: 0.1,
1969
2024
  reasoning: `Gini ${giniCoefficient.toFixed(2)} \u2014 near-perfect equality. Economy lacks stakes. Increase winner rewards to create meaningful spread.`
@@ -1978,7 +2033,7 @@ var P33_FairNotEqual = {
1978
2033
  severity: 7,
1979
2034
  evidence: { giniCoefficient },
1980
2035
  suggestedAction: {
1981
- parameter: "auctionFee",
2036
+ parameter: "transactionFee",
1982
2037
  direction: "increase",
1983
2038
  magnitude: 0.2,
1984
2039
  reasoning: `Gini ${giniCoefficient.toFixed(2)} \u2014 oligarchy level. Toxic inequality. Raise transaction fees to redistribute wealth from rich to pool.`
@@ -1993,7 +2048,7 @@ var P33_FairNotEqual = {
1993
2048
  severity: 4,
1994
2049
  evidence: { giniCoefficient },
1995
2050
  suggestedAction: {
1996
- parameter: "auctionFee",
2051
+ parameter: "transactionFee",
1997
2052
  direction: "increase",
1998
2053
  magnitude: 0.1,
1999
2054
  reasoning: `Gini ${giniCoefficient.toFixed(2)} \u2014 high inequality warning. Gently raise fees to slow wealth concentration.`
@@ -2018,7 +2073,7 @@ var P36_MechanicFrictionDetector = {
2018
2073
  severity: 5,
2019
2074
  evidence: { churnRate, avgSatisfaction, velocity },
2020
2075
  suggestedAction: {
2021
- parameter: "arenaReward",
2076
+ parameter: "rewardRate",
2022
2077
  direction: "increase",
2023
2078
  magnitude: 0.15,
2024
2079
  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 +2098,7 @@ var P37_LatecommerProblem = {
2043
2098
  severity: 6,
2044
2099
  evidence: { timeToValue, avgSatisfaction, churnRate },
2045
2100
  suggestedAction: {
2046
- parameter: "craftingCost",
2101
+ parameter: "productionCost",
2047
2102
  direction: "decrease",
2048
2103
  magnitude: 0.15,
2049
2104
  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 +2125,7 @@ var P45_TimeBudget = {
2070
2125
  severity: 5,
2071
2126
  evidence: { timeToValue, avgSatisfaction, timeBudgetRatio: thresholds.timeBudgetRatio },
2072
2127
  suggestedAction: {
2073
- parameter: "arenaEntryFee",
2128
+ parameter: "entryFee",
2074
2129
  direction: "decrease",
2075
2130
  magnitude: 0.15,
2076
2131
  reasoning: `Time-to-value ${timeToValue} ticks with ${avgSatisfaction.toFixed(0)} satisfaction. Economy requires too much time investment. Lower barriers to participation.`
@@ -2100,7 +2155,7 @@ var P50_PayPowerRatio = {
2100
2155
  threshold: thresholds.payPowerRatioMax
2101
2156
  },
2102
2157
  suggestedAction: {
2103
- parameter: "auctionFee",
2158
+ parameter: "transactionFee",
2104
2159
  direction: "increase",
2105
2160
  magnitude: 0.2,
2106
2161
  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 +2190,7 @@ var P34_ExtractionRatio = {
2135
2190
  severity: 8,
2136
2191
  evidence: { extractionRatio, threshold: thresholds.extractionRatioRed },
2137
2192
  suggestedAction: {
2138
- parameter: "auctionFee",
2193
+ parameter: "transactionFee",
2139
2194
  direction: "increase",
2140
2195
  magnitude: 0.25,
2141
2196
  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 +2205,7 @@ var P34_ExtractionRatio = {
2150
2205
  severity: 5,
2151
2206
  evidence: { extractionRatio, threshold: thresholds.extractionRatioYellow },
2152
2207
  suggestedAction: {
2153
- parameter: "auctionFee",
2208
+ parameter: "transactionFee",
2154
2209
  direction: "increase",
2155
2210
  magnitude: 0.1,
2156
2211
  reasoning: `Extraction ratio ${(extractionRatio * 100).toFixed(0)}% (warning: ${(thresholds.extractionRatioYellow * 100).toFixed(0)}%). Economy trending toward extraction-heavy. Apply early pressure.`
@@ -2166,7 +2221,7 @@ var P47_SmokeTest = {
2166
2221
  id: "P47",
2167
2222
  name: "Smoke Test",
2168
2223
  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.",
2224
+ 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
2225
  check(metrics, thresholds) {
2171
2226
  const { smokeTestRatio } = metrics;
2172
2227
  if (isNaN(smokeTestRatio)) return { violated: false };
@@ -2176,7 +2231,7 @@ var P47_SmokeTest = {
2176
2231
  severity: 9,
2177
2232
  evidence: { smokeTestRatio, threshold: thresholds.smokeTestCritical },
2178
2233
  suggestedAction: {
2179
- parameter: "arenaReward",
2234
+ parameter: "rewardRate",
2180
2235
  direction: "increase",
2181
2236
  magnitude: 0.2,
2182
2237
  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 +2246,7 @@ var P47_SmokeTest = {
2191
2246
  severity: 6,
2192
2247
  evidence: { smokeTestRatio, threshold: thresholds.smokeTestWarning },
2193
2248
  suggestedAction: {
2194
- parameter: "arenaReward",
2249
+ parameter: "rewardRate",
2195
2250
  direction: "increase",
2196
2251
  magnitude: 0.1,
2197
2252
  reasoning: `Utility/market ratio ${(smokeTestRatio * 100).toFixed(0)}% (warning). Economy is >70% speculative. Boost utility rewards to restore intrinsic value anchor.`
@@ -2207,7 +2262,7 @@ var P48_CurrencyInsulation = {
2207
2262
  id: "P48",
2208
2263
  name: "Currency Insulation",
2209
2264
  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.",
2265
+ 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
2266
  check(metrics, thresholds) {
2212
2267
  const { currencyInsulation } = metrics;
2213
2268
  if (isNaN(currencyInsulation)) return { violated: false };
@@ -2217,7 +2272,7 @@ var P48_CurrencyInsulation = {
2217
2272
  severity: 6,
2218
2273
  evidence: { currencyInsulation, threshold: thresholds.currencyInsulationMax },
2219
2274
  suggestedAction: {
2220
- parameter: "auctionFee",
2275
+ parameter: "transactionFee",
2221
2276
  direction: "increase",
2222
2277
  magnitude: 0.1,
2223
2278
  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 +2312,7 @@ var P51_SharkTooth = {
2257
2312
  threshold: thresholds.sharkToothPeakDecay
2258
2313
  },
2259
2314
  suggestedAction: {
2260
- parameter: "arenaReward",
2315
+ parameter: "rewardRate",
2261
2316
  direction: "increase",
2262
2317
  magnitude: 0.1,
2263
2318
  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 +2330,7 @@ var P51_SharkTooth = {
2275
2330
  severity: 4,
2276
2331
  evidence: { lastValley, prevValley, ratio: lastValley / prevValley },
2277
2332
  suggestedAction: {
2278
- parameter: "craftingCost",
2333
+ parameter: "productionCost",
2279
2334
  direction: "decrease",
2280
2335
  magnitude: 0.1,
2281
2336
  reasoning: "Between-event engagement declining (deepening valleys). Base economy not sustaining participants between events. Lower production costs to improve off-event value."
@@ -2292,7 +2347,7 @@ var P52_EndowmentEffect = {
2292
2347
  id: "P52",
2293
2348
  name: "Endowment Effect",
2294
2349
  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).",
2350
+ 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
2351
  check(metrics, _thresholds) {
2297
2352
  const { avgSatisfaction, churnRate } = metrics;
2298
2353
  const { eventCompletionRate } = metrics;
@@ -2303,7 +2358,7 @@ var P52_EndowmentEffect = {
2303
2358
  severity: 4,
2304
2359
  evidence: { eventCompletionRate, avgSatisfaction, churnRate },
2305
2360
  suggestedAction: {
2306
- parameter: "arenaReward",
2361
+ parameter: "rewardRate",
2307
2362
  direction: "increase",
2308
2363
  magnitude: 0.15,
2309
2364
  reasoning: `${(eventCompletionRate * 100).toFixed(0)}% event completion but satisfaction only ${avgSatisfaction.toFixed(0)}. Events not creating perceived value. Increase reward quality/quantity.`
@@ -2333,10 +2388,10 @@ var P53_EventCompletionRate = {
2333
2388
  max: thresholds.eventCompletionMax
2334
2389
  },
2335
2390
  suggestedAction: {
2336
- parameter: "craftingCost",
2391
+ parameter: "productionCost",
2337
2392
  direction: "decrease",
2338
2393
  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.`
2394
+ 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
2395
  },
2341
2396
  confidence: 0.8,
2342
2397
  estimatedLag: 10
@@ -2348,7 +2403,7 @@ var P53_EventCompletionRate = {
2348
2403
  severity: 3,
2349
2404
  evidence: { eventCompletionRate, max: thresholds.eventCompletionMax },
2350
2405
  suggestedAction: {
2351
- parameter: "arenaEntryFee",
2406
+ parameter: "entryFee",
2352
2407
  direction: "increase",
2353
2408
  magnitude: 0.05,
2354
2409
  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 +2428,7 @@ var P54_LiveOpsCadence = {
2373
2428
  severity: 3,
2374
2429
  evidence: { velocity, avgSatisfaction, tick: metrics.tick },
2375
2430
  suggestedAction: {
2376
- parameter: "arenaReward",
2431
+ parameter: "rewardRate",
2377
2432
  direction: "increase",
2378
2433
  magnitude: 0.1,
2379
2434
  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 +2459,7 @@ var P56_ContentDropShock = {
2404
2459
  postDropMax: thresholds.postDropArbitrageMax
2405
2460
  },
2406
2461
  suggestedAction: {
2407
- parameter: "auctionFee",
2462
+ parameter: "transactionFee",
2408
2463
  direction: "decrease",
2409
2464
  magnitude: 0.1,
2410
2465
  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 +2625,21 @@ var Simulator = class {
2570
2625
  flowEffect(action, metrics) {
2571
2626
  const { parameter, direction } = action;
2572
2627
  const sign = direction === "increase" ? -1 : 1;
2573
- if (parameter === "craftingCost" || parameter === "alchemyCost") {
2628
+ const roleEntries = Object.entries(metrics.populationByRole).sort((a, b) => b[1] - a[1]);
2629
+ const dominantRoleCount = roleEntries[0]?.[1] ?? 0;
2630
+ if (parameter === "productionCost") {
2574
2631
  return sign * metrics.netFlow * 0.2;
2575
2632
  }
2576
- if (parameter === "auctionFee") {
2633
+ if (parameter === "transactionFee") {
2577
2634
  return sign * metrics.velocity * 10 * 0.1;
2578
2635
  }
2579
- if (parameter === "arenaEntryFee") {
2580
- return sign * (metrics.populationByRole["Fighter"] ?? 0) * 0.5;
2636
+ if (parameter === "entryFee") {
2637
+ return sign * dominantRoleCount * 0.5;
2581
2638
  }
2582
- if (parameter === "arenaReward") {
2583
- return -sign * (metrics.populationByRole["Fighter"] ?? 0) * 0.3;
2639
+ if (parameter === "rewardRate") {
2640
+ return -sign * dominantRoleCount * 0.3;
2584
2641
  }
2585
- if (parameter === "miningYield" || parameter === "lumberYield") {
2642
+ if (parameter === "yieldRate") {
2586
2643
  return sign * metrics.faucetVolume * 0.15;
2587
2644
  }
2588
2645
  return sign * metrics.netFlow * 0.1;
@@ -2895,30 +2952,33 @@ var RingBuffer = class {
2895
2952
  }
2896
2953
  };
2897
2954
  var MetricStore = class {
2898
- constructor() {
2955
+ constructor(tickConfig) {
2899
2956
  /** Fine: last 200 ticks, one entry per tick */
2900
2957
  this.fine = new RingBuffer(200);
2901
- /** Medium: last 200 windows of 10 ticks */
2958
+ /** Medium: last 200 windows */
2902
2959
  this.medium = new RingBuffer(200);
2903
- /** Coarse: last 200 epochs of 100 ticks */
2960
+ /** Coarse: last 200 epochs */
2904
2961
  this.coarse = new RingBuffer(200);
2905
2962
  this.ticksSinceLastMedium = 0;
2906
2963
  this.ticksSinceLastCoarse = 0;
2907
2964
  this.mediumAccumulator = [];
2908
2965
  this.coarseAccumulator = [];
2966
+ const config = { ...DEFAULT_TICK_CONFIG, ...tickConfig };
2967
+ this.mediumWindow = config.mediumWindow;
2968
+ this.coarseWindow = config.coarseWindow;
2909
2969
  }
2910
2970
  record(metrics) {
2911
2971
  this.fine.push(metrics);
2912
2972
  this.mediumAccumulator.push(metrics);
2913
2973
  this.ticksSinceLastMedium++;
2914
- if (this.ticksSinceLastMedium >= 10) {
2974
+ if (this.ticksSinceLastMedium >= this.mediumWindow) {
2915
2975
  this.medium.push(this.aggregate(this.mediumAccumulator));
2916
2976
  this.mediumAccumulator = [];
2917
2977
  this.ticksSinceLastMedium = 0;
2918
2978
  }
2919
2979
  this.coarseAccumulator.push(metrics);
2920
2980
  this.ticksSinceLastCoarse++;
2921
- if (this.ticksSinceLastCoarse >= 100) {
2981
+ if (this.ticksSinceLastCoarse >= this.coarseWindow) {
2922
2982
  this.coarse.push(this.aggregate(this.coarseAccumulator));
2923
2983
  this.coarseAccumulator = [];
2924
2984
  this.ticksSinceLastCoarse = 0;
@@ -3018,7 +3078,7 @@ var PersonaTracker = class {
3018
3078
  /** Classify all agents and return persona distribution */
3019
3079
  getDistribution() {
3020
3080
  const counts = {
3021
- Gamer: 0,
3081
+ Active: 0,
3022
3082
  Trader: 0,
3023
3083
  Collector: 0,
3024
3084
  Speculator: 0,
@@ -3042,7 +3102,7 @@ var PersonaTracker = class {
3042
3102
  return distribution;
3043
3103
  }
3044
3104
  classify(history) {
3045
- if (history.length === 0) return "Gamer";
3105
+ if (history.length === 0) return "Active";
3046
3106
  const avg = (key) => {
3047
3107
  const vals = history.map((h) => h[key]);
3048
3108
  return vals.reduce((s, v) => s + v, 0) / vals.length;
@@ -3056,21 +3116,18 @@ var PersonaTracker = class {
3056
3116
  if (uniqueItems > 5 && extraction < 0) return "Collector";
3057
3117
  if (extraction > 100) return "Earner";
3058
3118
  if (extraction > 50) return "Speculator";
3059
- return "Gamer";
3119
+ return "Active";
3060
3120
  }
3061
3121
  };
3062
3122
 
3063
3123
  // src/AgentE.ts
3064
3124
  var AgentE = class {
3065
3125
  constructor(config) {
3066
- // ── Pipeline ──
3067
- this.observer = new Observer();
3068
3126
  this.simulator = new Simulator();
3069
3127
  this.planner = new Planner();
3070
3128
  this.executor = new Executor();
3071
3129
  // ── State ──
3072
3130
  this.log = new DecisionLog();
3073
- this.store = new MetricStore();
3074
3131
  this.personaTracker = new PersonaTracker();
3075
3132
  this.params = {};
3076
3133
  this.eventBuffer = [];
@@ -3089,6 +3146,7 @@ var AgentE = class {
3089
3146
  mode: this.mode,
3090
3147
  dominantRoles: config.dominantRoles ?? [],
3091
3148
  idealDistribution: config.idealDistribution ?? {},
3149
+ tickConfig: config.tickConfig ?? { duration: 1, unit: "tick" },
3092
3150
  gracePeriod: config.gracePeriod ?? 50,
3093
3151
  checkInterval: config.checkInterval ?? 5,
3094
3152
  maxAdjustmentPercent: config.maxAdjustmentPercent ?? 0.15,
@@ -3100,6 +3158,9 @@ var AgentE = class {
3100
3158
  maxAdjustmentPercent: config.maxAdjustmentPercent ?? DEFAULT_THRESHOLDS.maxAdjustmentPercent,
3101
3159
  cooldownTicks: config.cooldownTicks ?? DEFAULT_THRESHOLDS.cooldownTicks
3102
3160
  };
3161
+ const tickConfig = { ...DEFAULT_TICK_CONFIG, ...config.tickConfig };
3162
+ this.observer = new Observer(tickConfig);
3163
+ this.store = new MetricStore(tickConfig);
3103
3164
  this.diagnoser = new Diagnoser(ALL_PRINCIPLES);
3104
3165
  if (config.onDecision) this.on("decision", config.onDecision);
3105
3166
  if (config.onAlert) this.on("alert", config.onAlert);
@@ -3131,7 +3192,7 @@ var AgentE = class {
3131
3192
  this.isRunning = false;
3132
3193
  this.isPaused = false;
3133
3194
  }
3134
- // ── Main cycle (call once per tick from your game loop) ────────────────────
3195
+ // ── Main cycle (call once per tick from your economy loop) ─────────────────
3135
3196
  async tick(state) {
3136
3197
  if (!this.isRunning || this.isPaused) return;
3137
3198
  const currentState = state ?? await Promise.resolve(this.adapter.getState());