@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.js CHANGED
@@ -144,8 +144,8 @@ var DEFAULT_THRESHOLDS = {
144
144
  maxAdjustmentPercent: 0.15,
145
145
  cooldownTicks: 15,
146
146
  // Currency (P13)
147
- arenaWinRate: 0.65,
148
- arenaHouseCut: 0.1,
147
+ poolWinRate: 0.65,
148
+ poolHouseCut: 0.1,
149
149
  // Population balance (P9)
150
150
  roleSwitchFrictionMax: 0.05,
151
151
  // >5% of population switching in one period = herd
@@ -176,7 +176,7 @@ var DEFAULT_THRESHOLDS = {
176
176
  disposalTradeWeightDiscount: 0.5
177
177
  };
178
178
  var PERSONA_HEALTHY_RANGES = {
179
- Gamer: { min: 0.2, max: 0.4 },
179
+ Active: { min: 0.2, max: 0.4 },
180
180
  Trader: { min: 0.05, max: 0.15 },
181
181
  Collector: { min: 0.05, max: 0.15 },
182
182
  Speculator: { min: 0, max: 0.1 },
@@ -186,14 +186,21 @@ var PERSONA_HEALTHY_RANGES = {
186
186
  Whale: { min: 0, max: 0.05 },
187
187
  Influencer: { min: 0, max: 0.05 }
188
188
  };
189
+ var DEFAULT_TICK_CONFIG = {
190
+ duration: 1,
191
+ unit: "tick",
192
+ mediumWindow: 10,
193
+ coarseWindow: 100
194
+ };
189
195
 
190
196
  // src/Observer.ts
191
197
  var Observer = class {
192
- constructor() {
198
+ constructor(tickConfig) {
193
199
  this.previousMetrics = null;
194
200
  this.previousPrices = {};
195
201
  this.customMetricFns = {};
196
202
  this.anchorBaseline = null;
203
+ this.tickConfig = { ...DEFAULT_TICK_CONFIG, ...tickConfig };
197
204
  }
198
205
  registerCustomMetric(name, fn) {
199
206
  this.customMetricFns[name] = fn;
@@ -208,6 +215,7 @@ var Observer = class {
208
215
  const tradeEvents = [];
209
216
  const roleChangeEvents = [];
210
217
  let churnCount = 0;
218
+ let productionAmount = 0;
211
219
  for (const e of recentEvents) {
212
220
  switch (e.type) {
213
221
  case "mint":
@@ -218,6 +226,9 @@ var Observer = class {
218
226
  case "consume":
219
227
  sinkVolume += e.amount ?? 0;
220
228
  break;
229
+ case "produce":
230
+ productionAmount += e.amount ?? 1;
231
+ break;
221
232
  case "trade":
222
233
  tradeEvents.push(e);
223
234
  break;
@@ -291,24 +302,24 @@ var Observer = class {
291
302
  pinchPoints[resource] = "optimal";
292
303
  }
293
304
  }
294
- const productionIndex = recentEvents.filter((e) => e.type === "produce").reduce((s, e) => s + (e.amount ?? 1), 0);
305
+ const productionIndex = productionAmount;
295
306
  const maxPossibleProduction = productionIndex + sinkVolume;
296
307
  const capacityUsage = maxPossibleProduction > 0 ? productionIndex / maxPossibleProduction : 0;
297
308
  const satisfactions = Object.values(state.agentSatisfaction ?? {});
298
309
  const avgSatisfaction = satisfactions.length > 0 ? satisfactions.reduce((s, v) => s + v, 0) / satisfactions.length : 80;
299
310
  const blockedAgentCount = satisfactions.filter((s) => s < 20).length;
300
- const timeToValue = tick > 0 ? Math.max(0, 20 - tick * 0.1) : 20;
311
+ const timeToValue = totalAgents > 0 ? blockedAgentCount / totalAgents * 100 : 0;
301
312
  const poolSizes = { ...state.poolSizes ?? {} };
302
313
  if (!this.anchorBaseline && tick === 1 && totalSupply > 0) {
303
314
  this.anchorBaseline = {
304
- goldPerHour: totalSupply / Math.max(1, totalAgents),
305
- itemsPerGold: priceIndex > 0 ? 1 / priceIndex : 0
315
+ currencyPerPeriod: totalSupply / Math.max(1, totalAgents),
316
+ itemsPerCurrency: priceIndex > 0 ? 1 / priceIndex : 0
306
317
  };
307
318
  }
308
319
  let anchorRatioDrift = 0;
309
320
  if (this.anchorBaseline && totalAgents > 0) {
310
- const currentGoldPerHour = totalSupply / totalAgents;
311
- anchorRatioDrift = this.anchorBaseline.goldPerHour > 0 ? (currentGoldPerHour - this.anchorBaseline.goldPerHour) / this.anchorBaseline.goldPerHour : 0;
321
+ const currentCurrencyPerPeriod = totalSupply / totalAgents;
322
+ anchorRatioDrift = this.anchorBaseline.currencyPerPeriod > 0 ? (currentCurrencyPerPeriod - this.anchorBaseline.currencyPerPeriod) / this.anchorBaseline.currencyPerPeriod : 0;
312
323
  }
313
324
  let arbitrageIndex = 0;
314
325
  const priceKeys = Object.keys(prices).filter((k) => prices[k] > 0);
@@ -533,7 +544,7 @@ var P1_ProductionMatchesConsumption = {
533
544
  id: "P1",
534
545
  name: "Production Must Match Consumption",
535
546
  category: "supply_chain",
536
- 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.",
547
+ description: "If producer rate < consumer rate, supply deficit kills the economy. Raw materials piling at production locations happened because this was out of balance.",
537
548
  check(metrics, _thresholds) {
538
549
  const { supplyByResource, demandSignals, populationByRole } = metrics;
539
550
  const violations = [];
@@ -544,19 +555,22 @@ var P1_ProductionMatchesConsumption = {
544
555
  violations.push(resource);
545
556
  }
546
557
  }
547
- const crafters = (populationByRole["Crafter"] ?? 0) + (populationByRole["Alchemist"] ?? 0);
548
- const consumers = populationByRole["Fighter"] ?? 0;
549
- const productionDeficit = consumers > 0 && crafters / consumers < 0.1;
550
- if (violations.length > 0 || productionDeficit) {
558
+ const roleEntries = Object.entries(populationByRole).sort((a, b) => b[1] - a[1]);
559
+ const totalPop = metrics.totalAgents;
560
+ const dominantRole = roleEntries[0];
561
+ const dominantCount = dominantRole?.[1] ?? 0;
562
+ const dominantShare = totalPop > 0 ? dominantCount / totalPop : 0;
563
+ const populationImbalance = dominantShare > 0.4 && violations.length > 0;
564
+ if (violations.length > 0 || populationImbalance) {
551
565
  return {
552
566
  violated: true,
553
567
  severity: 7,
554
- evidence: { scarceResources: violations, crafters, consumers },
568
+ evidence: { scarceResources: violations, dominantRole: dominantRole?.[0], dominantShare },
555
569
  suggestedAction: {
556
- parameter: "craftingCost",
570
+ parameter: "productionCost",
557
571
  direction: "decrease",
558
572
  magnitude: 0.15,
559
- reasoning: "Lower crafting cost to incentivise more production."
573
+ reasoning: "Lower production cost to incentivise more production."
560
574
  },
561
575
  confidence: violations.length > 0 ? 0.85 : 0.6,
562
576
  estimatedLag: 10
@@ -569,26 +583,28 @@ var P2_ClosedLoopsNeedDirectHandoff = {
569
583
  id: "P2",
570
584
  name: "Closed Loops Need Direct Handoff",
571
585
  category: "supply_chain",
572
- 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.",
586
+ 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.",
573
587
  check(metrics, _thresholds) {
574
- const { supplyByResource, prices, velocity } = metrics;
575
- const ore = supplyByResource["ore"] ?? 0;
576
- const wood = supplyByResource["wood"] ?? 0;
577
- const orePrice = prices["ore"] ?? 0;
578
- const woodPrice = prices["wood"] ?? 0;
579
- const oreBacklog = ore > 50 && orePrice > 0;
580
- const woodBacklog = wood > 50 && woodPrice > 0;
588
+ const { supplyByResource, prices, velocity, totalAgents } = metrics;
589
+ const avgSupplyPerAgent = totalAgents > 0 ? Object.values(supplyByResource).reduce((s, v) => s + v, 0) / totalAgents : 0;
590
+ const backlogResources = [];
591
+ for (const [resource, supply] of Object.entries(supplyByResource)) {
592
+ const price = prices[resource] ?? 0;
593
+ if (supply > avgSupplyPerAgent * 0.5 && price > 0) {
594
+ backlogResources.push(resource);
595
+ }
596
+ }
581
597
  const stagnant = velocity < 3;
582
- if ((oreBacklog || woodBacklog) && stagnant) {
598
+ if (backlogResources.length > 0 && stagnant) {
583
599
  return {
584
600
  violated: true,
585
601
  severity: 5,
586
- evidence: { ore, wood, velocity },
602
+ evidence: { backlogResources, velocity },
587
603
  suggestedAction: {
588
- parameter: "auctionFee",
604
+ parameter: "transactionFee",
589
605
  direction: "increase",
590
606
  magnitude: 0.2,
591
- reasoning: "Raise AH fees to discourage raw material listings. Direct hand-off at production zones is the correct channel."
607
+ reasoning: "Raise market fees to discourage raw material listings. Direct hand-off at production zones is the correct channel."
592
608
  },
593
609
  confidence: 0.7,
594
610
  estimatedLag: 5
@@ -601,29 +617,31 @@ var P3_BootstrapCapitalCoversFirstTransaction = {
601
617
  id: "P3",
602
618
  name: "Bootstrap Capital Covers First Transaction",
603
619
  category: "supply_chain",
604
- 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.",
620
+ 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.",
605
621
  check(metrics, _thresholds) {
606
- const { populationByRole, supplyByResource, prices } = metrics;
607
- const crafters = populationByRole["Crafter"] ?? 0;
608
- const alchemists = populationByRole["Alchemist"] ?? 0;
609
- const weapons = supplyByResource["weapons"] ?? 0;
610
- const potions = supplyByResource["potions"] ?? 0;
611
- const crafterBootstrapFail = crafters > 0 && weapons === 0 && (prices["ore"] ?? 0) > 0;
612
- const alchemistBootstrapFail = alchemists > 0 && potions === 0 && (prices["wood"] ?? 0) > 0;
613
- if (crafterBootstrapFail || alchemistBootstrapFail) {
614
- return {
615
- violated: true,
616
- severity: 8,
617
- evidence: { crafters, alchemists, weapons, potions },
618
- suggestedAction: {
619
- parameter: "craftingCost",
620
- direction: "decrease",
621
- magnitude: 0.3,
622
- reasoning: "Producers cannot complete first transaction. Lower production cost to unblock bootstrap."
623
- },
624
- confidence: 0.8,
625
- estimatedLag: 3
626
- };
622
+ const { populationByRole, supplyByResource, prices, totalAgents } = metrics;
623
+ const totalProducers = Object.values(populationByRole).reduce((s, v) => s + v, 0);
624
+ if (totalProducers > 0) {
625
+ for (const [resource, supply] of Object.entries(supplyByResource)) {
626
+ if (supply === 0) {
627
+ const anyInputPriced = Object.values(prices).some((p) => p > 0);
628
+ if (anyInputPriced) {
629
+ return {
630
+ violated: true,
631
+ severity: 8,
632
+ evidence: { resource, totalProducers, supply },
633
+ suggestedAction: {
634
+ parameter: "productionCost",
635
+ direction: "decrease",
636
+ magnitude: 0.3,
637
+ reasoning: "Producers cannot complete first transaction. Lower production cost to unblock bootstrap."
638
+ },
639
+ confidence: 0.8,
640
+ estimatedLag: 3
641
+ };
642
+ }
643
+ }
644
+ }
627
645
  }
628
646
  return { violated: false };
629
647
  }
@@ -632,41 +650,38 @@ var P4_MaterialsFlowFasterThanCooldown = {
632
650
  id: "P4",
633
651
  name: "Materials Flow Faster Than Cooldown",
634
652
  category: "supply_chain",
635
- 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.",
653
+ 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.",
636
654
  check(metrics, _thresholds) {
637
- const { supplyByResource, populationByRole, velocity } = metrics;
638
- const gatherers = populationByRole["Gatherer"] ?? 0;
639
- const crafters = populationByRole["Crafter"] ?? 0;
640
- const alchemists = populationByRole["Alchemist"] ?? 0;
641
- const producers = crafters + alchemists;
642
- const gathererToProcuderRatio = gatherers / Math.max(1, producers);
643
- if (producers > 0 && gathererToProcuderRatio < 0.5 && velocity < 5) {
655
+ const { supplyByResource, populationByRole, velocity, totalAgents } = metrics;
656
+ const totalSupply = Object.values(supplyByResource).reduce((s, v) => s + v, 0);
657
+ const avgSupplyPerAgent = totalAgents > 0 ? totalSupply / totalAgents : 0;
658
+ const roleEntries = Object.entries(populationByRole);
659
+ const totalRoles = roleEntries.length;
660
+ if (totalRoles >= 2 && velocity < 5 && avgSupplyPerAgent < 0.5) {
644
661
  return {
645
662
  violated: true,
646
663
  severity: 5,
647
- evidence: { gatherers, crafters, alchemists, gathererToProcuderRatio },
664
+ evidence: { avgSupplyPerAgent, velocity, totalRoles },
648
665
  suggestedAction: {
649
- parameter: "miningYield",
666
+ parameter: "yieldRate",
650
667
  direction: "increase",
651
668
  magnitude: 0.15,
652
- reasoning: "Too few gatherers relative to producers. Increase yield to compensate."
669
+ reasoning: "Low supply per agent with stagnant velocity. Increase yield to compensate."
653
670
  },
654
671
  confidence: 0.65,
655
672
  estimatedLag: 8
656
673
  };
657
674
  }
658
- const ore = supplyByResource["ore"] ?? 0;
659
- const wood = supplyByResource["wood"] ?? 0;
660
- if (ore > 80 || wood > 80) {
675
+ if (avgSupplyPerAgent > 2) {
661
676
  return {
662
677
  violated: true,
663
678
  severity: 4,
664
- evidence: { ore, wood, gatherers, producers },
679
+ evidence: { avgSupplyPerAgent, totalSupply, totalAgents },
665
680
  suggestedAction: {
666
- parameter: "miningYield",
681
+ parameter: "yieldRate",
667
682
  direction: "decrease",
668
683
  magnitude: 0.2,
669
- reasoning: "Raw materials piling up. Gatherers outpacing producers."
684
+ reasoning: "Raw materials piling up. Extractors outpacing producers."
670
685
  },
671
686
  confidence: 0.8,
672
687
  estimatedLag: 5
@@ -691,7 +706,7 @@ var P60_SurplusDisposalAsymmetry = {
691
706
  discount: thresholds.disposalTradeWeightDiscount
692
707
  },
693
708
  suggestedAction: {
694
- parameter: "craftingCost",
709
+ parameter: "productionCost",
695
710
  direction: "decrease",
696
711
  magnitude: 0.1,
697
712
  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.`
@@ -716,7 +731,7 @@ var P5_ProfitabilityIsCompetitive = {
716
731
  id: "P5",
717
732
  name: "Profitability Is Competitive, Not Absolute",
718
733
  category: "incentive",
719
- 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.",
734
+ 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.",
720
735
  check(metrics, thresholds) {
721
736
  const { roleShares, populationByRole } = metrics;
722
737
  const highShareRoles = [];
@@ -734,7 +749,7 @@ var P5_ProfitabilityIsCompetitive = {
734
749
  population: populationByRole[dominantRole]
735
750
  },
736
751
  suggestedAction: {
737
- parameter: "auctionFee",
752
+ parameter: "transactionFee",
738
753
  direction: "increase",
739
754
  magnitude: thresholds.maxAdjustmentPercent,
740
755
  reasoning: `${dominantRole} share at ${((roleShares[dominantRole] ?? 0) * 100).toFixed(0)}%. Likely stampede from non-competitive profitability formula. Raise market friction to slow role accumulation.`
@@ -760,7 +775,7 @@ var P6_CrowdingMultiplierOnAllRoles = {
760
775
  severity: 5,
761
776
  evidence: { role, share },
762
777
  suggestedAction: {
763
- parameter: "craftingCost",
778
+ parameter: "productionCost",
764
779
  direction: "increase",
765
780
  magnitude: 0.1,
766
781
  reasoning: `${role} at ${(share * 100).toFixed(0)}% \u2014 no crowding pressure detected. Apply role-specific cost increase to simulate saturation.`
@@ -777,28 +792,33 @@ var P7_NonSpecialistsSubsidiseSpecialists = {
777
792
  id: "P7",
778
793
  name: "Non-Specialists Subsidise Specialists in Zero-Sum Games",
779
794
  category: "incentive",
780
- 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.",
795
+ 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.",
781
796
  check(metrics, _thresholds) {
782
- const { populationByRole, poolSizes } = metrics;
783
- const arenaPot = poolSizes["arena"] ?? poolSizes["arenaPot"] ?? 0;
784
- if (arenaPot <= 0) return { violated: false };
785
- const fighters = populationByRole["Fighter"] ?? 0;
786
- const total = metrics.totalAgents;
787
- const fighterShare = fighters / Math.max(1, total);
788
- if (fighterShare > 0.7 && arenaPot < 100) {
789
- return {
790
- violated: true,
791
- severity: 6,
792
- evidence: { fighterShare, arenaPot },
793
- suggestedAction: {
794
- parameter: "arenaEntryFee",
795
- direction: "decrease",
796
- magnitude: 0.1,
797
- reasoning: "Arena pot draining \u2014 too many specialists, not enough subsidising non-specialists. Lower entry fee to attract diverse participants."
798
- },
799
- confidence: 0.75,
800
- estimatedLag: 5
801
- };
797
+ const { poolSizes } = metrics;
798
+ for (const [poolName, poolSize] of Object.entries(poolSizes)) {
799
+ if (poolSize <= 0) continue;
800
+ const roleEntries = Object.entries(metrics.populationByRole);
801
+ if (roleEntries.length === 0) continue;
802
+ const [dominantRole, dominantPop] = roleEntries.reduce(
803
+ (max, entry) => entry[1] > max[1] ? entry : max
804
+ );
805
+ const total = metrics.totalAgents;
806
+ const dominantShare = dominantPop / Math.max(1, total);
807
+ if (dominantShare > 0.7 && poolSize < 100) {
808
+ return {
809
+ violated: true,
810
+ severity: 6,
811
+ evidence: { poolName, poolSize, dominantRole, dominantShare },
812
+ suggestedAction: {
813
+ parameter: "entryFee",
814
+ direction: "decrease",
815
+ magnitude: 0.1,
816
+ 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.`
817
+ },
818
+ confidence: 0.75,
819
+ estimatedLag: 5
820
+ };
821
+ }
802
822
  }
803
823
  return { violated: false };
804
824
  }
@@ -807,7 +827,7 @@ var P8_RegulatorCannotFightDesign = {
807
827
  id: "P8",
808
828
  name: "Regulator Cannot Fight the Design",
809
829
  category: "incentive",
810
- 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.",
830
+ 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.",
811
831
  check(metrics, _thresholds) {
812
832
  const { roleShares, avgSatisfaction } = metrics;
813
833
  if (avgSatisfaction < 45) {
@@ -818,7 +838,7 @@ var P8_RegulatorCannotFightDesign = {
818
838
  severity: 4,
819
839
  evidence: { dominantRole: dominantRole[0], share: dominantRole[1], avgSatisfaction },
820
840
  suggestedAction: {
821
- parameter: "arenaReward",
841
+ parameter: "rewardRate",
822
842
  direction: "increase",
823
843
  magnitude: 0.1,
824
844
  reasoning: `Low satisfaction with ${dominantRole[0]} dominant. Regulator may be suppressing a structurally necessary role. Ease pressure on dominant role rewards.`
@@ -853,7 +873,7 @@ var P9_RoleSwitchingNeedsFriction = {
853
873
  severity: 5,
854
874
  evidence: { totalChurnRate: totalChurn, churnByRole },
855
875
  suggestedAction: {
856
- parameter: "craftingCost",
876
+ parameter: "productionCost",
857
877
  direction: "increase",
858
878
  magnitude: 0.05,
859
879
  reasoning: `Role switch rate ${(totalChurn * 100).toFixed(1)}% exceeds friction threshold. Increase production costs to slow herd movement.`
@@ -870,7 +890,7 @@ var P10_SpawnWeightingUsesInversePopulation = {
870
890
  id: "P10",
871
891
  name: "Spawn Weighting Uses Inverse Population",
872
892
  category: "population",
873
- description: "New agents should preferentially fill the least-populated roles. Flat spawn probability causes initial imbalances to compound.",
893
+ description: "New agents should preferentially fill the least-populated roles. Flat entry probability causes initial imbalances to compound.",
874
894
  check(metrics, _thresholds) {
875
895
  const { roleShares } = metrics;
876
896
  if (Object.keys(roleShares).length === 0) return { violated: false };
@@ -885,10 +905,10 @@ var P10_SpawnWeightingUsesInversePopulation = {
885
905
  severity: 4,
886
906
  evidence: { roleShares, stdDev, leastPopulatedRole: minRole?.[0] },
887
907
  suggestedAction: {
888
- parameter: "miningYield",
908
+ parameter: "yieldRate",
889
909
  direction: "increase",
890
910
  magnitude: 0.05,
891
- 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.`
911
+ 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.`
892
912
  },
893
913
  confidence: 0.6,
894
914
  estimatedLag: 20
@@ -911,7 +931,7 @@ var P11_TwoTierPressure = {
911
931
  severity: 6,
912
932
  evidence: { role, share },
913
933
  suggestedAction: {
914
- parameter: "auctionFee",
934
+ parameter: "transactionFee",
915
935
  direction: "increase",
916
936
  magnitude: 0.15,
917
937
  reasoning: `${role} at ${(share * 100).toFixed(0)}% \u2014 continuous pressure was insufficient. Hard intervention needed alongside resumed continuous pressure.`
@@ -939,7 +959,7 @@ var P46_PersonaDiversity = {
939
959
  severity: 5,
940
960
  evidence: { dominantPersona: persona, share, personaDistribution },
941
961
  suggestedAction: {
942
- parameter: "arenaReward",
962
+ parameter: "rewardRate",
943
963
  direction: "increase",
944
964
  magnitude: 0.1,
945
965
  reasoning: `${persona} persona at ${(share * 100).toFixed(0)}% \u2014 behavioral monoculture. Diversify reward structures to attract other persona types.`
@@ -956,7 +976,7 @@ var P46_PersonaDiversity = {
956
976
  severity: 3,
957
977
  evidence: { significantClusters, required: thresholds.personaMinClusters },
958
978
  suggestedAction: {
959
- parameter: "auctionFee",
979
+ parameter: "transactionFee",
960
980
  direction: "decrease",
961
981
  magnitude: 0.05,
962
982
  reasoning: `Only ${significantClusters} significant persona clusters (need ${thresholds.personaMinClusters}). Lower trade barriers to attract non-dominant persona types.`
@@ -980,7 +1000,7 @@ var P12_OnePrimaryFaucet = {
980
1000
  id: "P12",
981
1001
  name: "One Primary Faucet",
982
1002
  category: "currency",
983
- description: "Multiple independent currency sources (gathering + crafting + quests) each creating gold causes uncontrolled inflation. One clear primary faucet makes the economy predictable and auditable.",
1003
+ description: "Multiple independent currency sources (gathering + production + quests) each creating currency causes uncontrolled inflation. One clear primary faucet makes the economy predictable and auditable.",
984
1004
  check(metrics, thresholds) {
985
1005
  const { netFlow, faucetVolume, sinkVolume } = metrics;
986
1006
  if (netFlow > thresholds.netFlowWarnThreshold) {
@@ -989,10 +1009,10 @@ var P12_OnePrimaryFaucet = {
989
1009
  severity: 5,
990
1010
  evidence: { netFlow, faucetVolume, sinkVolume },
991
1011
  suggestedAction: {
992
- parameter: "craftingCost",
1012
+ parameter: "productionCost",
993
1013
  direction: "increase",
994
1014
  magnitude: 0.15,
995
- reasoning: `Net flow +${netFlow.toFixed(1)} g/t. Inflationary. Increase crafting cost (primary sink) to balance faucet output.`
1015
+ reasoning: `Net flow +${netFlow.toFixed(1)}/tick. Inflationary. Increase production cost (primary sink) to balance faucet output.`
996
1016
  },
997
1017
  confidence: 0.8,
998
1018
  estimatedLag: 8
@@ -1004,10 +1024,10 @@ var P12_OnePrimaryFaucet = {
1004
1024
  severity: 4,
1005
1025
  evidence: { netFlow, faucetVolume, sinkVolume },
1006
1026
  suggestedAction: {
1007
- parameter: "craftingCost",
1027
+ parameter: "productionCost",
1008
1028
  direction: "decrease",
1009
1029
  magnitude: 0.15,
1010
- reasoning: `Net flow ${netFlow.toFixed(1)} g/t. Deflationary. Decrease crafting cost to ease sink pressure.`
1030
+ reasoning: `Net flow ${netFlow.toFixed(1)}/tick. Deflationary. Decrease production cost to ease sink pressure.`
1011
1031
  },
1012
1032
  confidence: 0.8,
1013
1033
  estimatedLag: 8
@@ -1020,36 +1040,40 @@ var P13_PotsAreZeroSumAndSelfRegulate = {
1020
1040
  id: "P13",
1021
1041
  name: "Pots Self-Regulate with Correct Multiplier",
1022
1042
  category: "currency",
1023
- 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.",
1043
+ 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.",
1024
1044
  check(metrics, thresholds) {
1025
- const { poolSizes } = metrics;
1026
- const arenaPot = poolSizes["arena"] ?? poolSizes["arenaPot"] ?? 0;
1027
- const fighters = metrics.populationByRole["Fighter"] ?? 0;
1028
- if (fighters > 5 && arenaPot < 50) {
1029
- const { arenaWinRate, arenaHouseCut } = thresholds;
1030
- const maxSustainableMultiplier = (1 - arenaHouseCut) / arenaWinRate;
1031
- return {
1032
- violated: true,
1033
- severity: 7,
1034
- evidence: { arenaPot, fighters, maxSustainableMultiplier },
1035
- suggestedAction: {
1036
- parameter: "arenaReward",
1037
- direction: "decrease",
1038
- magnitude: 0.15,
1039
- reasoning: `Arena pot at ${arenaPot.toFixed(0)}g with ${fighters} fighters. Sustainable multiplier \u2264 ${maxSustainableMultiplier.toFixed(2)}. Reduce reward multiplier to prevent pot drain.`
1040
- },
1041
- confidence: 0.85,
1042
- estimatedLag: 3
1043
- };
1045
+ const { poolSizes, populationByRole } = metrics;
1046
+ const totalAgents = metrics.totalAgents;
1047
+ const roleEntries = Object.entries(populationByRole).sort((a, b) => b[1] - a[1]);
1048
+ const dominantRole = roleEntries[0]?.[0];
1049
+ const dominantCount = roleEntries[0]?.[1] ?? 0;
1050
+ for (const [poolName, poolSize] of Object.entries(poolSizes)) {
1051
+ if (dominantCount > 5 && poolSize < 50) {
1052
+ const { poolWinRate, poolHouseCut } = thresholds;
1053
+ const maxSustainableMultiplier = (1 - poolHouseCut) / poolWinRate;
1054
+ return {
1055
+ violated: true,
1056
+ severity: 7,
1057
+ evidence: { pool: poolName, poolSize, participants: dominantCount, maxSustainableMultiplier },
1058
+ suggestedAction: {
1059
+ parameter: "rewardRate",
1060
+ direction: "decrease",
1061
+ magnitude: 0.15,
1062
+ 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.`
1063
+ },
1064
+ confidence: 0.85,
1065
+ estimatedLag: 3
1066
+ };
1067
+ }
1044
1068
  }
1045
1069
  return { violated: false };
1046
1070
  }
1047
1071
  };
1048
1072
  var P14_TrackActualInjection = {
1049
1073
  id: "P14",
1050
- name: "Track Actual Gold Injection, Not Value Creation",
1074
+ name: "Track Actual Currency Injection, Not Value Creation",
1051
1075
  category: "currency",
1052
- 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.',
1076
+ description: 'Counting resource gathering as "currency injected" is misleading. Currency enters through faucet mechanisms (spawning, rewards). Fake metrics break every downstream decision.',
1053
1077
  check(metrics, _thresholds) {
1054
1078
  const { faucetVolume, netFlow, totalSupply } = metrics;
1055
1079
  const supplyGrowthRate = Math.abs(netFlow) / Math.max(1, totalSupply);
@@ -1059,10 +1083,10 @@ var P14_TrackActualInjection = {
1059
1083
  severity: 4,
1060
1084
  evidence: { faucetVolume, netFlow, supplyGrowthRate },
1061
1085
  suggestedAction: {
1062
- parameter: "miningYield",
1086
+ parameter: "yieldRate",
1063
1087
  direction: "decrease",
1064
1088
  magnitude: 0.1,
1065
- reasoning: `Supply growing at ${(supplyGrowthRate * 100).toFixed(1)}%/tick. Verify gold injection tracking. Resources should not create gold directly.`
1089
+ reasoning: `Supply growing at ${(supplyGrowthRate * 100).toFixed(1)}%/tick. Verify currency injection tracking. Resources should not create currency directly.`
1066
1090
  },
1067
1091
  confidence: 0.55,
1068
1092
  estimatedLag: 5
@@ -1075,12 +1099,11 @@ var P15_PoolsNeedCapAndDecay = {
1075
1099
  id: "P15",
1076
1100
  name: "Pools Need Cap + Decay",
1077
1101
  category: "currency",
1078
- 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.",
1102
+ 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.",
1079
1103
  check(metrics, thresholds) {
1080
1104
  const { poolSizes, totalSupply } = metrics;
1081
1105
  const { poolCapPercent } = thresholds;
1082
1106
  for (const [pool, size] of Object.entries(poolSizes)) {
1083
- if (pool === "arena" || pool === "arenaPot") continue;
1084
1107
  const shareOfSupply = size / Math.max(1, totalSupply);
1085
1108
  if (shareOfSupply > poolCapPercent * 2) {
1086
1109
  return {
@@ -1088,10 +1111,10 @@ var P15_PoolsNeedCapAndDecay = {
1088
1111
  severity: 6,
1089
1112
  evidence: { pool, size, shareOfSupply, cap: poolCapPercent },
1090
1113
  suggestedAction: {
1091
- parameter: "auctionFee",
1114
+ parameter: "transactionFee",
1092
1115
  direction: "decrease",
1093
1116
  magnitude: 0.1,
1094
- reasoning: `${pool} pool at ${(shareOfSupply * 100).toFixed(1)}% of supply (cap: ${(poolCapPercent * 100).toFixed(0)}%). Gold frozen. Lower fees to encourage circulation over accumulation.`
1117
+ reasoning: `${pool} pool at ${(shareOfSupply * 100).toFixed(1)}% of supply (cap: ${(poolCapPercent * 100).toFixed(0)}%). Currency frozen. Lower fees to encourage circulation over accumulation.`
1095
1118
  },
1096
1119
  confidence: 0.85,
1097
1120
  estimatedLag: 5
@@ -1108,22 +1131,23 @@ var P16_WithdrawalPenaltyScales = {
1108
1131
  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.",
1109
1132
  check(metrics, _thresholds) {
1110
1133
  const { poolSizes, totalSupply } = metrics;
1111
- const bankPool = poolSizes["bank"] ?? poolSizes["bankPool"] ?? 0;
1112
1134
  const stakedEstimate = totalSupply * 0.15;
1113
- if (bankPool < 10 && stakedEstimate > 100) {
1114
- return {
1115
- violated: true,
1116
- severity: 3,
1117
- evidence: { bankPool, estimatedStaked: stakedEstimate },
1118
- suggestedAction: {
1119
- parameter: "auctionFee",
1120
- direction: "increase",
1121
- magnitude: 0.05,
1122
- reasoning: "Bank pool depleted while significant gold should be staked. Early withdrawals may be draining yield pool. Ensure withdrawal penalty scales with lock duration."
1123
- },
1124
- confidence: 0.45,
1125
- estimatedLag: 10
1126
- };
1135
+ for (const [poolName, poolSize] of Object.entries(poolSizes)) {
1136
+ if (poolSize < 10 && stakedEstimate > 100) {
1137
+ return {
1138
+ violated: true,
1139
+ severity: 3,
1140
+ evidence: { pool: poolName, poolSize, estimatedStaked: stakedEstimate },
1141
+ suggestedAction: {
1142
+ parameter: "transactionFee",
1143
+ direction: "increase",
1144
+ magnitude: 0.05,
1145
+ reasoning: `${poolName} pool depleted while significant currency should be locked. Early withdrawals may be draining the pool. Ensure withdrawal penalty scales with lock duration.`
1146
+ },
1147
+ confidence: 0.45,
1148
+ estimatedLag: 10
1149
+ };
1150
+ }
1127
1151
  }
1128
1152
  return { violated: false };
1129
1153
  }
@@ -1142,7 +1166,7 @@ var P32_VelocityAboveSupply = {
1142
1166
  severity: 4,
1143
1167
  evidence: { velocity, totalSupply, totalResources },
1144
1168
  suggestedAction: {
1145
- parameter: "auctionFee",
1169
+ parameter: "transactionFee",
1146
1170
  direction: "decrease",
1147
1171
  magnitude: 0.2,
1148
1172
  reasoning: `Velocity ${velocity}/t with ${totalResources} resources in system. Economy stagnant despite available supply. Lower trading friction.`
@@ -1178,7 +1202,7 @@ var P58_NoNaturalNumeraire = {
1178
1202
  meanPrice: mean
1179
1203
  },
1180
1204
  suggestedAction: {
1181
- parameter: "craftingCost",
1205
+ parameter: "productionCost",
1182
1206
  direction: "increase",
1183
1207
  magnitude: 0.1,
1184
1208
  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.`
@@ -1205,7 +1229,7 @@ var P17_GracePeriodBeforeIntervention = {
1205
1229
  id: "P17",
1206
1230
  name: "Grace Period Before Intervention",
1207
1231
  category: "bootstrap",
1208
- 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.",
1232
+ 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.",
1209
1233
  check(metrics, _thresholds) {
1210
1234
  if (metrics.tick < 30 && metrics.avgSatisfaction < 40) {
1211
1235
  return {
@@ -1213,7 +1237,7 @@ var P17_GracePeriodBeforeIntervention = {
1213
1237
  severity: 7,
1214
1238
  evidence: { tick: metrics.tick, avgSatisfaction: metrics.avgSatisfaction },
1215
1239
  suggestedAction: {
1216
- parameter: "arenaEntryFee",
1240
+ parameter: "entryFee",
1217
1241
  direction: "decrease",
1218
1242
  magnitude: 0.2,
1219
1243
  reasoning: `Very low satisfaction at tick ${metrics.tick}. Intervention may have fired during grace period. Ease all costs to let economy bootstrap.`
@@ -1229,27 +1253,26 @@ var P18_FirstProducerNeedsStartingInventory = {
1229
1253
  id: "P18",
1230
1254
  name: "First Producer Needs Starting Inventory + Capital",
1231
1255
  category: "bootstrap",
1232
- 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.",
1256
+ 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.",
1233
1257
  check(metrics, _thresholds) {
1234
1258
  if (metrics.tick > 20) return { violated: false };
1235
- const weapons = metrics.supplyByResource["weapons"] ?? 0;
1236
- const potions = metrics.supplyByResource["potions"] ?? 0;
1237
- const crafters = metrics.populationByRole["Crafter"] ?? 0;
1238
- const alchemists = metrics.populationByRole["Alchemist"] ?? 0;
1239
- if (crafters > 0 && weapons === 0 || alchemists > 0 && potions === 0) {
1240
- return {
1241
- violated: true,
1242
- severity: 8,
1243
- evidence: { tick: metrics.tick, weapons, potions, crafters, alchemists },
1244
- suggestedAction: {
1245
- parameter: "craftingCost",
1246
- direction: "decrease",
1247
- magnitude: 0.5,
1248
- reasoning: "Bootstrap failure: producers have no products on tick 1-20. Drastically reduce production cost to allow immediate output."
1249
- },
1250
- confidence: 0.9,
1251
- estimatedLag: 2
1252
- };
1259
+ const hasAgents = metrics.totalAgents > 0;
1260
+ for (const [resource, supply] of Object.entries(metrics.supplyByResource)) {
1261
+ if (supply === 0 && hasAgents) {
1262
+ return {
1263
+ violated: true,
1264
+ severity: 8,
1265
+ evidence: { tick: metrics.tick, resource, supply, totalAgents: metrics.totalAgents },
1266
+ suggestedAction: {
1267
+ parameter: "productionCost",
1268
+ direction: "decrease",
1269
+ magnitude: 0.5,
1270
+ reasoning: `Bootstrap failure: ${resource} supply is 0 at tick ${metrics.tick} with ${metrics.totalAgents} agents. Drastically reduce production cost to allow immediate output.`
1271
+ },
1272
+ confidence: 0.9,
1273
+ estimatedLag: 2
1274
+ };
1275
+ }
1253
1276
  }
1254
1277
  return { violated: false };
1255
1278
  }
@@ -1258,22 +1281,32 @@ var P19_StartingSupplyExceedsDemand = {
1258
1281
  id: "P19",
1259
1282
  name: "Starting Supply Exceeds Initial Demand",
1260
1283
  category: "bootstrap",
1261
- 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.",
1284
+ 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.",
1262
1285
  check(metrics, _thresholds) {
1263
1286
  if (metrics.tick > 30) return { violated: false };
1264
- const fighters = metrics.populationByRole["Fighter"] ?? 0;
1265
- const weapons = metrics.supplyByResource["weapons"] ?? 0;
1266
- const potions = metrics.supplyByResource["potions"] ?? 0;
1267
- if (fighters > 5 && weapons < fighters * 0.5) {
1287
+ const roleEntries = Object.entries(metrics.populationByRole);
1288
+ if (roleEntries.length === 0) return { violated: false };
1289
+ const [mostPopulatedRole, population] = roleEntries.reduce(
1290
+ (max, entry) => entry[1] > max[1] ? entry : max
1291
+ );
1292
+ if (population < 5) return { violated: false };
1293
+ const totalResourceSupply = Object.values(metrics.supplyByResource).reduce((sum, s) => sum + s, 0);
1294
+ const resourcesPerAgent = totalResourceSupply / Math.max(1, population);
1295
+ if (resourcesPerAgent < 0.5) {
1268
1296
  return {
1269
1297
  violated: true,
1270
1298
  severity: 6,
1271
- evidence: { fighters, weapons, potions, weaponsPerFighter: weapons / Math.max(1, fighters) },
1299
+ evidence: {
1300
+ mostPopulatedRole,
1301
+ population,
1302
+ totalResourceSupply,
1303
+ resourcesPerAgent
1304
+ },
1272
1305
  suggestedAction: {
1273
- parameter: "arenaReward",
1306
+ parameter: "rewardRate",
1274
1307
  direction: "increase",
1275
1308
  magnitude: 0.2,
1276
- reasoning: `${fighters} fighters but only ${weapons} weapons. Cold-start scarcity. Boost arena reward to attract fighters even without weapons.`
1309
+ reasoning: `${mostPopulatedRole} (${population} agents) has insufficient resources (${resourcesPerAgent.toFixed(2)} per agent). Cold-start scarcity. Boost competitive pool reward to attract participation despite scarcity.`
1277
1310
  },
1278
1311
  confidence: 0.75,
1279
1312
  estimatedLag: 5
@@ -1293,7 +1326,7 @@ var P20_DecayPreventsAccumulation = {
1293
1326
  id: "P20",
1294
1327
  name: "Decay Prevents Accumulation",
1295
1328
  category: "feedback",
1296
- 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.",
1329
+ 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.",
1297
1330
  check(metrics, _thresholds) {
1298
1331
  const { supplyByResource, velocity, totalAgents } = metrics;
1299
1332
  const totalResources = Object.values(supplyByResource).reduce((s, v) => s + v, 0);
@@ -1304,7 +1337,7 @@ var P20_DecayPreventsAccumulation = {
1304
1337
  severity: 4,
1305
1338
  evidence: { totalResources, resourcesPerAgent, velocity },
1306
1339
  suggestedAction: {
1307
- parameter: "miningYield",
1340
+ parameter: "yieldRate",
1308
1341
  direction: "decrease",
1309
1342
  magnitude: 0.1,
1310
1343
  reasoning: `${totalResources.toFixed(0)} resources with velocity ${velocity}/t. Likely hoarding. Reduce yield to increase scarcity and force circulation.`
@@ -1320,7 +1353,7 @@ var P21_PriceFromGlobalSupply = {
1320
1353
  id: "P21",
1321
1354
  name: "Price Reflects Global Supply, Not Just AH Listings",
1322
1355
  category: "feedback",
1323
- description: "If prices only update from Auction House activity, agents with hoarded inventory see artificially high prices and keep gathering when they should stop.",
1356
+ description: "If prices only update from market activity, agents with hoarded inventory see artificially high prices and keep gathering when they should stop.",
1324
1357
  check(metrics, _thresholds) {
1325
1358
  const { priceVolatility, supplyByResource, prices } = metrics;
1326
1359
  for (const resource of Object.keys(prices)) {
@@ -1332,7 +1365,7 @@ var P21_PriceFromGlobalSupply = {
1332
1365
  severity: 3,
1333
1366
  evidence: { resource, volatility, supply, price: prices[resource] },
1334
1367
  suggestedAction: {
1335
- parameter: "auctionFee",
1368
+ parameter: "transactionFee",
1336
1369
  direction: "increase",
1337
1370
  magnitude: 0.05,
1338
1371
  reasoning: `${resource} price volatile (${(volatility * 100).toFixed(0)}%) despite supply ${supply}. Price may not reflect global inventory. Increase trading friction to stabilise.`
@@ -1352,23 +1385,36 @@ var P22_MarketAwarenessPreventsSurplus = {
1352
1385
  description: "Producers who craft without checking market prices will create surpluses that crash prices. Agents need to see prices before deciding to produce.",
1353
1386
  check(metrics, _thresholds) {
1354
1387
  const { supplyByResource, prices, productionIndex } = metrics;
1355
- const weapons = supplyByResource["weapons"] ?? 0;
1356
- const weaponPrice = prices["weapons"] ?? 0;
1357
- const healthyWeaponPrice = 30;
1358
- if (weapons > 100 && weaponPrice < healthyWeaponPrice * 0.5 && productionIndex > 0) {
1359
- return {
1360
- violated: true,
1361
- severity: 4,
1362
- evidence: { weapons, weaponPrice, productionIndex },
1363
- suggestedAction: {
1364
- parameter: "craftingCost",
1365
- direction: "increase",
1366
- magnitude: 0.1,
1367
- reasoning: `${weapons} weapons with price ${weaponPrice.toFixed(0)}g but still producing. Producers appear unaware of market. Raise production cost to slow output.`
1368
- },
1369
- confidence: 0.7,
1370
- estimatedLag: 8
1371
- };
1388
+ const priceValues = Object.values(prices).filter((p) => p > 0);
1389
+ if (priceValues.length === 0) return { violated: false };
1390
+ const sortedPrices = [...priceValues].sort((a, b) => a - b);
1391
+ const medianPrice = sortedPrices[Math.floor(sortedPrices.length / 2)] ?? 0;
1392
+ for (const [resource, price] of Object.entries(prices)) {
1393
+ if (price <= 0) continue;
1394
+ const supply = supplyByResource[resource] ?? 0;
1395
+ const priceDeviation = price / Math.max(1, medianPrice);
1396
+ if (priceDeviation < 0.33 && supply > 100 && productionIndex > 0) {
1397
+ return {
1398
+ violated: true,
1399
+ severity: 4,
1400
+ evidence: {
1401
+ resource,
1402
+ price,
1403
+ medianPrice,
1404
+ priceDeviation,
1405
+ supply,
1406
+ productionIndex
1407
+ },
1408
+ suggestedAction: {
1409
+ parameter: "productionCost",
1410
+ direction: "increase",
1411
+ magnitude: 0.1,
1412
+ 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.`
1413
+ },
1414
+ confidence: 0.7,
1415
+ estimatedLag: 8
1416
+ };
1417
+ }
1372
1418
  }
1373
1419
  return { violated: false };
1374
1420
  }
@@ -1377,7 +1423,7 @@ var P23_ProfitabilityFactorsFeasibility = {
1377
1423
  id: "P23",
1378
1424
  name: "Profitability Factors Execution Feasibility",
1379
1425
  category: "feedback",
1380
- 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.",
1426
+ 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.",
1381
1427
  check(metrics, _thresholds) {
1382
1428
  const { avgSatisfaction, blockedAgentCount, totalAgents } = metrics;
1383
1429
  const blockedFraction = blockedAgentCount / Math.max(1, totalAgents);
@@ -1387,7 +1433,7 @@ var P23_ProfitabilityFactorsFeasibility = {
1387
1433
  severity: 5,
1388
1434
  evidence: { blockedFraction, blockedAgentCount, avgSatisfaction },
1389
1435
  suggestedAction: {
1390
- parameter: "craftingCost",
1436
+ parameter: "productionCost",
1391
1437
  direction: "decrease",
1392
1438
  magnitude: 0.15,
1393
1439
  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.`
@@ -1413,7 +1459,7 @@ var P24_BlockedAgentsDecayFaster = {
1413
1459
  severity: 5,
1414
1460
  evidence: { blockedFraction, blockedAgentCount, churnRate },
1415
1461
  suggestedAction: {
1416
- parameter: "auctionFee",
1462
+ parameter: "transactionFee",
1417
1463
  direction: "decrease",
1418
1464
  magnitude: 0.15,
1419
1465
  reasoning: `${(blockedFraction * 100).toFixed(0)}% of agents blocked. Blocked agents churn silently, skewing metrics. Lower fees to unblock market participation.`
@@ -1438,26 +1484,35 @@ var P25_CorrectLeversForCorrectProblems = {
1438
1484
  id: "P25",
1439
1485
  name: "Target the Correct Lever",
1440
1486
  category: "regulator",
1441
- 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.",
1487
+ 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.",
1442
1488
  check(metrics, thresholds) {
1443
1489
  const { netFlow, supplyByResource } = metrics;
1444
- const ore = supplyByResource["ore"] ?? 0;
1445
- const wood = supplyByResource["wood"] ?? 0;
1446
- const resourceFlood = ore + wood > 100;
1447
- if (netFlow > thresholds.netFlowWarnThreshold && resourceFlood) {
1448
- return {
1449
- violated: true,
1450
- severity: 4,
1451
- evidence: { netFlow, ore, wood },
1452
- suggestedAction: {
1453
- parameter: "miningYield",
1454
- direction: "decrease",
1455
- magnitude: 0.15,
1456
- reasoning: `Inflation with raw material backlog (ore ${ore}, wood ${wood}). Root cause is gathering. Correct lever: miningYield, not fees.`
1457
- },
1458
- confidence: 0.75,
1459
- estimatedLag: 8
1460
- };
1490
+ const resourceEntries = Object.entries(supplyByResource);
1491
+ if (resourceEntries.length === 0) return { violated: false };
1492
+ const totalSupply = resourceEntries.reduce((sum, [_, s]) => sum + s, 0);
1493
+ const avgSupply = totalSupply / resourceEntries.length;
1494
+ for (const [resource, supply] of resourceEntries) {
1495
+ if (supply > avgSupply * 3 && netFlow > thresholds.netFlowWarnThreshold) {
1496
+ return {
1497
+ violated: true,
1498
+ severity: 4,
1499
+ evidence: {
1500
+ resource,
1501
+ supply,
1502
+ avgSupply,
1503
+ ratio: supply / Math.max(1, avgSupply),
1504
+ netFlow
1505
+ },
1506
+ suggestedAction: {
1507
+ parameter: "yieldRate",
1508
+ direction: "decrease",
1509
+ magnitude: 0.15,
1510
+ 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.`
1511
+ },
1512
+ confidence: 0.75,
1513
+ estimatedLag: 8
1514
+ };
1515
+ }
1461
1516
  }
1462
1517
  return { violated: false };
1463
1518
  }
@@ -1475,7 +1530,7 @@ var P26_ContinuousPressureBeatsThresholdCuts = {
1475
1530
  severity: 4,
1476
1531
  evidence: { inflationRate },
1477
1532
  suggestedAction: {
1478
- parameter: "craftingCost",
1533
+ parameter: "productionCost",
1479
1534
  direction: inflationRate > 0 ? "increase" : "decrease",
1480
1535
  magnitude: Math.min(thresholds.maxAdjustmentPercent, 0.05),
1481
1536
  // force smaller step
@@ -1501,7 +1556,7 @@ var P27_AdjustmentsNeedCooldowns = {
1501
1556
  severity: 4,
1502
1557
  evidence: { churnRate, avgSatisfaction },
1503
1558
  suggestedAction: {
1504
- parameter: "arenaEntryFee",
1559
+ parameter: "entryFee",
1505
1560
  direction: "decrease",
1506
1561
  magnitude: 0.05,
1507
1562
  reasoning: `High churn (${(churnRate * 100).toFixed(1)}%) with low satisfaction. Possible oscillation from rapid adjustments. Apply small correction only.`
@@ -1517,7 +1572,7 @@ var P28_StructuralDominanceIsNotPathological = {
1517
1572
  id: "P28",
1518
1573
  name: "Structural Dominance \u2260 Pathological Monopoly",
1519
1574
  category: "regulator",
1520
- 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".',
1575
+ 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".',
1521
1576
  check(metrics, _thresholds) {
1522
1577
  const { roleShares, avgSatisfaction } = metrics;
1523
1578
  const dominant = Object.entries(roleShares).sort((a, b) => b[1] - a[1])[0];
@@ -1532,7 +1587,7 @@ var P28_StructuralDominanceIsNotPathological = {
1532
1587
  severity: 5,
1533
1588
  evidence: { dominantRole, dominantShare, avgSatisfaction },
1534
1589
  suggestedAction: {
1535
- parameter: "craftingCost",
1590
+ parameter: "productionCost",
1536
1591
  direction: "decrease",
1537
1592
  magnitude: 0.1,
1538
1593
  reasoning: `${dominantRole} dominant (${(dominantShare * 100).toFixed(0)}%) with low satisfaction. Pathological dominance \u2014 agents trapped, not thriving. Ease costs to allow role switching.`
@@ -1557,7 +1612,7 @@ var P38_CommunicationPreventsRevolt = {
1557
1612
  severity: 3,
1558
1613
  evidence: { churnRate },
1559
1614
  suggestedAction: {
1560
- parameter: "arenaReward",
1615
+ parameter: "rewardRate",
1561
1616
  direction: "increase",
1562
1617
  magnitude: 0.1,
1563
1618
  reasoning: `High churn (${(churnRate * 100).toFixed(1)}%) \u2014 agents leaving. Ensure all recent adjustments are logged with reasoning to diagnose cause.`
@@ -1582,7 +1637,7 @@ var P29_PinchPoint = {
1582
1637
  id: "P29",
1583
1638
  name: "Pinch Point",
1584
1639
  category: "market_dynamics",
1585
- 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.",
1640
+ 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.",
1586
1641
  check(metrics, _thresholds) {
1587
1642
  const { pinchPoints, supplyByResource, demandSignals } = metrics;
1588
1643
  for (const [resource, status] of Object.entries(pinchPoints)) {
@@ -1594,7 +1649,7 @@ var P29_PinchPoint = {
1594
1649
  severity: 7,
1595
1650
  evidence: { resource, supply, demand, status },
1596
1651
  suggestedAction: {
1597
- parameter: "craftingCost",
1652
+ parameter: "productionCost",
1598
1653
  direction: "decrease",
1599
1654
  magnitude: 0.15,
1600
1655
  reasoning: `${resource} is a pinch point and currently SCARCE (supply ${supply}, demand ${demand}). Reduce production cost to increase throughput.`
@@ -1610,7 +1665,7 @@ var P29_PinchPoint = {
1610
1665
  severity: 4,
1611
1666
  evidence: { resource, supply, status },
1612
1667
  suggestedAction: {
1613
- parameter: "craftingCost",
1668
+ parameter: "productionCost",
1614
1669
  direction: "increase",
1615
1670
  magnitude: 0.1,
1616
1671
  reasoning: `${resource} is a pinch point and OVERSUPPLIED (supply ${supply}). Raise production cost to reduce surplus.`
@@ -1627,7 +1682,7 @@ var P30_MovingPinchPoint = {
1627
1682
  id: "P30",
1628
1683
  name: "Moving Pinch Point",
1629
1684
  category: "market_dynamics",
1630
- 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.",
1685
+ 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.",
1631
1686
  check(metrics, _thresholds) {
1632
1687
  const { capacityUsage, supplyByResource, avgSatisfaction } = metrics;
1633
1688
  const totalResources = Object.values(supplyByResource).reduce((s, v) => s + v, 0);
@@ -1638,7 +1693,7 @@ var P30_MovingPinchPoint = {
1638
1693
  severity: 3,
1639
1694
  evidence: { capacityUsage, resourcesPerAgent, avgSatisfaction },
1640
1695
  suggestedAction: {
1641
- parameter: "craftingCost",
1696
+ parameter: "productionCost",
1642
1697
  direction: "increase",
1643
1698
  magnitude: 0.1,
1644
1699
  reasoning: "Economy operating at full capacity with abundant resources and high satisfaction. Pinch point may have been cleared. Increase production cost to restore scarcity."
@@ -1684,7 +1739,7 @@ var P57_CombinatorialPriceSpace = {
1684
1739
  target: thresholds.relativePriceConvergenceTarget
1685
1740
  },
1686
1741
  suggestedAction: {
1687
- parameter: "auctionFee",
1742
+ parameter: "transactionFee",
1688
1743
  direction: "decrease",
1689
1744
  magnitude: 0.1,
1690
1745
  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.`
@@ -1707,7 +1762,7 @@ var P31_AnchorValueTracking = {
1707
1762
  id: "P31",
1708
1763
  name: "Anchor Value Tracking",
1709
1764
  category: "measurement",
1710
- 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.",
1765
+ 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.",
1711
1766
  check(metrics, _thresholds) {
1712
1767
  const { anchorRatioDrift, inflationRate } = metrics;
1713
1768
  if (Math.abs(anchorRatioDrift) > 0.25) {
@@ -1716,10 +1771,10 @@ var P31_AnchorValueTracking = {
1716
1771
  severity: 5,
1717
1772
  evidence: { anchorRatioDrift, inflationRate },
1718
1773
  suggestedAction: {
1719
- parameter: "craftingCost",
1774
+ parameter: "productionCost",
1720
1775
  direction: anchorRatioDrift > 0 ? "increase" : "decrease",
1721
1776
  magnitude: 0.1,
1722
- reasoning: `Anchor ratio has drifted ${(anchorRatioDrift * 100).toFixed(0)}% from baseline. Time-to-value for players is changing. Adjust production costs to restore.`
1777
+ reasoning: `Anchor ratio has drifted ${(anchorRatioDrift * 100).toFixed(0)}% from baseline. Time-to-value for participants is changing. Adjust production costs to restore.`
1723
1778
  },
1724
1779
  confidence: 0.65,
1725
1780
  estimatedLag: 10
@@ -1741,7 +1796,7 @@ var P41_MultiResolutionMonitoring = {
1741
1796
  severity: 4,
1742
1797
  evidence: { giniCoefficient, avgSatisfaction },
1743
1798
  suggestedAction: {
1744
- parameter: "auctionFee",
1799
+ parameter: "transactionFee",
1745
1800
  direction: "increase",
1746
1801
  magnitude: 0.1,
1747
1802
  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.`
@@ -1770,7 +1825,7 @@ var P55_ArbitrageThermometer = {
1770
1825
  critical: thresholds.arbitrageIndexCritical
1771
1826
  },
1772
1827
  suggestedAction: {
1773
- parameter: "auctionFee",
1828
+ parameter: "transactionFee",
1774
1829
  direction: "decrease",
1775
1830
  magnitude: 0.15,
1776
1831
  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.`
@@ -1788,7 +1843,7 @@ var P55_ArbitrageThermometer = {
1788
1843
  warning: thresholds.arbitrageIndexWarning
1789
1844
  },
1790
1845
  suggestedAction: {
1791
- parameter: "auctionFee",
1846
+ parameter: "transactionFee",
1792
1847
  direction: "decrease",
1793
1848
  magnitude: 0.08,
1794
1849
  reasoning: `Arbitrage index ${arbitrageIndex.toFixed(2)} above warning threshold (${thresholds.arbitrageIndexWarning}). Early sign of price divergence. Gently reduce friction to support self-correction.`
@@ -1816,7 +1871,7 @@ var P59_GiftEconomyNoise = {
1816
1871
  threshold: thresholds.giftTradeFilterRatio
1817
1872
  },
1818
1873
  suggestedAction: {
1819
- parameter: "auctionFee",
1874
+ parameter: "transactionFee",
1820
1875
  direction: "increase",
1821
1876
  magnitude: 0.05,
1822
1877
  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.`
@@ -1840,7 +1895,7 @@ var P42_TheMedianPrinciple = {
1840
1895
  id: "P42",
1841
1896
  name: "The Median Principle",
1842
1897
  category: "statistical",
1843
- 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%.",
1898
+ 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%.",
1844
1899
  check(metrics, thresholds) {
1845
1900
  const { meanMedianDivergence, giniCoefficient } = metrics;
1846
1901
  if (meanMedianDivergence > thresholds.meanMedianDivergenceMax) {
@@ -1854,10 +1909,10 @@ var P42_TheMedianPrinciple = {
1854
1909
  medianBalance: metrics.medianBalance
1855
1910
  },
1856
1911
  suggestedAction: {
1857
- parameter: "auctionFee",
1912
+ parameter: "transactionFee",
1858
1913
  direction: "increase",
1859
1914
  magnitude: 0.15,
1860
- 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.`
1915
+ 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.`
1861
1916
  },
1862
1917
  confidence: 0.85,
1863
1918
  estimatedLag: 15
@@ -1879,7 +1934,7 @@ var P43_SimulationMinimum = {
1879
1934
  severity: 3,
1880
1935
  evidence: { inflationRate, minIterations: thresholds.simulationMinIterations },
1881
1936
  suggestedAction: {
1882
- parameter: "craftingCost",
1937
+ parameter: "productionCost",
1883
1938
  direction: inflationRate > 0 ? "increase" : "decrease",
1884
1939
  magnitude: 0.05,
1885
1940
  reasoning: `Large inflation rate swing (${(inflationRate * 100).toFixed(0)}%). Ensure all decisions use \u2265${thresholds.simulationMinIterations} simulation iterations. Apply conservative correction.`
@@ -1917,7 +1972,7 @@ var P39_TheLagPrinciple = {
1917
1972
  severity: 5,
1918
1973
  evidence: { inflationRate, netFlow, lagRange: [lagMin, lagMax] },
1919
1974
  suggestedAction: {
1920
- parameter: "craftingCost",
1975
+ parameter: "productionCost",
1921
1976
  direction: "increase",
1922
1977
  magnitude: 0.03,
1923
1978
  // very small — oscillation means over-adjusting
@@ -1944,7 +1999,7 @@ var P44_ComplexityBudget = {
1944
1999
  severity: 3,
1945
2000
  evidence: { customMetricCount, budgetMax: thresholds.complexityBudgetMax },
1946
2001
  suggestedAction: {
1947
- parameter: "auctionFee",
2002
+ parameter: "transactionFee",
1948
2003
  direction: "decrease",
1949
2004
  magnitude: 0.01,
1950
2005
  reasoning: `${customMetricCount} custom metrics tracked (budget: ${thresholds.complexityBudgetMax}). Consider pruning low-impact parameters. Applying minimal correction to avoid adding complexity.`
@@ -1966,25 +2021,25 @@ var P35_DestructionCreatesValue = {
1966
2021
  id: "P35",
1967
2022
  name: "Destruction Creates Value",
1968
2023
  category: "resource",
1969
- 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.",
2024
+ description: "If nothing is ever permanently lost, inflation is inevitable. Resource durability and consumption mechanisms create destruction. Without them, supply grows without bound.",
1970
2025
  check(metrics, _thresholds) {
1971
2026
  const { supplyByResource, sinkVolume, netFlow } = metrics;
1972
- const weapons = supplyByResource["weapons"] ?? 0;
1973
- const potions = supplyByResource["potions"] ?? 0;
1974
- if ((weapons > 200 || potions > 200) && sinkVolume < 5 && netFlow > 0) {
1975
- return {
1976
- violated: true,
1977
- severity: 6,
1978
- evidence: { weapons, potions, sinkVolume, netFlow },
1979
- suggestedAction: {
1980
- parameter: "arenaEntryFee",
1981
- direction: "decrease",
1982
- magnitude: 0.1,
1983
- reasoning: `${weapons} weapons + ${potions} potions with low destruction (sink ${sinkVolume}/t). Consumables not being consumed. Lower arena entry to increase weapon/potion usage.`
1984
- },
1985
- confidence: 0.7,
1986
- estimatedLag: 5
1987
- };
2027
+ for (const [resource, supply] of Object.entries(supplyByResource)) {
2028
+ if (supply > 200 && sinkVolume < 5 && netFlow > 0) {
2029
+ return {
2030
+ violated: true,
2031
+ severity: 6,
2032
+ evidence: { resource, supply, sinkVolume, netFlow },
2033
+ suggestedAction: {
2034
+ parameter: "entryFee",
2035
+ direction: "decrease",
2036
+ magnitude: 0.1,
2037
+ reasoning: `${resource} supply at ${supply} units with low destruction (sink ${sinkVolume}/t). Resources not being consumed. Lower competitive pool entry to increase resource usage.`
2038
+ },
2039
+ confidence: 0.7,
2040
+ estimatedLag: 5
2041
+ };
2042
+ }
1988
2043
  }
1989
2044
  return { violated: false };
1990
2045
  }
@@ -2004,7 +2059,7 @@ var P40_ReplacementRate = {
2004
2059
  severity: 6,
2005
2060
  evidence: { productionIndex, sinkVolume, replacementRatio },
2006
2061
  suggestedAction: {
2007
- parameter: "miningYield",
2062
+ parameter: "yieldRate",
2008
2063
  direction: "increase",
2009
2064
  magnitude: 0.15,
2010
2065
  reasoning: `Replacement rate ${replacementRatio.toFixed(2)} (need \u2265${thresholds.replacementRateMultiplier}). Production below consumption. Resources will deplete. Increase yield.`
@@ -2018,7 +2073,7 @@ var P40_ReplacementRate = {
2018
2073
  severity: 3,
2019
2074
  evidence: { productionIndex, sinkVolume, replacementRatio },
2020
2075
  suggestedAction: {
2021
- parameter: "miningYield",
2076
+ parameter: "yieldRate",
2022
2077
  direction: "decrease",
2023
2078
  magnitude: 0.1,
2024
2079
  reasoning: `Replacement rate ${replacementRatio.toFixed(2)} \u2014 overproducing. Production far exceeds consumption. Reduce yield to prevent glut.`
@@ -2044,7 +2099,7 @@ var P49_IdleAssetTax = {
2044
2099
  severity: 5,
2045
2100
  evidence: { giniCoefficient, top10PctShare, velocity },
2046
2101
  suggestedAction: {
2047
- parameter: "auctionFee",
2102
+ parameter: "transactionFee",
2048
2103
  direction: "increase",
2049
2104
  magnitude: 0.15,
2050
2105
  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.`
@@ -2076,7 +2131,7 @@ var P33_FairNotEqual = {
2076
2131
  severity: 3,
2077
2132
  evidence: { giniCoefficient },
2078
2133
  suggestedAction: {
2079
- parameter: "arenaReward",
2134
+ parameter: "rewardRate",
2080
2135
  direction: "increase",
2081
2136
  magnitude: 0.1,
2082
2137
  reasoning: `Gini ${giniCoefficient.toFixed(2)} \u2014 near-perfect equality. Economy lacks stakes. Increase winner rewards to create meaningful spread.`
@@ -2091,7 +2146,7 @@ var P33_FairNotEqual = {
2091
2146
  severity: 7,
2092
2147
  evidence: { giniCoefficient },
2093
2148
  suggestedAction: {
2094
- parameter: "auctionFee",
2149
+ parameter: "transactionFee",
2095
2150
  direction: "increase",
2096
2151
  magnitude: 0.2,
2097
2152
  reasoning: `Gini ${giniCoefficient.toFixed(2)} \u2014 oligarchy level. Toxic inequality. Raise transaction fees to redistribute wealth from rich to pool.`
@@ -2106,7 +2161,7 @@ var P33_FairNotEqual = {
2106
2161
  severity: 4,
2107
2162
  evidence: { giniCoefficient },
2108
2163
  suggestedAction: {
2109
- parameter: "auctionFee",
2164
+ parameter: "transactionFee",
2110
2165
  direction: "increase",
2111
2166
  magnitude: 0.1,
2112
2167
  reasoning: `Gini ${giniCoefficient.toFixed(2)} \u2014 high inequality warning. Gently raise fees to slow wealth concentration.`
@@ -2131,7 +2186,7 @@ var P36_MechanicFrictionDetector = {
2131
2186
  severity: 5,
2132
2187
  evidence: { churnRate, avgSatisfaction, velocity },
2133
2188
  suggestedAction: {
2134
- parameter: "arenaReward",
2189
+ parameter: "rewardRate",
2135
2190
  direction: "increase",
2136
2191
  magnitude: 0.15,
2137
2192
  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."
@@ -2156,7 +2211,7 @@ var P37_LatecommerProblem = {
2156
2211
  severity: 6,
2157
2212
  evidence: { timeToValue, avgSatisfaction, churnRate },
2158
2213
  suggestedAction: {
2159
- parameter: "craftingCost",
2214
+ parameter: "productionCost",
2160
2215
  direction: "decrease",
2161
2216
  magnitude: 0.15,
2162
2217
  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.`
@@ -2183,7 +2238,7 @@ var P45_TimeBudget = {
2183
2238
  severity: 5,
2184
2239
  evidence: { timeToValue, avgSatisfaction, timeBudgetRatio: thresholds.timeBudgetRatio },
2185
2240
  suggestedAction: {
2186
- parameter: "arenaEntryFee",
2241
+ parameter: "entryFee",
2187
2242
  direction: "decrease",
2188
2243
  magnitude: 0.15,
2189
2244
  reasoning: `Time-to-value ${timeToValue} ticks with ${avgSatisfaction.toFixed(0)} satisfaction. Economy requires too much time investment. Lower barriers to participation.`
@@ -2213,7 +2268,7 @@ var P50_PayPowerRatio = {
2213
2268
  threshold: thresholds.payPowerRatioMax
2214
2269
  },
2215
2270
  suggestedAction: {
2216
- parameter: "auctionFee",
2271
+ parameter: "transactionFee",
2217
2272
  direction: "increase",
2218
2273
  magnitude: 0.2,
2219
2274
  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.`
@@ -2248,7 +2303,7 @@ var P34_ExtractionRatio = {
2248
2303
  severity: 8,
2249
2304
  evidence: { extractionRatio, threshold: thresholds.extractionRatioRed },
2250
2305
  suggestedAction: {
2251
- parameter: "auctionFee",
2306
+ parameter: "transactionFee",
2252
2307
  direction: "increase",
2253
2308
  magnitude: 0.25,
2254
2309
  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.`
@@ -2263,7 +2318,7 @@ var P34_ExtractionRatio = {
2263
2318
  severity: 5,
2264
2319
  evidence: { extractionRatio, threshold: thresholds.extractionRatioYellow },
2265
2320
  suggestedAction: {
2266
- parameter: "auctionFee",
2321
+ parameter: "transactionFee",
2267
2322
  direction: "increase",
2268
2323
  magnitude: 0.1,
2269
2324
  reasoning: `Extraction ratio ${(extractionRatio * 100).toFixed(0)}% (warning: ${(thresholds.extractionRatioYellow * 100).toFixed(0)}%). Economy trending toward extraction-heavy. Apply early pressure.`
@@ -2279,7 +2334,7 @@ var P47_SmokeTest = {
2279
2334
  id: "P47",
2280
2335
  name: "Smoke Test",
2281
2336
  category: "open_economy",
2282
- 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.",
2337
+ 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.",
2283
2338
  check(metrics, thresholds) {
2284
2339
  const { smokeTestRatio } = metrics;
2285
2340
  if (isNaN(smokeTestRatio)) return { violated: false };
@@ -2289,7 +2344,7 @@ var P47_SmokeTest = {
2289
2344
  severity: 9,
2290
2345
  evidence: { smokeTestRatio, threshold: thresholds.smokeTestCritical },
2291
2346
  suggestedAction: {
2292
- parameter: "arenaReward",
2347
+ parameter: "rewardRate",
2293
2348
  direction: "increase",
2294
2349
  magnitude: 0.2,
2295
2350
  reasoning: `Utility/market ratio ${(smokeTestRatio * 100).toFixed(0)}% (critical). Economy is >90% speculative. Collapse risk is extreme. Increase utility rewards to anchor real value.`
@@ -2304,7 +2359,7 @@ var P47_SmokeTest = {
2304
2359
  severity: 6,
2305
2360
  evidence: { smokeTestRatio, threshold: thresholds.smokeTestWarning },
2306
2361
  suggestedAction: {
2307
- parameter: "arenaReward",
2362
+ parameter: "rewardRate",
2308
2363
  direction: "increase",
2309
2364
  magnitude: 0.1,
2310
2365
  reasoning: `Utility/market ratio ${(smokeTestRatio * 100).toFixed(0)}% (warning). Economy is >70% speculative. Boost utility rewards to restore intrinsic value anchor.`
@@ -2320,7 +2375,7 @@ var P48_CurrencyInsulation = {
2320
2375
  id: "P48",
2321
2376
  name: "Currency Insulation",
2322
2377
  category: "open_economy",
2323
- 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.",
2378
+ 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.",
2324
2379
  check(metrics, thresholds) {
2325
2380
  const { currencyInsulation } = metrics;
2326
2381
  if (isNaN(currencyInsulation)) return { violated: false };
@@ -2330,7 +2385,7 @@ var P48_CurrencyInsulation = {
2330
2385
  severity: 6,
2331
2386
  evidence: { currencyInsulation, threshold: thresholds.currencyInsulationMax },
2332
2387
  suggestedAction: {
2333
- parameter: "auctionFee",
2388
+ parameter: "transactionFee",
2334
2389
  direction: "increase",
2335
2390
  magnitude: 0.1,
2336
2391
  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.`
@@ -2370,7 +2425,7 @@ var P51_SharkTooth = {
2370
2425
  threshold: thresholds.sharkToothPeakDecay
2371
2426
  },
2372
2427
  suggestedAction: {
2373
- parameter: "arenaReward",
2428
+ parameter: "rewardRate",
2374
2429
  direction: "increase",
2375
2430
  magnitude: 0.1,
2376
2431
  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.`
@@ -2388,7 +2443,7 @@ var P51_SharkTooth = {
2388
2443
  severity: 4,
2389
2444
  evidence: { lastValley, prevValley, ratio: lastValley / prevValley },
2390
2445
  suggestedAction: {
2391
- parameter: "craftingCost",
2446
+ parameter: "productionCost",
2392
2447
  direction: "decrease",
2393
2448
  magnitude: 0.1,
2394
2449
  reasoning: "Between-event engagement declining (deepening valleys). Base economy not sustaining participants between events. Lower production costs to improve off-event value."
@@ -2405,7 +2460,7 @@ var P52_EndowmentEffect = {
2405
2460
  id: "P52",
2406
2461
  name: "Endowment Effect",
2407
2462
  category: "liveops",
2408
- 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).",
2463
+ 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).",
2409
2464
  check(metrics, _thresholds) {
2410
2465
  const { avgSatisfaction, churnRate } = metrics;
2411
2466
  const { eventCompletionRate } = metrics;
@@ -2416,7 +2471,7 @@ var P52_EndowmentEffect = {
2416
2471
  severity: 4,
2417
2472
  evidence: { eventCompletionRate, avgSatisfaction, churnRate },
2418
2473
  suggestedAction: {
2419
- parameter: "arenaReward",
2474
+ parameter: "rewardRate",
2420
2475
  direction: "increase",
2421
2476
  magnitude: 0.15,
2422
2477
  reasoning: `${(eventCompletionRate * 100).toFixed(0)}% event completion but satisfaction only ${avgSatisfaction.toFixed(0)}. Events not creating perceived value. Increase reward quality/quantity.`
@@ -2446,10 +2501,10 @@ var P53_EventCompletionRate = {
2446
2501
  max: thresholds.eventCompletionMax
2447
2502
  },
2448
2503
  suggestedAction: {
2449
- parameter: "craftingCost",
2504
+ parameter: "productionCost",
2450
2505
  direction: "decrease",
2451
2506
  magnitude: 0.15,
2452
- 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.`
2507
+ 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.`
2453
2508
  },
2454
2509
  confidence: 0.8,
2455
2510
  estimatedLag: 10
@@ -2461,7 +2516,7 @@ var P53_EventCompletionRate = {
2461
2516
  severity: 3,
2462
2517
  evidence: { eventCompletionRate, max: thresholds.eventCompletionMax },
2463
2518
  suggestedAction: {
2464
- parameter: "arenaEntryFee",
2519
+ parameter: "entryFee",
2465
2520
  direction: "increase",
2466
2521
  magnitude: 0.05,
2467
2522
  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.`
@@ -2486,7 +2541,7 @@ var P54_LiveOpsCadence = {
2486
2541
  severity: 3,
2487
2542
  evidence: { velocity, avgSatisfaction, tick: metrics.tick },
2488
2543
  suggestedAction: {
2489
- parameter: "arenaReward",
2544
+ parameter: "rewardRate",
2490
2545
  direction: "increase",
2491
2546
  magnitude: 0.1,
2492
2547
  reasoning: "Low velocity and satisfaction after long runtime. Possible content staleness. Increase rewards as bridge while new content is developed (developer action required)."
@@ -2517,7 +2572,7 @@ var P56_ContentDropShock = {
2517
2572
  postDropMax: thresholds.postDropArbitrageMax
2518
2573
  },
2519
2574
  suggestedAction: {
2520
- parameter: "auctionFee",
2575
+ parameter: "transactionFee",
2521
2576
  direction: "decrease",
2522
2577
  magnitude: 0.1,
2523
2578
  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.`
@@ -2683,19 +2738,21 @@ var Simulator = class {
2683
2738
  flowEffect(action, metrics) {
2684
2739
  const { parameter, direction } = action;
2685
2740
  const sign = direction === "increase" ? -1 : 1;
2686
- if (parameter === "craftingCost" || parameter === "alchemyCost") {
2741
+ const roleEntries = Object.entries(metrics.populationByRole).sort((a, b) => b[1] - a[1]);
2742
+ const dominantRoleCount = roleEntries[0]?.[1] ?? 0;
2743
+ if (parameter === "productionCost") {
2687
2744
  return sign * metrics.netFlow * 0.2;
2688
2745
  }
2689
- if (parameter === "auctionFee") {
2746
+ if (parameter === "transactionFee") {
2690
2747
  return sign * metrics.velocity * 10 * 0.1;
2691
2748
  }
2692
- if (parameter === "arenaEntryFee") {
2693
- return sign * (metrics.populationByRole["Fighter"] ?? 0) * 0.5;
2749
+ if (parameter === "entryFee") {
2750
+ return sign * dominantRoleCount * 0.5;
2694
2751
  }
2695
- if (parameter === "arenaReward") {
2696
- return -sign * (metrics.populationByRole["Fighter"] ?? 0) * 0.3;
2752
+ if (parameter === "rewardRate") {
2753
+ return -sign * dominantRoleCount * 0.3;
2697
2754
  }
2698
- if (parameter === "miningYield" || parameter === "lumberYield") {
2755
+ if (parameter === "yieldRate") {
2699
2756
  return sign * metrics.faucetVolume * 0.15;
2700
2757
  }
2701
2758
  return sign * metrics.netFlow * 0.1;
@@ -3008,30 +3065,33 @@ var RingBuffer = class {
3008
3065
  }
3009
3066
  };
3010
3067
  var MetricStore = class {
3011
- constructor() {
3068
+ constructor(tickConfig) {
3012
3069
  /** Fine: last 200 ticks, one entry per tick */
3013
3070
  this.fine = new RingBuffer(200);
3014
- /** Medium: last 200 windows of 10 ticks */
3071
+ /** Medium: last 200 windows */
3015
3072
  this.medium = new RingBuffer(200);
3016
- /** Coarse: last 200 epochs of 100 ticks */
3073
+ /** Coarse: last 200 epochs */
3017
3074
  this.coarse = new RingBuffer(200);
3018
3075
  this.ticksSinceLastMedium = 0;
3019
3076
  this.ticksSinceLastCoarse = 0;
3020
3077
  this.mediumAccumulator = [];
3021
3078
  this.coarseAccumulator = [];
3079
+ const config = { ...DEFAULT_TICK_CONFIG, ...tickConfig };
3080
+ this.mediumWindow = config.mediumWindow;
3081
+ this.coarseWindow = config.coarseWindow;
3022
3082
  }
3023
3083
  record(metrics) {
3024
3084
  this.fine.push(metrics);
3025
3085
  this.mediumAccumulator.push(metrics);
3026
3086
  this.ticksSinceLastMedium++;
3027
- if (this.ticksSinceLastMedium >= 10) {
3087
+ if (this.ticksSinceLastMedium >= this.mediumWindow) {
3028
3088
  this.medium.push(this.aggregate(this.mediumAccumulator));
3029
3089
  this.mediumAccumulator = [];
3030
3090
  this.ticksSinceLastMedium = 0;
3031
3091
  }
3032
3092
  this.coarseAccumulator.push(metrics);
3033
3093
  this.ticksSinceLastCoarse++;
3034
- if (this.ticksSinceLastCoarse >= 100) {
3094
+ if (this.ticksSinceLastCoarse >= this.coarseWindow) {
3035
3095
  this.coarse.push(this.aggregate(this.coarseAccumulator));
3036
3096
  this.coarseAccumulator = [];
3037
3097
  this.ticksSinceLastCoarse = 0;
@@ -3131,7 +3191,7 @@ var PersonaTracker = class {
3131
3191
  /** Classify all agents and return persona distribution */
3132
3192
  getDistribution() {
3133
3193
  const counts = {
3134
- Gamer: 0,
3194
+ Active: 0,
3135
3195
  Trader: 0,
3136
3196
  Collector: 0,
3137
3197
  Speculator: 0,
@@ -3155,7 +3215,7 @@ var PersonaTracker = class {
3155
3215
  return distribution;
3156
3216
  }
3157
3217
  classify(history) {
3158
- if (history.length === 0) return "Gamer";
3218
+ if (history.length === 0) return "Active";
3159
3219
  const avg = (key) => {
3160
3220
  const vals = history.map((h) => h[key]);
3161
3221
  return vals.reduce((s, v) => s + v, 0) / vals.length;
@@ -3169,21 +3229,18 @@ var PersonaTracker = class {
3169
3229
  if (uniqueItems > 5 && extraction < 0) return "Collector";
3170
3230
  if (extraction > 100) return "Earner";
3171
3231
  if (extraction > 50) return "Speculator";
3172
- return "Gamer";
3232
+ return "Active";
3173
3233
  }
3174
3234
  };
3175
3235
 
3176
3236
  // src/AgentE.ts
3177
3237
  var AgentE = class {
3178
3238
  constructor(config) {
3179
- // ── Pipeline ──
3180
- this.observer = new Observer();
3181
3239
  this.simulator = new Simulator();
3182
3240
  this.planner = new Planner();
3183
3241
  this.executor = new Executor();
3184
3242
  // ── State ──
3185
3243
  this.log = new DecisionLog();
3186
- this.store = new MetricStore();
3187
3244
  this.personaTracker = new PersonaTracker();
3188
3245
  this.params = {};
3189
3246
  this.eventBuffer = [];
@@ -3202,6 +3259,7 @@ var AgentE = class {
3202
3259
  mode: this.mode,
3203
3260
  dominantRoles: config.dominantRoles ?? [],
3204
3261
  idealDistribution: config.idealDistribution ?? {},
3262
+ tickConfig: config.tickConfig ?? { duration: 1, unit: "tick" },
3205
3263
  gracePeriod: config.gracePeriod ?? 50,
3206
3264
  checkInterval: config.checkInterval ?? 5,
3207
3265
  maxAdjustmentPercent: config.maxAdjustmentPercent ?? 0.15,
@@ -3213,6 +3271,9 @@ var AgentE = class {
3213
3271
  maxAdjustmentPercent: config.maxAdjustmentPercent ?? DEFAULT_THRESHOLDS.maxAdjustmentPercent,
3214
3272
  cooldownTicks: config.cooldownTicks ?? DEFAULT_THRESHOLDS.cooldownTicks
3215
3273
  };
3274
+ const tickConfig = { ...DEFAULT_TICK_CONFIG, ...config.tickConfig };
3275
+ this.observer = new Observer(tickConfig);
3276
+ this.store = new MetricStore(tickConfig);
3216
3277
  this.diagnoser = new Diagnoser(ALL_PRINCIPLES);
3217
3278
  if (config.onDecision) this.on("decision", config.onDecision);
3218
3279
  if (config.onAlert) this.on("alert", config.onAlert);
@@ -3244,7 +3305,7 @@ var AgentE = class {
3244
3305
  this.isRunning = false;
3245
3306
  this.isPaused = false;
3246
3307
  }
3247
- // ── Main cycle (call once per tick from your game loop) ────────────────────
3308
+ // ── Main cycle (call once per tick from your economy loop) ─────────────────
3248
3309
  async tick(state) {
3249
3310
  if (!this.isRunning || this.isPaused) return;
3250
3311
  const currentState = state ?? await Promise.resolve(this.adapter.getState());