@agent-e/core 1.1.3 → 1.2.0

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