@agent-e/core 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -12,11 +12,11 @@ var DEFAULT_THRESHOLDS = {
12
12
  smokeTestWarning: 0.3,
13
13
  smokeTestCritical: 0.1,
14
14
  currencyInsulationMax: 0.5,
15
- // Player Experience (P45, P50)
15
+ // Participant Experience (P45, P50)
16
16
  timeBudgetRatio: 0.8,
17
17
  payPowerRatioMax: 2,
18
18
  payPowerRatioTarget: 1.5,
19
- // LiveOps (P51, P53)
19
+ // Operations (P51, P53)
20
20
  sharkToothPeakDecay: 0.95,
21
21
  sharkToothValleyDecay: 0.9,
22
22
  eventCompletionMin: 0.4,
@@ -70,7 +70,7 @@ var PERSONA_HEALTHY_RANGES = {
70
70
  Earner: { min: 0, max: 0.15 },
71
71
  Builder: { min: 0.05, max: 0.15 },
72
72
  Social: { min: 0.1, max: 0.2 },
73
- Whale: { min: 0, max: 0.05 },
73
+ HighValue: { min: 0, max: 0.05 },
74
74
  Influencer: { min: 0, max: 0.05 }
75
75
  };
76
76
  var DEFAULT_TICK_CONFIG = {
@@ -107,7 +107,7 @@ var Observer = class {
107
107
  const curr = e.currency ?? defaultCurrency;
108
108
  switch (e.type) {
109
109
  case "mint":
110
- case "spawn":
110
+ case "enter":
111
111
  faucetVolumeByCurrency[curr] = (faucetVolumeByCurrency[curr] ?? 0) + (e.amount ?? 0);
112
112
  break;
113
113
  case "burn":
@@ -129,6 +129,46 @@ var Observer = class {
129
129
  break;
130
130
  }
131
131
  }
132
+ const flowBySystem = {};
133
+ const activityBySystem = {};
134
+ const actorsBySystem = {};
135
+ const flowBySource = {};
136
+ const flowBySink = {};
137
+ for (const e of recentEvents) {
138
+ if (e.system) {
139
+ activityBySystem[e.system] = (activityBySystem[e.system] ?? 0) + 1;
140
+ if (!actorsBySystem[e.system]) actorsBySystem[e.system] = /* @__PURE__ */ new Set();
141
+ actorsBySystem[e.system].add(e.actor);
142
+ const amt = e.amount ?? 0;
143
+ if (e.type === "mint" || e.type === "enter") {
144
+ flowBySystem[e.system] = (flowBySystem[e.system] ?? 0) + amt;
145
+ } else if (e.type === "burn" || e.type === "consume") {
146
+ flowBySystem[e.system] = (flowBySystem[e.system] ?? 0) - amt;
147
+ }
148
+ }
149
+ if (e.sourceOrSink) {
150
+ const amt = e.amount ?? 0;
151
+ if (e.type === "mint" || e.type === "enter") {
152
+ flowBySource[e.sourceOrSink] = (flowBySource[e.sourceOrSink] ?? 0) + amt;
153
+ } else if (e.type === "burn" || e.type === "consume") {
154
+ flowBySink[e.sourceOrSink] = (flowBySink[e.sourceOrSink] ?? 0) + amt;
155
+ }
156
+ }
157
+ }
158
+ const participantsBySystem = {};
159
+ for (const [sys, actors] of Object.entries(actorsBySystem)) {
160
+ participantsBySystem[sys] = actors.size;
161
+ }
162
+ const totalSourceFlow = Object.values(flowBySource).reduce((s, v) => s + v, 0);
163
+ const sourceShare = {};
164
+ for (const [src, vol] of Object.entries(flowBySource)) {
165
+ sourceShare[src] = totalSourceFlow > 0 ? vol / totalSourceFlow : 0;
166
+ }
167
+ const totalSinkFlow = Object.values(flowBySink).reduce((s, v) => s + v, 0);
168
+ const sinkShare = {};
169
+ for (const [snk, vol] of Object.entries(flowBySink)) {
170
+ sinkShare[snk] = totalSinkFlow > 0 ? vol / totalSinkFlow : 0;
171
+ }
132
172
  const currencies = state.currencies;
133
173
  const totalSupplyByCurrency = {};
134
174
  const balancesByCurrency = {};
@@ -338,7 +378,8 @@ var Observer = class {
338
378
  for (const [name, fn] of Object.entries(this.customMetricFns)) {
339
379
  try {
340
380
  custom[name] = fn(state);
341
- } catch {
381
+ } catch (err) {
382
+ console.warn(`[AgentE] Custom metric '${name}' threw an error:`, err);
342
383
  custom[name] = NaN;
343
384
  }
344
385
  }
@@ -415,6 +456,13 @@ var Observer = class {
415
456
  sharkToothValleys: this.previousMetrics?.sharkToothValleys ?? [],
416
457
  eventCompletionRate: NaN,
417
458
  contentDropAge,
459
+ flowBySystem,
460
+ activityBySystem,
461
+ participantsBySystem,
462
+ flowBySource,
463
+ flowBySink,
464
+ sourceShare,
465
+ sinkShare,
418
466
  custom
419
467
  };
420
468
  this.previousMetrics = metrics;
@@ -557,6 +605,13 @@ function emptyMetrics(tick = 0) {
557
605
  sharkToothValleys: [],
558
606
  eventCompletionRate: NaN,
559
607
  contentDropAge: 0,
608
+ flowBySystem: {},
609
+ activityBySystem: {},
610
+ participantsBySystem: {},
611
+ flowBySource: {},
612
+ flowBySink: {},
613
+ sourceShare: {},
614
+ sinkShare: {},
560
615
  custom: {}
561
616
  };
562
617
  }
@@ -589,7 +644,7 @@ var P1_ProductionMatchesConsumption = {
589
644
  severity: 7,
590
645
  evidence: { scarceResources: violations, dominantRole: dominantRole?.[0], dominantShare },
591
646
  suggestedAction: {
592
- parameter: "productionCost",
647
+ parameterType: "cost",
593
648
  direction: "decrease",
594
649
  magnitude: 0.15,
595
650
  reasoning: "Lower production cost to incentivise more production."
@@ -623,7 +678,8 @@ var P2_ClosedLoopsNeedDirectHandoff = {
623
678
  severity: 5,
624
679
  evidence: { backlogResources, velocity },
625
680
  suggestedAction: {
626
- parameter: "transactionFee",
681
+ parameterType: "fee",
682
+ scope: { tags: ["transaction"] },
627
683
  direction: "increase",
628
684
  magnitude: 0.2,
629
685
  reasoning: "Raise market fees to discourage raw material listings. Direct hand-off at production zones is the correct channel."
@@ -653,7 +709,7 @@ var P3_BootstrapCapitalCoversFirstTransaction = {
653
709
  severity: 8,
654
710
  evidence: { resource, totalProducers, supply },
655
711
  suggestedAction: {
656
- parameter: "productionCost",
712
+ parameterType: "cost",
657
713
  direction: "decrease",
658
714
  magnitude: 0.3,
659
715
  reasoning: "Producers cannot complete first transaction. Lower production cost to unblock bootstrap."
@@ -672,7 +728,7 @@ var P4_MaterialsFlowFasterThanCooldown = {
672
728
  id: "P4",
673
729
  name: "Materials Flow Faster Than Cooldown",
674
730
  category: "supply_chain",
675
- 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.",
731
+ description: "Input delivery rate must exceed or match production cooldown rate. If producers produce every 5 ticks but only receive raw materials every 10 ticks, they starve regardless of supply levels.",
676
732
  check(metrics, _thresholds) {
677
733
  const { supplyByResource, populationByRole, velocity, totalAgents } = metrics;
678
734
  const totalSupply = Object.values(supplyByResource).reduce((s, v) => s + v, 0);
@@ -685,7 +741,7 @@ var P4_MaterialsFlowFasterThanCooldown = {
685
741
  severity: 5,
686
742
  evidence: { avgSupplyPerAgent, velocity, totalRoles },
687
743
  suggestedAction: {
688
- parameter: "yieldRate",
744
+ parameterType: "yield",
689
745
  direction: "increase",
690
746
  magnitude: 0.15,
691
747
  reasoning: "Low supply per agent with stagnant velocity. Increase yield to compensate."
@@ -700,7 +756,7 @@ var P4_MaterialsFlowFasterThanCooldown = {
700
756
  severity: 4,
701
757
  evidence: { avgSupplyPerAgent, totalSupply, totalAgents },
702
758
  suggestedAction: {
703
- parameter: "yieldRate",
759
+ parameterType: "yield",
704
760
  direction: "decrease",
705
761
  magnitude: 0.2,
706
762
  reasoning: "Raw materials piling up. Extractors outpacing producers."
@@ -728,7 +784,7 @@ var P60_SurplusDisposalAsymmetry = {
728
784
  discount: thresholds.disposalTradeWeightDiscount
729
785
  },
730
786
  suggestedAction: {
731
- parameter: "productionCost",
787
+ parameterType: "cost",
732
788
  direction: "decrease",
733
789
  magnitude: 0.1,
734
790
  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.`
@@ -771,7 +827,8 @@ var P5_ProfitabilityIsCompetitive = {
771
827
  population: populationByRole[dominantRole]
772
828
  },
773
829
  suggestedAction: {
774
- parameter: "transactionFee",
830
+ parameterType: "fee",
831
+ scope: { tags: ["transaction"] },
775
832
  direction: "increase",
776
833
  magnitude: thresholds.maxAdjustmentPercent,
777
834
  reasoning: `${dominantRole} share at ${((roleShares[dominantRole] ?? 0) * 100).toFixed(0)}%. Likely stampede from non-competitive profitability formula. Raise market friction to slow role accumulation.`
@@ -797,7 +854,7 @@ var P6_CrowdingMultiplierOnAllRoles = {
797
854
  severity: 5,
798
855
  evidence: { role, share },
799
856
  suggestedAction: {
800
- parameter: "productionCost",
857
+ parameterType: "cost",
801
858
  direction: "increase",
802
859
  magnitude: 0.1,
803
860
  reasoning: `${role} at ${(share * 100).toFixed(0)}% \u2014 no crowding pressure detected. Apply role-specific cost increase to simulate saturation.`
@@ -832,7 +889,8 @@ var P7_NonSpecialistsSubsidiseSpecialists = {
832
889
  severity: 6,
833
890
  evidence: { poolName, poolSize, dominantRole, dominantShare },
834
891
  suggestedAction: {
835
- parameter: "entryFee",
892
+ parameterType: "fee",
893
+ scope: { tags: ["entry"] },
836
894
  direction: "decrease",
837
895
  magnitude: 0.1,
838
896
  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.`
@@ -860,7 +918,7 @@ var P8_RegulatorCannotFightDesign = {
860
918
  severity: 4,
861
919
  evidence: { dominantRole: dominantRole[0], share: dominantRole[1], avgSatisfaction },
862
920
  suggestedAction: {
863
- parameter: "rewardRate",
921
+ parameterType: "reward",
864
922
  direction: "increase",
865
923
  magnitude: 0.1,
866
924
  reasoning: `Low satisfaction with ${dominantRole[0]} dominant. Regulator may be suppressing a structurally necessary role. Ease pressure on dominant role rewards.`
@@ -895,7 +953,7 @@ var P9_RoleSwitchingNeedsFriction = {
895
953
  severity: 5,
896
954
  evidence: { totalChurnRate: totalChurn, churnByRole },
897
955
  suggestedAction: {
898
- parameter: "productionCost",
956
+ parameterType: "cost",
899
957
  direction: "increase",
900
958
  magnitude: 0.05,
901
959
  reasoning: `Role switch rate ${(totalChurn * 100).toFixed(1)}% exceeds friction threshold. Increase production costs to slow herd movement.`
@@ -910,9 +968,9 @@ var P9_RoleSwitchingNeedsFriction = {
910
968
  };
911
969
  var P10_SpawnWeightingUsesInversePopulation = {
912
970
  id: "P10",
913
- name: "Spawn Weighting Uses Inverse Population",
971
+ name: "Entry Weighting Uses Inverse Population",
914
972
  category: "population",
915
- description: "New agents should preferentially fill the least-populated roles. Flat entry probability causes initial imbalances to compound.",
973
+ description: "New entrants should preferentially fill the least-populated roles. Flat entry probability causes initial imbalances to compound.",
916
974
  check(metrics, _thresholds) {
917
975
  const { roleShares } = metrics;
918
976
  if (Object.keys(roleShares).length === 0) return { violated: false };
@@ -927,7 +985,7 @@ var P10_SpawnWeightingUsesInversePopulation = {
927
985
  severity: 4,
928
986
  evidence: { roleShares, stdDev, leastPopulatedRole: minRole?.[0] },
929
987
  suggestedAction: {
930
- parameter: "yieldRate",
988
+ parameterType: "yield",
931
989
  direction: "increase",
932
990
  magnitude: 0.05,
933
991
  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.`
@@ -953,7 +1011,8 @@ var P11_TwoTierPressure = {
953
1011
  severity: 6,
954
1012
  evidence: { role, share },
955
1013
  suggestedAction: {
956
- parameter: "transactionFee",
1014
+ parameterType: "fee",
1015
+ scope: { tags: ["transaction"] },
957
1016
  direction: "increase",
958
1017
  magnitude: 0.15,
959
1018
  reasoning: `${role} at ${(share * 100).toFixed(0)}% \u2014 continuous pressure was insufficient. Hard intervention needed alongside resumed continuous pressure.`
@@ -981,7 +1040,7 @@ var P46_PersonaDiversity = {
981
1040
  severity: 5,
982
1041
  evidence: { dominantPersona: persona, share, personaDistribution },
983
1042
  suggestedAction: {
984
- parameter: "rewardRate",
1043
+ parameterType: "reward",
985
1044
  direction: "increase",
986
1045
  magnitude: 0.1,
987
1046
  reasoning: `${persona} persona at ${(share * 100).toFixed(0)}% \u2014 behavioral monoculture. Diversify reward structures to attract other persona types.`
@@ -998,7 +1057,8 @@ var P46_PersonaDiversity = {
998
1057
  severity: 3,
999
1058
  evidence: { significantClusters, required: thresholds.personaMinClusters },
1000
1059
  suggestedAction: {
1001
- parameter: "transactionFee",
1060
+ parameterType: "fee",
1061
+ scope: { tags: ["transaction"] },
1002
1062
  direction: "decrease",
1003
1063
  magnitude: 0.05,
1004
1064
  reasoning: `Only ${significantClusters} significant persona clusters (need ${thresholds.personaMinClusters}). Lower trade barriers to attract non-dominant persona types.`
@@ -1022,7 +1082,7 @@ var P12_OnePrimaryFaucet = {
1022
1082
  id: "P12",
1023
1083
  name: "One Primary Faucet",
1024
1084
  category: "currency",
1025
- description: "Multiple independent currency sources (gathering + production + quests) each creating currency causes uncontrolled inflation. One clear primary faucet makes the economy predictable and auditable.",
1085
+ description: "Multiple independent currency sources (gathering + production + activities) each creating currency causes uncontrolled inflation. One clear primary faucet makes the economy predictable and auditable.",
1026
1086
  check(metrics, thresholds) {
1027
1087
  for (const curr of metrics.currencies) {
1028
1088
  const netFlow = metrics.netFlowByCurrency[curr] ?? 0;
@@ -1034,9 +1094,9 @@ var P12_OnePrimaryFaucet = {
1034
1094
  severity: 5,
1035
1095
  evidence: { currency: curr, netFlow, faucetVolume, sinkVolume },
1036
1096
  suggestedAction: {
1037
- parameter: "productionCost",
1097
+ parameterType: "cost",
1038
1098
  direction: "increase",
1039
- currency: curr,
1099
+ scope: { currency: curr },
1040
1100
  magnitude: 0.15,
1041
1101
  reasoning: `[${curr}] Net flow +${netFlow.toFixed(1)}/tick. Inflationary. Increase production cost (primary sink) to balance faucet output.`
1042
1102
  },
@@ -1050,9 +1110,9 @@ var P12_OnePrimaryFaucet = {
1050
1110
  severity: 4,
1051
1111
  evidence: { currency: curr, netFlow, faucetVolume, sinkVolume },
1052
1112
  suggestedAction: {
1053
- parameter: "productionCost",
1113
+ parameterType: "cost",
1054
1114
  direction: "decrease",
1055
- currency: curr,
1115
+ scope: { currency: curr },
1056
1116
  magnitude: 0.15,
1057
1117
  reasoning: `[${curr}] Net flow ${netFlow.toFixed(1)}/tick. Deflationary. Decrease production cost to ease sink pressure.`
1058
1118
  },
@@ -1084,9 +1144,9 @@ var P13_PotsAreZeroSumAndSelfRegulate = {
1084
1144
  severity: 7,
1085
1145
  evidence: { currency: curr, pool: poolName, poolSize, participants: dominantCount, maxSustainableMultiplier },
1086
1146
  suggestedAction: {
1087
- parameter: "rewardRate",
1147
+ parameterType: "reward",
1088
1148
  direction: "decrease",
1089
- currency: curr,
1149
+ scope: { currency: curr },
1090
1150
  magnitude: 0.15,
1091
1151
  reasoning: `[${curr}] ${poolName} pool at ${poolSize.toFixed(0)} currency with ${dominantCount} active participants. Sustainable multiplier \u2264 ${maxSustainableMultiplier.toFixed(2)}. Reduce reward multiplier to prevent pool drain.`
1092
1152
  },
@@ -1103,7 +1163,7 @@ var P14_TrackActualInjection = {
1103
1163
  id: "P14",
1104
1164
  name: "Track Actual Currency Injection, Not Value Creation",
1105
1165
  category: "currency",
1106
- description: 'Counting resource gathering as "currency injected" is misleading. Currency enters through faucet mechanisms (spawning, rewards). Fake metrics break every downstream decision.',
1166
+ description: 'Counting resource gathering as "currency injected" is misleading. Currency enters through faucet mechanisms (entering, rewards). Fake metrics break every downstream decision.',
1107
1167
  check(metrics, _thresholds) {
1108
1168
  for (const curr of metrics.currencies) {
1109
1169
  const faucetVolume = metrics.faucetVolumeByCurrency[curr] ?? 0;
@@ -1116,9 +1176,9 @@ var P14_TrackActualInjection = {
1116
1176
  severity: 4,
1117
1177
  evidence: { currency: curr, faucetVolume, netFlow, supplyGrowthRate },
1118
1178
  suggestedAction: {
1119
- parameter: "yieldRate",
1179
+ parameterType: "yield",
1120
1180
  direction: "decrease",
1121
- currency: curr,
1181
+ scope: { currency: curr },
1122
1182
  magnitude: 0.1,
1123
1183
  reasoning: `[${curr}] Supply growing at ${(supplyGrowthRate * 100).toFixed(1)}%/tick. Verify currency injection tracking. Resources should not create currency directly.`
1124
1184
  },
@@ -1148,9 +1208,9 @@ var P15_PoolsNeedCapAndDecay = {
1148
1208
  severity: 6,
1149
1209
  evidence: { currency: curr, pool, size, shareOfSupply, cap: poolCapPercent },
1150
1210
  suggestedAction: {
1151
- parameter: "transactionFee",
1211
+ parameterType: "fee",
1152
1212
  direction: "decrease",
1153
- currency: curr,
1213
+ scope: { tags: ["transaction"], currency: curr },
1154
1214
  magnitude: 0.1,
1155
1215
  reasoning: `[${curr}] ${pool} pool at ${(shareOfSupply * 100).toFixed(1)}% of supply (cap: ${(poolCapPercent * 100).toFixed(0)}%). Currency frozen. Lower fees to encourage circulation over accumulation.`
1156
1216
  },
@@ -1180,9 +1240,9 @@ var P16_WithdrawalPenaltyScales = {
1180
1240
  severity: 3,
1181
1241
  evidence: { currency: curr, pool: poolName, poolSize, estimatedStaked: stakedEstimate },
1182
1242
  suggestedAction: {
1183
- parameter: "transactionFee",
1243
+ parameterType: "fee",
1184
1244
  direction: "increase",
1185
- currency: curr,
1245
+ scope: { tags: ["transaction"], currency: curr },
1186
1246
  magnitude: 0.05,
1187
1247
  reasoning: `[${curr}] ${poolName} pool depleted while significant currency should be locked. Early withdrawals may be draining the pool. Ensure withdrawal penalty scales with lock duration.`
1188
1248
  },
@@ -1212,9 +1272,9 @@ var P32_VelocityAboveSupply = {
1212
1272
  severity: 4,
1213
1273
  evidence: { currency: curr, velocity, totalSupply, totalResources },
1214
1274
  suggestedAction: {
1215
- parameter: "transactionFee",
1275
+ parameterType: "fee",
1216
1276
  direction: "decrease",
1217
- currency: curr,
1277
+ scope: { tags: ["transaction"], currency: curr },
1218
1278
  magnitude: 0.2,
1219
1279
  reasoning: `[${curr}] Velocity ${velocity}/t with ${totalResources} resources in system. Economy stagnant despite available supply. Lower trading friction.`
1220
1280
  },
@@ -1254,9 +1314,9 @@ var P58_NoNaturalNumeraire = {
1254
1314
  meanPrice: mean
1255
1315
  },
1256
1316
  suggestedAction: {
1257
- parameter: "productionCost",
1317
+ parameterType: "cost",
1258
1318
  direction: "increase",
1259
- currency: curr,
1319
+ scope: { currency: curr },
1260
1320
  magnitude: 0.1,
1261
1321
  reasoning: `[${curr}] 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.`
1262
1322
  },
@@ -1291,7 +1351,8 @@ var P17_GracePeriodBeforeIntervention = {
1291
1351
  severity: 7,
1292
1352
  evidence: { tick: metrics.tick, avgSatisfaction: metrics.avgSatisfaction },
1293
1353
  suggestedAction: {
1294
- parameter: "entryFee",
1354
+ parameterType: "fee",
1355
+ scope: { tags: ["entry"] },
1295
1356
  direction: "decrease",
1296
1357
  magnitude: 0.2,
1297
1358
  reasoning: `Very low satisfaction at tick ${metrics.tick}. Intervention may have fired during grace period. Ease all costs to let economy bootstrap.`
@@ -1318,7 +1379,7 @@ var P18_FirstProducerNeedsStartingInventory = {
1318
1379
  severity: 8,
1319
1380
  evidence: { tick: metrics.tick, resource, supply, totalAgents: metrics.totalAgents },
1320
1381
  suggestedAction: {
1321
- parameter: "productionCost",
1382
+ parameterType: "cost",
1322
1383
  direction: "decrease",
1323
1384
  magnitude: 0.5,
1324
1385
  reasoning: `Bootstrap failure: ${resource} supply is 0 at tick ${metrics.tick} with ${metrics.totalAgents} agents. Drastically reduce production cost to allow immediate output.`
@@ -1357,7 +1418,7 @@ var P19_StartingSupplyExceedsDemand = {
1357
1418
  resourcesPerAgent
1358
1419
  },
1359
1420
  suggestedAction: {
1360
- parameter: "rewardRate",
1421
+ parameterType: "reward",
1361
1422
  direction: "increase",
1362
1423
  magnitude: 0.2,
1363
1424
  reasoning: `${mostPopulatedRole} (${population} agents) has insufficient resources (${resourcesPerAgent.toFixed(2)} per agent). Cold-start scarcity. Boost competitive pool reward to attract participation despite scarcity.`
@@ -1391,7 +1452,7 @@ var P20_DecayPreventsAccumulation = {
1391
1452
  severity: 4,
1392
1453
  evidence: { totalResources, resourcesPerAgent, velocity },
1393
1454
  suggestedAction: {
1394
- parameter: "yieldRate",
1455
+ parameterType: "yield",
1395
1456
  direction: "decrease",
1396
1457
  magnitude: 0.1,
1397
1458
  reasoning: `${totalResources.toFixed(0)} resources with velocity ${velocity}/t. Likely hoarding. Reduce yield to increase scarcity and force circulation.`
@@ -1419,7 +1480,8 @@ var P21_PriceFromGlobalSupply = {
1419
1480
  severity: 3,
1420
1481
  evidence: { resource, volatility, supply, price: prices[resource] },
1421
1482
  suggestedAction: {
1422
- parameter: "transactionFee",
1483
+ parameterType: "fee",
1484
+ scope: { tags: ["transaction"] },
1423
1485
  direction: "increase",
1424
1486
  magnitude: 0.05,
1425
1487
  reasoning: `${resource} price volatile (${(volatility * 100).toFixed(0)}%) despite supply ${supply}. Price may not reflect global inventory. Increase trading friction to stabilise.`
@@ -1436,7 +1498,7 @@ var P22_MarketAwarenessPreventsSurplus = {
1436
1498
  id: "P22",
1437
1499
  name: "Market Awareness Prevents Overproduction",
1438
1500
  category: "feedback",
1439
- description: "Producers who craft without checking market prices will create surpluses that crash prices. Agents need to see prices before deciding to produce.",
1501
+ description: "Producers who produce without checking market prices will create surpluses that crash prices. Agents need to see prices before deciding to produce.",
1440
1502
  check(metrics, _thresholds) {
1441
1503
  const { supplyByResource, prices, productionIndex } = metrics;
1442
1504
  const priceValues = Object.values(prices).filter((p) => p > 0);
@@ -1460,7 +1522,7 @@ var P22_MarketAwarenessPreventsSurplus = {
1460
1522
  productionIndex
1461
1523
  },
1462
1524
  suggestedAction: {
1463
- parameter: "productionCost",
1525
+ parameterType: "cost",
1464
1526
  direction: "increase",
1465
1527
  magnitude: 0.1,
1466
1528
  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.`
@@ -1487,7 +1549,7 @@ var P23_ProfitabilityFactorsFeasibility = {
1487
1549
  severity: 5,
1488
1550
  evidence: { blockedFraction, blockedAgentCount, avgSatisfaction },
1489
1551
  suggestedAction: {
1490
- parameter: "productionCost",
1552
+ parameterType: "cost",
1491
1553
  direction: "decrease",
1492
1554
  magnitude: 0.15,
1493
1555
  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.`
@@ -1513,7 +1575,8 @@ var P24_BlockedAgentsDecayFaster = {
1513
1575
  severity: 5,
1514
1576
  evidence: { blockedFraction, blockedAgentCount, churnRate },
1515
1577
  suggestedAction: {
1516
- parameter: "transactionFee",
1578
+ parameterType: "fee",
1579
+ scope: { tags: ["transaction"] },
1517
1580
  direction: "decrease",
1518
1581
  magnitude: 0.15,
1519
1582
  reasoning: `${(blockedFraction * 100).toFixed(0)}% of agents blocked. Blocked agents churn silently, skewing metrics. Lower fees to unblock market participation.`
@@ -1558,7 +1621,7 @@ var P25_CorrectLeversForCorrectProblems = {
1558
1621
  netFlow
1559
1622
  },
1560
1623
  suggestedAction: {
1561
- parameter: "yieldRate",
1624
+ parameterType: "yield",
1562
1625
  direction: "decrease",
1563
1626
  magnitude: 0.15,
1564
1627
  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.`
@@ -1584,7 +1647,7 @@ var P26_ContinuousPressureBeatsThresholdCuts = {
1584
1647
  severity: 4,
1585
1648
  evidence: { inflationRate },
1586
1649
  suggestedAction: {
1587
- parameter: "productionCost",
1650
+ parameterType: "cost",
1588
1651
  direction: inflationRate > 0 ? "increase" : "decrease",
1589
1652
  magnitude: Math.min(thresholds.maxAdjustmentPercent, 0.05),
1590
1653
  // force smaller step
@@ -1610,7 +1673,8 @@ var P27_AdjustmentsNeedCooldowns = {
1610
1673
  severity: 4,
1611
1674
  evidence: { churnRate, avgSatisfaction },
1612
1675
  suggestedAction: {
1613
- parameter: "entryFee",
1676
+ parameterType: "fee",
1677
+ scope: { tags: ["entry"] },
1614
1678
  direction: "decrease",
1615
1679
  magnitude: 0.05,
1616
1680
  reasoning: `High churn (${(churnRate * 100).toFixed(1)}%) with low satisfaction. Possible oscillation from rapid adjustments. Apply small correction only.`
@@ -1641,7 +1705,7 @@ var P28_StructuralDominanceIsNotPathological = {
1641
1705
  severity: 5,
1642
1706
  evidence: { dominantRole, dominantShare, avgSatisfaction },
1643
1707
  suggestedAction: {
1644
- parameter: "productionCost",
1708
+ parameterType: "cost",
1645
1709
  direction: "decrease",
1646
1710
  magnitude: 0.1,
1647
1711
  reasoning: `${dominantRole} dominant (${(dominantShare * 100).toFixed(0)}%) with low satisfaction. Pathological dominance \u2014 agents trapped, not thriving. Ease costs to allow role switching.`
@@ -1657,7 +1721,7 @@ var P38_CommunicationPreventsRevolt = {
1657
1721
  id: "P38",
1658
1722
  name: "Communication Prevents Revolt",
1659
1723
  category: "regulator",
1660
- description: "Every adjustment must be logged with reasoning. An adjustment made without explanation to players causes revolt. AgentE logs every decision \u2014 this principle checks that logging is active.",
1724
+ description: "Every adjustment must be logged with reasoning. An adjustment made without explanation to participants causes revolt. AgentE logs every decision \u2014 this principle checks that logging is active.",
1661
1725
  check(metrics, _thresholds) {
1662
1726
  const { churnRate } = metrics;
1663
1727
  if (churnRate > 0.1) {
@@ -1666,7 +1730,7 @@ var P38_CommunicationPreventsRevolt = {
1666
1730
  severity: 3,
1667
1731
  evidence: { churnRate },
1668
1732
  suggestedAction: {
1669
- parameter: "rewardRate",
1733
+ parameterType: "reward",
1670
1734
  direction: "increase",
1671
1735
  magnitude: 0.1,
1672
1736
  reasoning: `High churn (${(churnRate * 100).toFixed(1)}%) \u2014 agents leaving. Ensure all recent adjustments are logged with reasoning to diagnose cause.`
@@ -1689,7 +1753,7 @@ var REGULATOR_PRINCIPLES = [
1689
1753
  // src/principles/market-dynamics.ts
1690
1754
  var P29_PinchPoint = {
1691
1755
  id: "P29",
1692
- name: "Pinch Point",
1756
+ name: "Bottleneck Detection",
1693
1757
  category: "market_dynamics",
1694
1758
  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.",
1695
1759
  check(metrics, _thresholds) {
@@ -1703,7 +1767,7 @@ var P29_PinchPoint = {
1703
1767
  severity: 7,
1704
1768
  evidence: { resource, supply, demand, status },
1705
1769
  suggestedAction: {
1706
- parameter: "productionCost",
1770
+ parameterType: "cost",
1707
1771
  direction: "decrease",
1708
1772
  magnitude: 0.15,
1709
1773
  reasoning: `${resource} is a pinch point and currently SCARCE (supply ${supply}, demand ${demand}). Reduce production cost to increase throughput.`
@@ -1719,7 +1783,7 @@ var P29_PinchPoint = {
1719
1783
  severity: 4,
1720
1784
  evidence: { resource, supply, status },
1721
1785
  suggestedAction: {
1722
- parameter: "productionCost",
1786
+ parameterType: "cost",
1723
1787
  direction: "increase",
1724
1788
  magnitude: 0.1,
1725
1789
  reasoning: `${resource} is a pinch point and OVERSUPPLIED (supply ${supply}). Raise production cost to reduce surplus.`
@@ -1734,9 +1798,9 @@ var P29_PinchPoint = {
1734
1798
  };
1735
1799
  var P30_MovingPinchPoint = {
1736
1800
  id: "P30",
1737
- name: "Moving Pinch Point",
1801
+ name: "Dynamic Bottleneck Rotation",
1738
1802
  category: "market_dynamics",
1739
- 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.",
1803
+ description: "Participant progression shifts the demand curve. A static pinch point that works early will be cleared later. The pinch point must move with participant progression to maintain ongoing scarcity and engagement.",
1740
1804
  check(metrics, _thresholds) {
1741
1805
  const { capacityUsage, supplyByResource, avgSatisfaction } = metrics;
1742
1806
  const totalResources = Object.values(supplyByResource).reduce((s, v) => s + v, 0);
@@ -1747,7 +1811,7 @@ var P30_MovingPinchPoint = {
1747
1811
  severity: 3,
1748
1812
  evidence: { capacityUsage, resourcesPerAgent, avgSatisfaction },
1749
1813
  suggestedAction: {
1750
- parameter: "productionCost",
1814
+ parameterType: "cost",
1751
1815
  direction: "increase",
1752
1816
  magnitude: 0.1,
1753
1817
  reasoning: "Economy operating at full capacity with abundant resources and high satisfaction. Pinch point may have been cleared. Increase production cost to restore scarcity."
@@ -1763,7 +1827,7 @@ var P57_CombinatorialPriceSpace = {
1763
1827
  id: "P57",
1764
1828
  name: "Combinatorial Price Space",
1765
1829
  category: "market_dynamics",
1766
- description: "N tradeable items generate (N\u22121)N/2 relative prices. With thousands of items no single agent can track them all. Design for distributed self-organization, not centralized pricing.",
1830
+ description: "N tradeable items generate (N\u22121)N/2 relative prices. With thousands of items no single participant can track them all. Design for distributed self-organization, not centralized pricing.",
1767
1831
  check(metrics, thresholds) {
1768
1832
  const { prices, priceVolatility } = metrics;
1769
1833
  const priceKeys = Object.keys(prices);
@@ -1793,7 +1857,8 @@ var P57_CombinatorialPriceSpace = {
1793
1857
  target: thresholds.relativePriceConvergenceTarget
1794
1858
  },
1795
1859
  suggestedAction: {
1796
- parameter: "transactionFee",
1860
+ parameterType: "fee",
1861
+ scope: { tags: ["transaction"] },
1797
1862
  direction: "decrease",
1798
1863
  magnitude: 0.1,
1799
1864
  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.`
@@ -1825,7 +1890,7 @@ var P31_AnchorValueTracking = {
1825
1890
  severity: 5,
1826
1891
  evidence: { anchorRatioDrift, inflationRate },
1827
1892
  suggestedAction: {
1828
- parameter: "productionCost",
1893
+ parameterType: "cost",
1829
1894
  direction: anchorRatioDrift > 0 ? "increase" : "decrease",
1830
1895
  magnitude: 0.1,
1831
1896
  reasoning: `Anchor ratio has drifted ${(anchorRatioDrift * 100).toFixed(0)}% from baseline. Time-to-value for participants is changing. Adjust production costs to restore.`
@@ -1850,7 +1915,8 @@ var P41_MultiResolutionMonitoring = {
1850
1915
  severity: 4,
1851
1916
  evidence: { giniCoefficient, avgSatisfaction },
1852
1917
  suggestedAction: {
1853
- parameter: "transactionFee",
1918
+ parameterType: "fee",
1919
+ scope: { tags: ["transaction"] },
1854
1920
  direction: "increase",
1855
1921
  magnitude: 0.1,
1856
1922
  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.`
@@ -1879,7 +1945,8 @@ var P55_ArbitrageThermometer = {
1879
1945
  critical: thresholds.arbitrageIndexCritical
1880
1946
  },
1881
1947
  suggestedAction: {
1882
- parameter: "transactionFee",
1948
+ parameterType: "fee",
1949
+ scope: { tags: ["transaction"] },
1883
1950
  direction: "decrease",
1884
1951
  magnitude: 0.15,
1885
1952
  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.`
@@ -1897,7 +1964,8 @@ var P55_ArbitrageThermometer = {
1897
1964
  warning: thresholds.arbitrageIndexWarning
1898
1965
  },
1899
1966
  suggestedAction: {
1900
- parameter: "transactionFee",
1967
+ parameterType: "fee",
1968
+ scope: { tags: ["transaction"] },
1901
1969
  direction: "decrease",
1902
1970
  magnitude: 0.08,
1903
1971
  reasoning: `Arbitrage index ${arbitrageIndex.toFixed(2)} above warning threshold (${thresholds.arbitrageIndexWarning}). Early sign of price divergence. Gently reduce friction to support self-correction.`
@@ -1925,7 +1993,8 @@ var P59_GiftEconomyNoise = {
1925
1993
  threshold: thresholds.giftTradeFilterRatio
1926
1994
  },
1927
1995
  suggestedAction: {
1928
- parameter: "transactionFee",
1996
+ parameterType: "fee",
1997
+ scope: { tags: ["transaction"] },
1929
1998
  direction: "increase",
1930
1999
  magnitude: 0.05,
1931
2000
  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.`
@@ -1968,9 +2037,9 @@ var P42_TheMedianPrinciple = {
1968
2037
  medianBalance
1969
2038
  },
1970
2039
  suggestedAction: {
1971
- parameter: "transactionFee",
2040
+ parameterType: "fee",
2041
+ scope: { tags: ["transaction"], currency: curr },
1972
2042
  direction: "increase",
1973
- currency: curr,
1974
2043
  magnitude: 0.15,
1975
2044
  reasoning: `[${curr}] 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.`
1976
2045
  },
@@ -1995,7 +2064,7 @@ var P43_SimulationMinimum = {
1995
2064
  severity: 3,
1996
2065
  evidence: { inflationRate, minIterations: thresholds.simulationMinIterations },
1997
2066
  suggestedAction: {
1998
- parameter: "productionCost",
2067
+ parameterType: "cost",
1999
2068
  direction: inflationRate > 0 ? "increase" : "decrease",
2000
2069
  magnitude: 0.05,
2001
2070
  reasoning: `Large inflation rate swing (${(inflationRate * 100).toFixed(0)}%). Ensure all decisions use \u2265${thresholds.simulationMinIterations} simulation iterations. Apply conservative correction.`
@@ -2033,7 +2102,7 @@ var P39_TheLagPrinciple = {
2033
2102
  severity: 5,
2034
2103
  evidence: { inflationRate, netFlow, lagRange: [lagMin, lagMax] },
2035
2104
  suggestedAction: {
2036
- parameter: "productionCost",
2105
+ parameterType: "cost",
2037
2106
  direction: "increase",
2038
2107
  magnitude: 0.03,
2039
2108
  // very small — oscillation means over-adjusting
@@ -2060,7 +2129,8 @@ var P44_ComplexityBudget = {
2060
2129
  severity: 3,
2061
2130
  evidence: { customMetricCount, budgetMax: thresholds.complexityBudgetMax },
2062
2131
  suggestedAction: {
2063
- parameter: "transactionFee",
2132
+ parameterType: "fee",
2133
+ scope: { tags: ["transaction"] },
2064
2134
  direction: "decrease",
2065
2135
  magnitude: 0.01,
2066
2136
  reasoning: `${customMetricCount} custom metrics tracked (budget: ${thresholds.complexityBudgetMax}). Consider pruning low-impact parameters. Applying minimal correction to avoid adding complexity.`
@@ -2092,7 +2162,8 @@ var P35_DestructionCreatesValue = {
2092
2162
  severity: 6,
2093
2163
  evidence: { resource, supply, sinkVolume, netFlow },
2094
2164
  suggestedAction: {
2095
- parameter: "entryFee",
2165
+ parameterType: "fee",
2166
+ scope: { tags: ["entry"] },
2096
2167
  direction: "decrease",
2097
2168
  magnitude: 0.1,
2098
2169
  reasoning: `${resource} supply at ${supply} units with low destruction (sink ${sinkVolume}/t). Resources not being consumed. Lower competitive pool entry to increase resource usage.`
@@ -2120,7 +2191,7 @@ var P40_ReplacementRate = {
2120
2191
  severity: 6,
2121
2192
  evidence: { productionIndex, sinkVolume, replacementRatio },
2122
2193
  suggestedAction: {
2123
- parameter: "yieldRate",
2194
+ parameterType: "yield",
2124
2195
  direction: "increase",
2125
2196
  magnitude: 0.15,
2126
2197
  reasoning: `Replacement rate ${replacementRatio.toFixed(2)} (need \u2265${thresholds.replacementRateMultiplier}). Production below consumption. Resources will deplete. Increase yield.`
@@ -2134,7 +2205,7 @@ var P40_ReplacementRate = {
2134
2205
  severity: 3,
2135
2206
  evidence: { productionIndex, sinkVolume, replacementRatio },
2136
2207
  suggestedAction: {
2137
- parameter: "yieldRate",
2208
+ parameterType: "yield",
2138
2209
  direction: "decrease",
2139
2210
  magnitude: 0.1,
2140
2211
  reasoning: `Replacement rate ${replacementRatio.toFixed(2)} \u2014 overproducing. Production far exceeds consumption. Reduce yield to prevent glut.`
@@ -2160,7 +2231,8 @@ var P49_IdleAssetTax = {
2160
2231
  severity: 5,
2161
2232
  evidence: { giniCoefficient, top10PctShare, velocity },
2162
2233
  suggestedAction: {
2163
- parameter: "transactionFee",
2234
+ parameterType: "fee",
2235
+ scope: { tags: ["transaction"] },
2164
2236
  direction: "increase",
2165
2237
  magnitude: 0.15,
2166
2238
  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.`
@@ -2178,11 +2250,11 @@ var RESOURCE_MGMT_PRINCIPLES = [
2178
2250
  P49_IdleAssetTax
2179
2251
  ];
2180
2252
 
2181
- // src/principles/player-experience.ts
2253
+ // src/principles/participant-experience.ts
2182
2254
  var P33_FairNotEqual = {
2183
2255
  id: "P33",
2184
2256
  name: "Fair \u2260 Equal",
2185
- category: "player_experience",
2257
+ category: "participant_experience",
2186
2258
  description: "Gini = 0 is boring \u2014 everyone has the same and there is nothing to strive for. Healthy inequality from skill/effort is fine. Inequality from money (pay-to-win) is toxic. Target Gini 0.3-0.45: meaningful spread, not oligarchy.",
2187
2259
  check(metrics, thresholds) {
2188
2260
  for (const curr of metrics.currencies) {
@@ -2193,9 +2265,9 @@ var P33_FairNotEqual = {
2193
2265
  severity: 3,
2194
2266
  evidence: { currency: curr, giniCoefficient },
2195
2267
  suggestedAction: {
2196
- parameter: "rewardRate",
2268
+ parameterType: "reward",
2197
2269
  direction: "increase",
2198
- currency: curr,
2270
+ scope: { currency: curr },
2199
2271
  magnitude: 0.1,
2200
2272
  reasoning: `[${curr}] Gini ${giniCoefficient.toFixed(2)} \u2014 near-perfect equality. Economy lacks stakes. Increase winner rewards to create meaningful spread.`
2201
2273
  },
@@ -2209,9 +2281,9 @@ var P33_FairNotEqual = {
2209
2281
  severity: 7,
2210
2282
  evidence: { currency: curr, giniCoefficient },
2211
2283
  suggestedAction: {
2212
- parameter: "transactionFee",
2284
+ parameterType: "fee",
2213
2285
  direction: "increase",
2214
- currency: curr,
2286
+ scope: { tags: ["transaction"], currency: curr },
2215
2287
  magnitude: 0.2,
2216
2288
  reasoning: `[${curr}] Gini ${giniCoefficient.toFixed(2)} \u2014 oligarchy level. Toxic inequality. Raise transaction fees to redistribute wealth from rich to pool.`
2217
2289
  },
@@ -2225,9 +2297,9 @@ var P33_FairNotEqual = {
2225
2297
  severity: 4,
2226
2298
  evidence: { currency: curr, giniCoefficient },
2227
2299
  suggestedAction: {
2228
- parameter: "transactionFee",
2300
+ parameterType: "fee",
2229
2301
  direction: "increase",
2230
- currency: curr,
2302
+ scope: { tags: ["transaction"], currency: curr },
2231
2303
  magnitude: 0.1,
2232
2304
  reasoning: `[${curr}] Gini ${giniCoefficient.toFixed(2)} \u2014 high inequality warning. Gently raise fees to slow wealth concentration.`
2233
2305
  },
@@ -2241,9 +2313,9 @@ var P33_FairNotEqual = {
2241
2313
  };
2242
2314
  var P36_MechanicFrictionDetector = {
2243
2315
  id: "P36",
2244
- name: "Mechanic Friction Detector",
2245
- category: "player_experience",
2246
- description: "Deterministic + probabilistic systems \u2192 expectation mismatch. When crafting is guaranteed but combat is random, players feel betrayed by the random side. Mix mechanics carefully or segregate them entirely.",
2316
+ name: "Mechanism Friction Detector",
2317
+ category: "participant_experience",
2318
+ description: "Deterministic + probabilistic systems \u2192 expectation mismatch. When production is guaranteed but competition is random, participants feel betrayed by the random side. Mix mechanisms carefully or segregate them entirely.",
2247
2319
  check(metrics, _thresholds) {
2248
2320
  const { avgSatisfaction, churnRate, velocity } = metrics;
2249
2321
  if (churnRate > 0.1 && avgSatisfaction < 50 && velocity > 3) {
@@ -2252,10 +2324,10 @@ var P36_MechanicFrictionDetector = {
2252
2324
  severity: 5,
2253
2325
  evidence: { churnRate, avgSatisfaction, velocity },
2254
2326
  suggestedAction: {
2255
- parameter: "rewardRate",
2327
+ parameterType: "reward",
2256
2328
  direction: "increase",
2257
2329
  magnitude: 0.15,
2258
- 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."
2330
+ reasoning: `Churn ${(churnRate * 100).toFixed(1)}% with satisfaction ${avgSatisfaction.toFixed(0)} despite active economy (velocity ` + velocity.toFixed(1) + "). Suggests mechanism friction (deterministic vs random systems). Increase rewards to compensate for perceived unfairness. ADVISORY: Review if mixing guaranteed and probabilistic mechanisms."
2259
2331
  },
2260
2332
  confidence: 0.55,
2261
2333
  estimatedLag: 15
@@ -2266,8 +2338,8 @@ var P36_MechanicFrictionDetector = {
2266
2338
  };
2267
2339
  var P37_LatecommerProblem = {
2268
2340
  id: "P37",
2269
- name: "Latecomer Problem",
2270
- category: "player_experience",
2341
+ name: "Late Entrant Problem",
2342
+ category: "participant_experience",
2271
2343
  description: "A new participant must reach viability in reasonable time. If all the good roles are saturated and prices are high, new agents cannot contribute and churn immediately.",
2272
2344
  check(metrics, _thresholds) {
2273
2345
  const { timeToValue, avgSatisfaction, churnRate } = metrics;
@@ -2277,7 +2349,7 @@ var P37_LatecommerProblem = {
2277
2349
  severity: 6,
2278
2350
  evidence: { timeToValue, avgSatisfaction, churnRate },
2279
2351
  suggestedAction: {
2280
- parameter: "productionCost",
2352
+ parameterType: "cost",
2281
2353
  direction: "decrease",
2282
2354
  magnitude: 0.15,
2283
2355
  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.`
@@ -2292,7 +2364,7 @@ var P37_LatecommerProblem = {
2292
2364
  var P45_TimeBudget = {
2293
2365
  id: "P45",
2294
2366
  name: "Time Budget",
2295
- category: "player_experience",
2367
+ category: "participant_experience",
2296
2368
  description: "required_time \u2264 available_time \xD7 0.8. If the economy requires more engagement than participants can realistically give, it is a disguised paywall. The 0.8 buffer accounts for real life.",
2297
2369
  check(metrics, thresholds) {
2298
2370
  const { timeToValue, avgSatisfaction } = metrics;
@@ -2304,8 +2376,9 @@ var P45_TimeBudget = {
2304
2376
  severity: 5,
2305
2377
  evidence: { timeToValue, avgSatisfaction, timeBudgetRatio: thresholds.timeBudgetRatio },
2306
2378
  suggestedAction: {
2307
- parameter: "entryFee",
2379
+ parameterType: "fee",
2308
2380
  direction: "decrease",
2381
+ scope: { tags: ["entry"] },
2309
2382
  magnitude: 0.15,
2310
2383
  reasoning: `Time-to-value ${timeToValue} ticks with ${avgSatisfaction.toFixed(0)} satisfaction. Economy requires too much time investment. Lower barriers to participation.`
2311
2384
  },
@@ -2319,7 +2392,7 @@ var P45_TimeBudget = {
2319
2392
  var P50_PayPowerRatio = {
2320
2393
  id: "P50",
2321
2394
  name: "Pay-Power Ratio",
2322
- category: "player_experience",
2395
+ category: "participant_experience",
2323
2396
  description: "spender / non-spender power ratio > 2.0 = pay-to-win territory. Target 1.5 (meaningful advantage without shutting out non-payers). Above 2.0, non-paying participants start leaving.",
2324
2397
  check(metrics, thresholds) {
2325
2398
  const { top10PctShare, giniCoefficient } = metrics;
@@ -2334,8 +2407,9 @@ var P50_PayPowerRatio = {
2334
2407
  threshold: thresholds.payPowerRatioMax
2335
2408
  },
2336
2409
  suggestedAction: {
2337
- parameter: "transactionFee",
2410
+ parameterType: "fee",
2338
2411
  direction: "increase",
2412
+ scope: { tags: ["transaction"] },
2339
2413
  magnitude: 0.2,
2340
2414
  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.`
2341
2415
  },
@@ -2346,7 +2420,7 @@ var P50_PayPowerRatio = {
2346
2420
  return { violated: false };
2347
2421
  }
2348
2422
  };
2349
- var PLAYER_EXPERIENCE_PRINCIPLES = [
2423
+ var PARTICIPANT_EXPERIENCE_PRINCIPLES = [
2350
2424
  P33_FairNotEqual,
2351
2425
  P36_MechanicFrictionDetector,
2352
2426
  P37_LatecommerProblem,
@@ -2369,7 +2443,8 @@ var P34_ExtractionRatio = {
2369
2443
  severity: 8,
2370
2444
  evidence: { extractionRatio, threshold: thresholds.extractionRatioRed },
2371
2445
  suggestedAction: {
2372
- parameter: "transactionFee",
2446
+ parameterType: "fee",
2447
+ scope: { tags: ["transaction"] },
2373
2448
  direction: "increase",
2374
2449
  magnitude: 0.25,
2375
2450
  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.`
@@ -2384,7 +2459,8 @@ var P34_ExtractionRatio = {
2384
2459
  severity: 5,
2385
2460
  evidence: { extractionRatio, threshold: thresholds.extractionRatioYellow },
2386
2461
  suggestedAction: {
2387
- parameter: "transactionFee",
2462
+ parameterType: "fee",
2463
+ scope: { tags: ["transaction"] },
2388
2464
  direction: "increase",
2389
2465
  magnitude: 0.1,
2390
2466
  reasoning: `Extraction ratio ${(extractionRatio * 100).toFixed(0)}% (warning: ${(thresholds.extractionRatioYellow * 100).toFixed(0)}%). Economy trending toward extraction-heavy. Apply early pressure.`
@@ -2410,7 +2486,7 @@ var P47_SmokeTest = {
2410
2486
  severity: 9,
2411
2487
  evidence: { smokeTestRatio, threshold: thresholds.smokeTestCritical },
2412
2488
  suggestedAction: {
2413
- parameter: "rewardRate",
2489
+ parameterType: "reward",
2414
2490
  direction: "increase",
2415
2491
  magnitude: 0.2,
2416
2492
  reasoning: `Utility/market ratio ${(smokeTestRatio * 100).toFixed(0)}% (critical). Economy is >90% speculative. Collapse risk is extreme. Increase utility rewards to anchor real value.`
@@ -2425,7 +2501,7 @@ var P47_SmokeTest = {
2425
2501
  severity: 6,
2426
2502
  evidence: { smokeTestRatio, threshold: thresholds.smokeTestWarning },
2427
2503
  suggestedAction: {
2428
- parameter: "rewardRate",
2504
+ parameterType: "reward",
2429
2505
  direction: "increase",
2430
2506
  magnitude: 0.1,
2431
2507
  reasoning: `Utility/market ratio ${(smokeTestRatio * 100).toFixed(0)}% (warning). Economy is >70% speculative. Boost utility rewards to restore intrinsic value anchor.`
@@ -2451,7 +2527,8 @@ var P48_CurrencyInsulation = {
2451
2527
  severity: 6,
2452
2528
  evidence: { currencyInsulation, threshold: thresholds.currencyInsulationMax },
2453
2529
  suggestedAction: {
2454
- parameter: "transactionFee",
2530
+ parameterType: "fee",
2531
+ scope: { tags: ["transaction"] },
2455
2532
  direction: "increase",
2456
2533
  magnitude: 0.1,
2457
2534
  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.`
@@ -2469,12 +2546,12 @@ var OPEN_ECONOMY_PRINCIPLES = [
2469
2546
  P48_CurrencyInsulation
2470
2547
  ];
2471
2548
 
2472
- // src/principles/liveops.ts
2549
+ // src/principles/operations.ts
2473
2550
  var P51_SharkTooth = {
2474
2551
  id: "P51",
2475
- name: "Shark Tooth Pattern",
2476
- category: "liveops",
2477
- description: "Each event peak should be \u226595% of the previous peak. If peaks are shrinking (shark tooth becoming flat), event fatigue is setting in. If valleys are deepening, the off-event economy is failing to sustain engagement.",
2552
+ name: "Cyclical Engagement Pattern",
2553
+ category: "operations",
2554
+ description: "Each activity peak should be >=95% of the previous peak. If peaks are shrinking (cyclical engagement becoming flat), activity fatigue is setting in. If valleys are deepening, the off-activity economy is failing to sustain engagement.",
2478
2555
  check(metrics, thresholds) {
2479
2556
  const { sharkToothPeaks, sharkToothValleys } = metrics;
2480
2557
  if (sharkToothPeaks.length < 2) return { violated: false };
@@ -2491,10 +2568,10 @@ var P51_SharkTooth = {
2491
2568
  threshold: thresholds.sharkToothPeakDecay
2492
2569
  },
2493
2570
  suggestedAction: {
2494
- parameter: "rewardRate",
2571
+ parameterType: "reward",
2495
2572
  direction: "increase",
2496
2573
  magnitude: 0.1,
2497
- 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.`
2574
+ reasoning: `Peak engagement dropped to ${(lastPeak / prevPeak * 100).toFixed(0)}% of previous peak (threshold: ${(thresholds.sharkToothPeakDecay * 100).toFixed(0)}%). Activity fatigue detected. Boost activity rewards to restore peak engagement.`
2498
2575
  },
2499
2576
  confidence: 0.75,
2500
2577
  estimatedLag: 30
@@ -2509,10 +2586,10 @@ var P51_SharkTooth = {
2509
2586
  severity: 4,
2510
2587
  evidence: { lastValley, prevValley, ratio: lastValley / prevValley },
2511
2588
  suggestedAction: {
2512
- parameter: "productionCost",
2589
+ parameterType: "cost",
2513
2590
  direction: "decrease",
2514
2591
  magnitude: 0.1,
2515
- reasoning: "Between-event engagement declining (deepening valleys). Base economy not sustaining participants between events. Lower production costs to improve off-event value."
2592
+ reasoning: "Between-activity engagement declining (deepening valleys). Base economy not sustaining participants between activities. Lower production costs to improve off-activity value."
2516
2593
  },
2517
2594
  confidence: 0.65,
2518
2595
  estimatedLag: 20
@@ -2525,8 +2602,8 @@ var P51_SharkTooth = {
2525
2602
  var P52_EndowmentEffect = {
2526
2603
  id: "P52",
2527
2604
  name: "Endowment Effect",
2528
- category: "liveops",
2529
- 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).",
2605
+ category: "operations",
2606
+ description: "Participants who never owned premium items do not value them. Free trial activities that let participants experience premium items drive conversions because ownership creates perceived value (endowment effect).",
2530
2607
  check(metrics, _thresholds) {
2531
2608
  const { avgSatisfaction, churnRate } = metrics;
2532
2609
  const { eventCompletionRate } = metrics;
@@ -2537,10 +2614,10 @@ var P52_EndowmentEffect = {
2537
2614
  severity: 4,
2538
2615
  evidence: { eventCompletionRate, avgSatisfaction, churnRate },
2539
2616
  suggestedAction: {
2540
- parameter: "rewardRate",
2617
+ parameterType: "reward",
2541
2618
  direction: "increase",
2542
2619
  magnitude: 0.15,
2543
- reasoning: `${(eventCompletionRate * 100).toFixed(0)}% event completion but satisfaction only ${avgSatisfaction.toFixed(0)}. Events not creating perceived value. Increase reward quality/quantity.`
2620
+ reasoning: `${(eventCompletionRate * 100).toFixed(0)}% activity completion but satisfaction only ${avgSatisfaction.toFixed(0)}. Activities not creating perceived value. Increase reward quality/quantity.`
2544
2621
  },
2545
2622
  confidence: 0.6,
2546
2623
  estimatedLag: 20
@@ -2551,8 +2628,8 @@ var P52_EndowmentEffect = {
2551
2628
  };
2552
2629
  var P53_EventCompletionRate = {
2553
2630
  id: "P53",
2554
- name: "Event Completion Rate Sweet Spot",
2555
- category: "liveops",
2631
+ name: "Activity Completion Rate Sweet Spot",
2632
+ category: "operations",
2556
2633
  description: "Free completion at 60-80% is the sweet spot. <40% = predatory design. >80% = no monetization pressure. 100% free = zero reason to ever spend.",
2557
2634
  check(metrics, thresholds) {
2558
2635
  const { eventCompletionRate } = metrics;
@@ -2567,10 +2644,10 @@ var P53_EventCompletionRate = {
2567
2644
  max: thresholds.eventCompletionMax
2568
2645
  },
2569
2646
  suggestedAction: {
2570
- parameter: "productionCost",
2647
+ parameterType: "cost",
2571
2648
  direction: "decrease",
2572
2649
  magnitude: 0.15,
2573
- 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.`
2650
+ reasoning: `Activity completion rate ${(eventCompletionRate * 100).toFixed(0)}% \u2014 predatory territory (min: ${(thresholds.eventCompletionMin * 100).toFixed(0)}%). Too hard for free participants. Lower barriers to participation.`
2574
2651
  },
2575
2652
  confidence: 0.8,
2576
2653
  estimatedLag: 10
@@ -2582,10 +2659,11 @@ var P53_EventCompletionRate = {
2582
2659
  severity: 3,
2583
2660
  evidence: { eventCompletionRate, max: thresholds.eventCompletionMax },
2584
2661
  suggestedAction: {
2585
- parameter: "entryFee",
2662
+ parameterType: "fee",
2663
+ scope: { tags: ["entry"] },
2586
2664
  direction: "increase",
2587
2665
  magnitude: 0.05,
2588
- 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.`
2666
+ reasoning: `Activity completion rate ${(eventCompletionRate * 100).toFixed(0)}% \u2014 no monetization pressure (max: ${(thresholds.eventCompletionMax * 100).toFixed(0)}%). Slightly raise costs to create meaningful premium differentiation.`
2589
2667
  },
2590
2668
  confidence: 0.55,
2591
2669
  estimatedLag: 10
@@ -2596,9 +2674,9 @@ var P53_EventCompletionRate = {
2596
2674
  };
2597
2675
  var P54_LiveOpsCadence = {
2598
2676
  id: "P54",
2599
- name: "LiveOps Cadence",
2600
- category: "liveops",
2601
- description: ">50% of events that are re-wrapped existing content \u2192 staleness. The cadence must include genuinely new content at regular intervals. This is an advisory principle \u2014 AgentE can flag but cannot fix content.",
2677
+ name: "Operational Cadence",
2678
+ category: "operations",
2679
+ description: ">50% of activities that are re-wrapped existing content \u2192 staleness. The cadence must include genuinely new content at regular intervals. This is an advisory principle \u2014 AgentE can flag but cannot fix content.",
2602
2680
  check(metrics, _thresholds) {
2603
2681
  const { velocity, avgSatisfaction } = metrics;
2604
2682
  if (velocity < 2 && avgSatisfaction < 55 && metrics.tick > 100) {
@@ -2607,7 +2685,7 @@ var P54_LiveOpsCadence = {
2607
2685
  severity: 3,
2608
2686
  evidence: { velocity, avgSatisfaction, tick: metrics.tick },
2609
2687
  suggestedAction: {
2610
- parameter: "rewardRate",
2688
+ parameterType: "reward",
2611
2689
  direction: "increase",
2612
2690
  magnitude: 0.1,
2613
2691
  reasoning: "Low velocity and satisfaction after long runtime. Possible content staleness. Increase rewards as bridge while new content is developed (developer action required)."
@@ -2621,8 +2699,8 @@ var P54_LiveOpsCadence = {
2621
2699
  };
2622
2700
  var P56_ContentDropShock = {
2623
2701
  id: "P56",
2624
- name: "Content-Drop Shock",
2625
- category: "liveops",
2702
+ name: "Supply Shock Absorption",
2703
+ category: "operations",
2626
2704
  description: "Every new-item injection shatters existing price equilibria \u2014 arbitrage spikes as participants re-price. Build cooldown windows for price discovery before measuring post-drop economic health.",
2627
2705
  check(metrics, thresholds) {
2628
2706
  const { contentDropAge, arbitrageIndex } = metrics;
@@ -2638,7 +2716,8 @@ var P56_ContentDropShock = {
2638
2716
  postDropMax: thresholds.postDropArbitrageMax
2639
2717
  },
2640
2718
  suggestedAction: {
2641
- parameter: "transactionFee",
2719
+ parameterType: "fee",
2720
+ scope: { tags: ["transaction"] },
2642
2721
  direction: "decrease",
2643
2722
  magnitude: 0.1,
2644
2723
  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.`
@@ -2651,7 +2730,7 @@ var P56_ContentDropShock = {
2651
2730
  return { violated: false };
2652
2731
  }
2653
2732
  };
2654
- var LIVEOPS_PRINCIPLES = [
2733
+ var OPERATIONS_PRINCIPLES = [
2655
2734
  P51_SharkTooth,
2656
2735
  P52_EndowmentEffect,
2657
2736
  P53_EventCompletionRate,
@@ -2685,23 +2764,24 @@ var ALL_PRINCIPLES = [
2685
2764
  // P39, P44
2686
2765
  ...RESOURCE_MGMT_PRINCIPLES,
2687
2766
  // P35, P40, P49
2688
- ...PLAYER_EXPERIENCE_PRINCIPLES,
2767
+ ...PARTICIPANT_EXPERIENCE_PRINCIPLES,
2689
2768
  // P33, P36, P37, P45, P50
2690
2769
  ...OPEN_ECONOMY_PRINCIPLES,
2691
2770
  // P34, P47-P48
2692
- ...LIVEOPS_PRINCIPLES
2771
+ ...OPERATIONS_PRINCIPLES
2693
2772
  // P51-P54, P56
2694
2773
  ];
2695
2774
 
2696
2775
  // src/Simulator.ts
2697
2776
  var Simulator = class {
2698
- constructor() {
2777
+ constructor(registry) {
2699
2778
  this.diagnoser = new Diagnoser(ALL_PRINCIPLES);
2700
2779
  // Cache beforeViolations for the *current* tick only (one entry max).
2701
2780
  // Using a Map here is intentional but the cache must be bounded — we only
2702
2781
  // care about the tick that is currently being evaluated, so we evict any
2703
2782
  // entries whose key differs from the incoming tick.
2704
2783
  this.beforeViolationsCache = /* @__PURE__ */ new Map();
2784
+ this.registry = registry;
2705
2785
  }
2706
2786
  /**
2707
2787
  * Simulate the effect of applying `action` to the current economy forward `forwardTicks`.
@@ -2768,7 +2848,7 @@ var Simulator = class {
2768
2848
  const multiplier = this.actionMultiplier(action);
2769
2849
  const noise = () => 1 + (Math.random() - 0.5) * 0.1;
2770
2850
  const currencies = metrics.currencies;
2771
- const targetCurrency = action.currency;
2851
+ const targetCurrency = action.scope?.currency;
2772
2852
  const supplies = { ...metrics.totalSupplyByCurrency };
2773
2853
  const netFlows = { ...metrics.netFlowByCurrency };
2774
2854
  const ginis = { ...metrics.giniCoefficientByCurrency };
@@ -2810,26 +2890,48 @@ var Simulator = class {
2810
2890
  return action.direction === "increase" ? 1 + base : 1 - base;
2811
2891
  }
2812
2892
  flowEffect(action, metrics, currency) {
2813
- const { parameter, direction } = action;
2893
+ const { direction } = action;
2814
2894
  const sign = direction === "increase" ? -1 : 1;
2815
2895
  const roleEntries = Object.entries(metrics.populationByRole).sort((a, b) => b[1] - a[1]);
2816
2896
  const dominantRoleCount = roleEntries[0]?.[1] ?? 0;
2817
- if (parameter === "productionCost") {
2818
- return sign * (metrics.netFlowByCurrency[currency] ?? 0) * 0.2;
2819
- }
2820
- if (parameter === "transactionFee") {
2821
- return sign * (metrics.velocityByCurrency[currency] ?? 0) * 10 * 0.1;
2822
- }
2823
- if (parameter === "entryFee") {
2824
- return sign * dominantRoleCount * 0.5;
2897
+ const resolvedKey = action.resolvedParameter;
2898
+ let impact;
2899
+ if (resolvedKey && this.registry) {
2900
+ impact = this.registry.getFlowImpact(resolvedKey);
2901
+ }
2902
+ if (!impact) {
2903
+ impact = this.inferFlowImpact(action.parameterType);
2904
+ }
2905
+ switch (impact) {
2906
+ case "sink":
2907
+ return sign * (metrics.netFlowByCurrency[currency] ?? 0) * 0.2;
2908
+ case "faucet":
2909
+ return -sign * dominantRoleCount * 0.3;
2910
+ case "neutral":
2911
+ return sign * dominantRoleCount * 0.5;
2912
+ case "mixed":
2913
+ return sign * (metrics.faucetVolumeByCurrency[currency] ?? 0) * 0.15;
2914
+ default:
2915
+ return sign * (metrics.netFlowByCurrency[currency] ?? 0) * 0.1;
2916
+ }
2917
+ }
2918
+ /** Infer flow impact from parameter type when registry is unavailable */
2919
+ inferFlowImpact(parameterType) {
2920
+ switch (parameterType) {
2921
+ case "cost":
2922
+ case "fee":
2923
+ case "penalty":
2924
+ return "sink";
2925
+ case "reward":
2926
+ return "faucet";
2927
+ case "yield":
2928
+ return "mixed";
2929
+ case "cap":
2930
+ case "multiplier":
2931
+ return "neutral";
2932
+ default:
2933
+ return "mixed";
2825
2934
  }
2826
- if (parameter === "rewardRate") {
2827
- return -sign * dominantRoleCount * 0.3;
2828
- }
2829
- if (parameter === "yieldRate") {
2830
- return sign * (metrics.faucetVolumeByCurrency[currency] ?? 0) * 0.15;
2831
- }
2832
- return sign * (metrics.netFlowByCurrency[currency] ?? 0) * 0.1;
2833
2935
  }
2834
2936
  checkImprovement(before, after, action) {
2835
2937
  const satisfactionImproved = after.avgSatisfaction >= before.avgSatisfaction - 2;
@@ -2909,16 +3011,30 @@ var Planner = class {
2909
3011
  * - parameter is still in cooldown
2910
3012
  * - simulation result failed
2911
3013
  * - complexity budget exceeded
3014
+ * - no matching parameter in registry
2912
3015
  */
2913
- plan(diagnosis, metrics, simulationResult, currentParams, thresholds) {
3016
+ plan(diagnosis, metrics, simulationResult, currentParams, thresholds, registry) {
2914
3017
  const action = diagnosis.violation.suggestedAction;
2915
- const param = action.parameter;
3018
+ let param;
3019
+ let resolvedBaseline;
3020
+ let scope;
3021
+ if (registry) {
3022
+ const resolved = registry.resolve(action.parameterType, action.scope);
3023
+ if (!resolved) return null;
3024
+ param = resolved.key;
3025
+ resolvedBaseline = resolved.currentValue;
3026
+ scope = resolved.scope;
3027
+ action.resolvedParameter = param;
3028
+ } else {
3029
+ param = action.resolvedParameter ?? action.parameterType;
3030
+ scope = action.scope;
3031
+ }
2916
3032
  if (this.lockedParams.has(param)) return null;
2917
3033
  if (this.isOnCooldown(param, metrics.tick, thresholds.cooldownTicks)) return null;
2918
3034
  if (!simulationResult.netImprovement) return null;
2919
3035
  if (!simulationResult.noNewProblems) return null;
2920
3036
  if (this.activePlanCount >= thresholds.complexityBudgetMax) return null;
2921
- const currentValue = currentParams[param] ?? 1;
3037
+ const currentValue = resolvedBaseline ?? currentParams[param] ?? action.absoluteValue ?? 1;
2922
3038
  const magnitude = Math.min(action.magnitude ?? 0.1, thresholds.maxAdjustmentPercent);
2923
3039
  let targetValue;
2924
3040
  if (action.direction === "set" && action.absoluteValue !== void 0) {
@@ -2938,7 +3054,7 @@ var Planner = class {
2938
3054
  id: `plan_${metrics.tick}_${param}`,
2939
3055
  diagnosis,
2940
3056
  parameter: param,
2941
- ...action.currency !== void 0 ? { currency: action.currency } : {},
3057
+ ...scope !== void 0 ? { scope } : {},
2942
3058
  currentValue,
2943
3059
  targetValue,
2944
3060
  maxChangePercent: thresholds.maxAdjustmentPercent,
@@ -2947,7 +3063,6 @@ var Planner = class {
2947
3063
  metric: "avgSatisfaction",
2948
3064
  direction: "below",
2949
3065
  threshold: Math.max(20, metrics.avgSatisfaction - 10),
2950
- // rollback if sat drops >10 pts
2951
3066
  checkAfterTick: metrics.tick + estimatedLag + 3
2952
3067
  },
2953
3068
  simulationResult,
@@ -2962,6 +3077,9 @@ var Planner = class {
2962
3077
  recordRolledBack(_plan) {
2963
3078
  this.activePlanCount = Math.max(0, this.activePlanCount - 1);
2964
3079
  }
3080
+ recordSettled(_plan) {
3081
+ this.activePlanCount = Math.max(0, this.activePlanCount - 1);
3082
+ }
2965
3083
  isOnCooldown(param, currentTick, cooldownTicks) {
2966
3084
  const lastApplied = this.cooldowns.get(param);
2967
3085
  if (lastApplied === void 0) return false;
@@ -2980,40 +3098,54 @@ var Executor = class {
2980
3098
  }
2981
3099
  async apply(plan, adapter, currentParams) {
2982
3100
  const originalValue = currentParams[plan.parameter] ?? plan.currentValue;
2983
- await adapter.setParam(plan.parameter, plan.targetValue, plan.currency);
3101
+ await adapter.setParam(plan.parameter, plan.targetValue, plan.scope);
2984
3102
  plan.appliedAt = plan.diagnosis.tick;
2985
3103
  this.activePlans.push({ plan, originalValue });
2986
3104
  }
2987
3105
  /**
2988
3106
  * Check all active plans for rollback conditions.
2989
- * Called every tick after metrics are computed.
2990
- * Returns list of plans that were rolled back.
3107
+ * Returns { rolledBack, settled } plans that were undone and plans that passed their window.
2991
3108
  */
2992
3109
  async checkRollbacks(metrics, adapter) {
2993
3110
  const rolledBack = [];
3111
+ const settled = [];
2994
3112
  const remaining = [];
2995
3113
  for (const active of this.activePlans) {
2996
3114
  const { plan, originalValue } = active;
2997
3115
  const rc = plan.rollbackCondition;
3116
+ const maxActiveTicks = 200;
3117
+ if (plan.appliedAt !== void 0 && metrics.tick - plan.appliedAt > maxActiveTicks) {
3118
+ settled.push(plan);
3119
+ continue;
3120
+ }
2998
3121
  if (metrics.tick < rc.checkAfterTick) {
2999
3122
  remaining.push(active);
3000
3123
  continue;
3001
3124
  }
3002
3125
  const metricValue = this.getMetricValue(metrics, rc.metric);
3126
+ if (Number.isNaN(metricValue)) {
3127
+ console.warn(
3128
+ `[AgentE] Rollback check: metric path '${rc.metric}' resolved to NaN for plan '${plan.id}'. Triggering rollback as fail-safe.`
3129
+ );
3130
+ await adapter.setParam(plan.parameter, originalValue, plan.scope);
3131
+ rolledBack.push(plan);
3132
+ continue;
3133
+ }
3003
3134
  const shouldRollback = rc.direction === "below" ? metricValue < rc.threshold : metricValue > rc.threshold;
3004
3135
  if (shouldRollback) {
3005
- await adapter.setParam(plan.parameter, originalValue, plan.currency);
3136
+ await adapter.setParam(plan.parameter, originalValue, plan.scope);
3006
3137
  rolledBack.push(plan);
3007
3138
  } else {
3008
3139
  const settledTick = rc.checkAfterTick + 10;
3009
3140
  if (metrics.tick > settledTick) {
3141
+ settled.push(plan);
3010
3142
  } else {
3011
3143
  remaining.push(active);
3012
3144
  }
3013
3145
  }
3014
3146
  }
3015
3147
  this.activePlans = remaining;
3016
- return rolledBack;
3148
+ return { rolledBack, settled };
3017
3149
  }
3018
3150
  getMetricValue(metrics, metricPath) {
3019
3151
  const parts = metricPath.split(".");
@@ -3102,7 +3234,8 @@ var DecisionLog = class {
3102
3234
  return {
3103
3235
  id: `stub_${metrics.tick}`,
3104
3236
  diagnosis,
3105
- parameter: action.parameter,
3237
+ parameter: action.resolvedParameter ?? action.parameterType,
3238
+ ...action.scope !== void 0 ? { scope: action.scope } : {},
3106
3239
  currentValue: 1,
3107
3240
  targetValue: 1,
3108
3241
  maxChangePercent: 0,
@@ -3340,13 +3473,13 @@ var PersonaTracker = class {
3340
3473
  Earner: 0,
3341
3474
  Builder: 0,
3342
3475
  Social: 0,
3343
- Whale: 0,
3476
+ HighValue: 0,
3344
3477
  Influencer: 0
3345
3478
  };
3346
3479
  let total = 0;
3347
3480
  for (const [, history] of this.agentHistory) {
3348
3481
  const persona = this.classify(history);
3349
- counts[persona]++;
3482
+ counts[persona] = (counts[persona] ?? 0) + 1;
3350
3483
  total++;
3351
3484
  }
3352
3485
  if (total === 0) return {};
@@ -3366,7 +3499,7 @@ var PersonaTracker = class {
3366
3499
  const extraction = avg("netExtraction");
3367
3500
  const uniqueItems = avg("uniqueItemsHeld");
3368
3501
  const spend = avg("spendAmount");
3369
- if (spend > 1e3) return "Whale";
3502
+ if (spend > 1e3) return "HighValue";
3370
3503
  if (txRate > 10) return "Trader";
3371
3504
  if (uniqueItems > 5 && extraction < 0) return "Collector";
3372
3505
  if (extraction > 100) return "Earner";
@@ -3375,12 +3508,116 @@ var PersonaTracker = class {
3375
3508
  }
3376
3509
  };
3377
3510
 
3511
+ // src/ParameterRegistry.ts
3512
+ var ParameterRegistry = class {
3513
+ constructor() {
3514
+ this.parameters = /* @__PURE__ */ new Map();
3515
+ }
3516
+ /** Register a parameter. Overwrites if key already exists. */
3517
+ register(param) {
3518
+ this.parameters.set(param.key, { ...param });
3519
+ }
3520
+ /** Register multiple parameters at once. */
3521
+ registerAll(params) {
3522
+ for (const p of params) this.register(p);
3523
+ }
3524
+ /**
3525
+ * Resolve a parameterType + scope to a concrete RegisteredParameter.
3526
+ * Returns the best match, or undefined if no match.
3527
+ *
3528
+ * Matching rules (in priority order):
3529
+ * 1. Exact type match + all scope fields match
3530
+ * 2. Exact type match + partial scope match (tags overlap)
3531
+ * 3. Exact type match + no scope constraints
3532
+ * 4. undefined (no match)
3533
+ */
3534
+ resolve(type, scope) {
3535
+ const candidates = this.findByType(type);
3536
+ if (candidates.length === 0) return void 0;
3537
+ if (candidates.length === 1) return candidates[0];
3538
+ let bestScore = -1;
3539
+ let best;
3540
+ for (const candidate of candidates) {
3541
+ const score = this.scopeMatchScore(candidate.scope, scope);
3542
+ if (score > bestScore) {
3543
+ bestScore = score;
3544
+ best = candidate;
3545
+ }
3546
+ }
3547
+ return best;
3548
+ }
3549
+ /** Find all parameters of a given type. */
3550
+ findByType(type) {
3551
+ const results = [];
3552
+ for (const param of this.parameters.values()) {
3553
+ if (param.type === type) results.push(param);
3554
+ }
3555
+ return results;
3556
+ }
3557
+ /** Find all parameters belonging to a given system. */
3558
+ findBySystem(system) {
3559
+ const results = [];
3560
+ for (const param of this.parameters.values()) {
3561
+ if (param.scope?.system === system) results.push(param);
3562
+ }
3563
+ return results;
3564
+ }
3565
+ /** Get a parameter by its concrete key. */
3566
+ get(key) {
3567
+ return this.parameters.get(key);
3568
+ }
3569
+ /** Get the flow impact of a parameter by its concrete key. */
3570
+ getFlowImpact(key) {
3571
+ return this.parameters.get(key)?.flowImpact;
3572
+ }
3573
+ /** Update the current value of a registered parameter. */
3574
+ updateValue(key, value) {
3575
+ const param = this.parameters.get(key);
3576
+ if (param) {
3577
+ param.currentValue = value;
3578
+ }
3579
+ }
3580
+ /** Get all registered parameters. */
3581
+ getAll() {
3582
+ return [...this.parameters.values()];
3583
+ }
3584
+ /** Number of registered parameters. */
3585
+ get size() {
3586
+ return this.parameters.size;
3587
+ }
3588
+ // ── Private ─────────────────────────────────────────────────────────────
3589
+ scopeMatchScore(paramScope, queryScope) {
3590
+ if (!queryScope) return 0;
3591
+ if (!paramScope) return 0;
3592
+ let score = 0;
3593
+ if (queryScope.system && paramScope.system) {
3594
+ if (queryScope.system === paramScope.system) score += 10;
3595
+ else return -1;
3596
+ }
3597
+ if (queryScope.currency && paramScope.currency) {
3598
+ if (queryScope.currency === paramScope.currency) score += 5;
3599
+ else return -1;
3600
+ }
3601
+ if (queryScope.tags && queryScope.tags.length > 0 && paramScope.tags && paramScope.tags.length > 0) {
3602
+ const overlap = queryScope.tags.filter((t) => paramScope.tags.includes(t)).length;
3603
+ if (overlap > 0) {
3604
+ score += overlap * 3;
3605
+ } else {
3606
+ return -1;
3607
+ }
3608
+ } else if (queryScope.tags && queryScope.tags.length > 0 && paramScope.tags && paramScope.tags.length > 0) {
3609
+ return -1;
3610
+ }
3611
+ return score;
3612
+ }
3613
+ };
3614
+
3378
3615
  // src/AgentE.ts
3379
3616
  var AgentE = class {
3380
3617
  constructor(config) {
3381
- this.simulator = new Simulator();
3382
3618
  this.planner = new Planner();
3383
3619
  this.executor = new Executor();
3620
+ this.registry = new ParameterRegistry();
3384
3621
  // ── State ──
3385
3622
  this.log = new DecisionLog();
3386
3623
  this.personaTracker = new PersonaTracker();
@@ -3405,7 +3642,8 @@ var AgentE = class {
3405
3642
  gracePeriod: config.gracePeriod ?? 50,
3406
3643
  checkInterval: config.checkInterval ?? 5,
3407
3644
  maxAdjustmentPercent: config.maxAdjustmentPercent ?? 0.15,
3408
- cooldownTicks: config.cooldownTicks ?? 15
3645
+ cooldownTicks: config.cooldownTicks ?? 15,
3646
+ parameters: config.parameters ?? []
3409
3647
  };
3410
3648
  this.thresholds = {
3411
3649
  ...DEFAULT_THRESHOLDS,
@@ -3417,6 +3655,10 @@ var AgentE = class {
3417
3655
  this.observer = new Observer(tickConfig);
3418
3656
  this.store = new MetricStore(tickConfig);
3419
3657
  this.diagnoser = new Diagnoser(ALL_PRINCIPLES);
3658
+ if (config.parameters) {
3659
+ this.registry.registerAll(config.parameters);
3660
+ }
3661
+ this.simulator = new Simulator(this.registry);
3420
3662
  if (config.onDecision) this.on("decision", config.onDecision);
3421
3663
  if (config.onAlert) this.on("alert", config.onAlert);
3422
3664
  if (config.onRollback) this.on("rollback", config.onRollback);
@@ -3454,15 +3696,24 @@ var AgentE = class {
3454
3696
  this.currentTick = currentState.tick;
3455
3697
  const events = [...this.eventBuffer];
3456
3698
  this.eventBuffer = [];
3457
- const metrics = this.observer.compute(currentState, events);
3699
+ let metrics;
3700
+ try {
3701
+ metrics = this.observer.compute(currentState, events);
3702
+ } catch (err) {
3703
+ console.error(`[AgentE] Observer.compute() failed at tick ${currentState.tick}:`, err);
3704
+ return;
3705
+ }
3458
3706
  this.store.record(metrics);
3459
3707
  this.personaTracker.update(currentState);
3460
3708
  metrics.personaDistribution = this.personaTracker.getDistribution();
3461
- const rolledBack = await this.executor.checkRollbacks(metrics, this.adapter);
3709
+ const { rolledBack, settled } = await this.executor.checkRollbacks(metrics, this.adapter);
3462
3710
  for (const plan2 of rolledBack) {
3463
3711
  this.planner.recordRolledBack(plan2);
3464
3712
  this.emit("rollback", plan2, "rollback condition triggered");
3465
3713
  }
3714
+ for (const plan2 of settled) {
3715
+ this.planner.recordSettled(plan2);
3716
+ }
3466
3717
  if (metrics.tick < this.config.gracePeriod) return;
3467
3718
  if (metrics.tick % this.config.checkInterval !== 0) return;
3468
3719
  const diagnoses = this.diagnoser.diagnose(metrics, this.thresholds);
@@ -3484,7 +3735,8 @@ var AgentE = class {
3484
3735
  metrics,
3485
3736
  simulationResult,
3486
3737
  this.params,
3487
- this.thresholds
3738
+ this.thresholds,
3739
+ this.registry
3488
3740
  );
3489
3741
  if (!plan) {
3490
3742
  let reason = "skipped_cooldown";
@@ -3504,6 +3756,7 @@ var AgentE = class {
3504
3756
  }
3505
3757
  await this.executor.apply(plan, this.adapter, this.params);
3506
3758
  this.params[plan.parameter] = plan.targetValue;
3759
+ this.registry.updateValue(plan.parameter, plan.targetValue);
3507
3760
  this.planner.recordApplied(plan, metrics.tick);
3508
3761
  const entry = this.log.record(topDiagnosis, plan, "applied", metrics);
3509
3762
  this.emit("decision", entry);
@@ -3513,6 +3766,7 @@ var AgentE = class {
3513
3766
  async apply(plan) {
3514
3767
  await this.executor.apply(plan, this.adapter, this.params);
3515
3768
  this.params[plan.parameter] = plan.targetValue;
3769
+ this.registry.updateValue(plan.parameter, plan.targetValue);
3516
3770
  this.planner.recordApplied(plan, this.currentTick);
3517
3771
  }
3518
3772
  // ── Developer API ───────────────────────────────────────────────────────────
@@ -3537,6 +3791,12 @@ var AgentE = class {
3537
3791
  removePrinciple(id) {
3538
3792
  this.diagnoser.removePrinciple(id);
3539
3793
  }
3794
+ registerParameter(param) {
3795
+ this.registry.register(param);
3796
+ }
3797
+ getRegistry() {
3798
+ return this.registry;
3799
+ }
3540
3800
  registerCustomMetric(name, fn) {
3541
3801
  this.observer.registerCustomMetric(name, fn);
3542
3802
  }
@@ -3938,11 +4198,11 @@ export {
3938
4198
  Executor,
3939
4199
  FEEDBACK_LOOP_PRINCIPLES,
3940
4200
  INCENTIVE_PRINCIPLES,
3941
- LIVEOPS_PRINCIPLES,
3942
4201
  MARKET_DYNAMICS_PRINCIPLES,
3943
4202
  MEASUREMENT_PRINCIPLES,
3944
4203
  MetricStore,
3945
4204
  OPEN_ECONOMY_PRINCIPLES,
4205
+ OPERATIONS_PRINCIPLES,
3946
4206
  Observer,
3947
4207
  P10_SpawnWeightingUsesInversePopulation,
3948
4208
  P11_TwoTierPressure,
@@ -4004,9 +4264,10 @@ export {
4004
4264
  P7_NonSpecialistsSubsidiseSpecialists,
4005
4265
  P8_RegulatorCannotFightDesign,
4006
4266
  P9_RoleSwitchingNeedsFriction,
4267
+ PARTICIPANT_EXPERIENCE_PRINCIPLES,
4007
4268
  PERSONA_HEALTHY_RANGES,
4008
- PLAYER_EXPERIENCE_PRINCIPLES,
4009
4269
  POPULATION_PRINCIPLES,
4270
+ ParameterRegistry,
4010
4271
  PersonaTracker,
4011
4272
  Planner,
4012
4273
  REGULATOR_PRINCIPLES,