@agent-e/core 1.4.4 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -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") {
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") {
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,16 @@ var Observer = class {
415
456
  sharkToothValleys: this.previousMetrics?.sharkToothValleys ?? [],
416
457
  eventCompletionRate: NaN,
417
458
  contentDropAge,
459
+ systems: state.systems ?? [],
460
+ sources: state.sources ?? [],
461
+ sinks: state.sinks ?? [],
462
+ flowBySystem,
463
+ activityBySystem,
464
+ participantsBySystem,
465
+ flowBySource,
466
+ flowBySink,
467
+ sourceShare,
468
+ sinkShare,
418
469
  custom
419
470
  };
420
471
  this.previousMetrics = metrics;
@@ -557,6 +608,16 @@ function emptyMetrics(tick = 0) {
557
608
  sharkToothValleys: [],
558
609
  eventCompletionRate: NaN,
559
610
  contentDropAge: 0,
611
+ systems: [],
612
+ sources: [],
613
+ sinks: [],
614
+ flowBySystem: {},
615
+ activityBySystem: {},
616
+ participantsBySystem: {},
617
+ flowBySource: {},
618
+ flowBySink: {},
619
+ sourceShare: {},
620
+ sinkShare: {},
560
621
  custom: {}
561
622
  };
562
623
  }
@@ -589,7 +650,7 @@ var P1_ProductionMatchesConsumption = {
589
650
  severity: 7,
590
651
  evidence: { scarceResources: violations, dominantRole: dominantRole?.[0], dominantShare },
591
652
  suggestedAction: {
592
- parameter: "productionCost",
653
+ parameterType: "cost",
593
654
  direction: "decrease",
594
655
  magnitude: 0.15,
595
656
  reasoning: "Lower production cost to incentivise more production."
@@ -623,7 +684,8 @@ var P2_ClosedLoopsNeedDirectHandoff = {
623
684
  severity: 5,
624
685
  evidence: { backlogResources, velocity },
625
686
  suggestedAction: {
626
- parameter: "transactionFee",
687
+ parameterType: "fee",
688
+ scope: { tags: ["transaction"] },
627
689
  direction: "increase",
628
690
  magnitude: 0.2,
629
691
  reasoning: "Raise market fees to discourage raw material listings. Direct hand-off at production zones is the correct channel."
@@ -653,7 +715,7 @@ var P3_BootstrapCapitalCoversFirstTransaction = {
653
715
  severity: 8,
654
716
  evidence: { resource, totalProducers, supply },
655
717
  suggestedAction: {
656
- parameter: "productionCost",
718
+ parameterType: "cost",
657
719
  direction: "decrease",
658
720
  magnitude: 0.3,
659
721
  reasoning: "Producers cannot complete first transaction. Lower production cost to unblock bootstrap."
@@ -672,7 +734,7 @@ var P4_MaterialsFlowFasterThanCooldown = {
672
734
  id: "P4",
673
735
  name: "Materials Flow Faster Than Cooldown",
674
736
  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.",
737
+ 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
738
  check(metrics, _thresholds) {
677
739
  const { supplyByResource, populationByRole, velocity, totalAgents } = metrics;
678
740
  const totalSupply = Object.values(supplyByResource).reduce((s, v) => s + v, 0);
@@ -685,7 +747,7 @@ var P4_MaterialsFlowFasterThanCooldown = {
685
747
  severity: 5,
686
748
  evidence: { avgSupplyPerAgent, velocity, totalRoles },
687
749
  suggestedAction: {
688
- parameter: "yieldRate",
750
+ parameterType: "yield",
689
751
  direction: "increase",
690
752
  magnitude: 0.15,
691
753
  reasoning: "Low supply per agent with stagnant velocity. Increase yield to compensate."
@@ -700,7 +762,7 @@ var P4_MaterialsFlowFasterThanCooldown = {
700
762
  severity: 4,
701
763
  evidence: { avgSupplyPerAgent, totalSupply, totalAgents },
702
764
  suggestedAction: {
703
- parameter: "yieldRate",
765
+ parameterType: "yield",
704
766
  direction: "decrease",
705
767
  magnitude: 0.2,
706
768
  reasoning: "Raw materials piling up. Extractors outpacing producers."
@@ -728,7 +790,7 @@ var P60_SurplusDisposalAsymmetry = {
728
790
  discount: thresholds.disposalTradeWeightDiscount
729
791
  },
730
792
  suggestedAction: {
731
- parameter: "productionCost",
793
+ parameterType: "cost",
732
794
  direction: "decrease",
733
795
  magnitude: 0.1,
734
796
  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 +833,8 @@ var P5_ProfitabilityIsCompetitive = {
771
833
  population: populationByRole[dominantRole]
772
834
  },
773
835
  suggestedAction: {
774
- parameter: "transactionFee",
836
+ parameterType: "fee",
837
+ scope: { tags: ["transaction"] },
775
838
  direction: "increase",
776
839
  magnitude: thresholds.maxAdjustmentPercent,
777
840
  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 +860,7 @@ var P6_CrowdingMultiplierOnAllRoles = {
797
860
  severity: 5,
798
861
  evidence: { role, share },
799
862
  suggestedAction: {
800
- parameter: "productionCost",
863
+ parameterType: "cost",
801
864
  direction: "increase",
802
865
  magnitude: 0.1,
803
866
  reasoning: `${role} at ${(share * 100).toFixed(0)}% \u2014 no crowding pressure detected. Apply role-specific cost increase to simulate saturation.`
@@ -832,7 +895,8 @@ var P7_NonSpecialistsSubsidiseSpecialists = {
832
895
  severity: 6,
833
896
  evidence: { poolName, poolSize, dominantRole, dominantShare },
834
897
  suggestedAction: {
835
- parameter: "entryFee",
898
+ parameterType: "fee",
899
+ scope: { tags: ["entry"] },
836
900
  direction: "decrease",
837
901
  magnitude: 0.1,
838
902
  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 +924,7 @@ var P8_RegulatorCannotFightDesign = {
860
924
  severity: 4,
861
925
  evidence: { dominantRole: dominantRole[0], share: dominantRole[1], avgSatisfaction },
862
926
  suggestedAction: {
863
- parameter: "rewardRate",
927
+ parameterType: "reward",
864
928
  direction: "increase",
865
929
  magnitude: 0.1,
866
930
  reasoning: `Low satisfaction with ${dominantRole[0]} dominant. Regulator may be suppressing a structurally necessary role. Ease pressure on dominant role rewards.`
@@ -885,7 +949,7 @@ var P9_RoleSwitchingNeedsFriction = {
885
949
  id: "P9",
886
950
  name: "Role Switching Needs Friction",
887
951
  category: "population",
888
- description: "If >5% of the population switches roles in a single evaluation period, it is a herd movement, not rational rebalancing. Without friction (satisfaction cost, cooldown), one good tick causes mass migration.",
952
+ description: "If >5% of the population switches roles in a single evaluation period, it is a herd movement, not rational rebalancing. Without friction (satisfaction cost, minimum interval), one good tick causes mass migration.",
889
953
  check(metrics, thresholds) {
890
954
  const { churnByRole, roleShares } = metrics;
891
955
  const totalChurn = Object.values(churnByRole).reduce((s, v) => s + v, 0);
@@ -895,7 +959,7 @@ var P9_RoleSwitchingNeedsFriction = {
895
959
  severity: 5,
896
960
  evidence: { totalChurnRate: totalChurn, churnByRole },
897
961
  suggestedAction: {
898
- parameter: "productionCost",
962
+ parameterType: "cost",
899
963
  direction: "increase",
900
964
  magnitude: 0.05,
901
965
  reasoning: `Role switch rate ${(totalChurn * 100).toFixed(1)}% exceeds friction threshold. Increase production costs to slow herd movement.`
@@ -908,11 +972,11 @@ var P9_RoleSwitchingNeedsFriction = {
908
972
  return { violated: false };
909
973
  }
910
974
  };
911
- var P10_SpawnWeightingUsesInversePopulation = {
975
+ var P10_EntryWeightingUsesInversePopulation = {
912
976
  id: "P10",
913
- name: "Spawn Weighting Uses Inverse Population",
977
+ name: "Entry Weighting Uses Inverse Population",
914
978
  category: "population",
915
- description: "New agents should preferentially fill the least-populated roles. Flat entry probability causes initial imbalances to compound.",
979
+ description: "New entrants should preferentially fill the least-populated roles. Flat entry probability causes initial imbalances to compound.",
916
980
  check(metrics, _thresholds) {
917
981
  const { roleShares } = metrics;
918
982
  if (Object.keys(roleShares).length === 0) return { violated: false };
@@ -927,7 +991,7 @@ var P10_SpawnWeightingUsesInversePopulation = {
927
991
  severity: 4,
928
992
  evidence: { roleShares, stdDev, leastPopulatedRole: minRole?.[0] },
929
993
  suggestedAction: {
930
- parameter: "yieldRate",
994
+ parameterType: "yield",
931
995
  direction: "increase",
932
996
  magnitude: 0.05,
933
997
  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 +1017,8 @@ var P11_TwoTierPressure = {
953
1017
  severity: 6,
954
1018
  evidence: { role, share },
955
1019
  suggestedAction: {
956
- parameter: "transactionFee",
1020
+ parameterType: "fee",
1021
+ scope: { tags: ["transaction"] },
957
1022
  direction: "increase",
958
1023
  magnitude: 0.15,
959
1024
  reasoning: `${role} at ${(share * 100).toFixed(0)}% \u2014 continuous pressure was insufficient. Hard intervention needed alongside resumed continuous pressure.`
@@ -981,7 +1046,7 @@ var P46_PersonaDiversity = {
981
1046
  severity: 5,
982
1047
  evidence: { dominantPersona: persona, share, personaDistribution },
983
1048
  suggestedAction: {
984
- parameter: "rewardRate",
1049
+ parameterType: "reward",
985
1050
  direction: "increase",
986
1051
  magnitude: 0.1,
987
1052
  reasoning: `${persona} persona at ${(share * 100).toFixed(0)}% \u2014 behavioral monoculture. Diversify reward structures to attract other persona types.`
@@ -998,7 +1063,8 @@ var P46_PersonaDiversity = {
998
1063
  severity: 3,
999
1064
  evidence: { significantClusters, required: thresholds.personaMinClusters },
1000
1065
  suggestedAction: {
1001
- parameter: "transactionFee",
1066
+ parameterType: "fee",
1067
+ scope: { tags: ["transaction"] },
1002
1068
  direction: "decrease",
1003
1069
  magnitude: 0.05,
1004
1070
  reasoning: `Only ${significantClusters} significant persona clusters (need ${thresholds.personaMinClusters}). Lower trade barriers to attract non-dominant persona types.`
@@ -1012,7 +1078,7 @@ var P46_PersonaDiversity = {
1012
1078
  };
1013
1079
  var POPULATION_PRINCIPLES = [
1014
1080
  P9_RoleSwitchingNeedsFriction,
1015
- P10_SpawnWeightingUsesInversePopulation,
1081
+ P10_EntryWeightingUsesInversePopulation,
1016
1082
  P11_TwoTierPressure,
1017
1083
  P46_PersonaDiversity
1018
1084
  ];
@@ -1022,7 +1088,7 @@ var P12_OnePrimaryFaucet = {
1022
1088
  id: "P12",
1023
1089
  name: "One Primary Faucet",
1024
1090
  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.",
1091
+ 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
1092
  check(metrics, thresholds) {
1027
1093
  for (const curr of metrics.currencies) {
1028
1094
  const netFlow = metrics.netFlowByCurrency[curr] ?? 0;
@@ -1034,9 +1100,9 @@ var P12_OnePrimaryFaucet = {
1034
1100
  severity: 5,
1035
1101
  evidence: { currency: curr, netFlow, faucetVolume, sinkVolume },
1036
1102
  suggestedAction: {
1037
- parameter: "productionCost",
1103
+ parameterType: "cost",
1038
1104
  direction: "increase",
1039
- currency: curr,
1105
+ scope: { currency: curr },
1040
1106
  magnitude: 0.15,
1041
1107
  reasoning: `[${curr}] Net flow +${netFlow.toFixed(1)}/tick. Inflationary. Increase production cost (primary sink) to balance faucet output.`
1042
1108
  },
@@ -1050,9 +1116,9 @@ var P12_OnePrimaryFaucet = {
1050
1116
  severity: 4,
1051
1117
  evidence: { currency: curr, netFlow, faucetVolume, sinkVolume },
1052
1118
  suggestedAction: {
1053
- parameter: "productionCost",
1119
+ parameterType: "cost",
1054
1120
  direction: "decrease",
1055
- currency: curr,
1121
+ scope: { currency: curr },
1056
1122
  magnitude: 0.15,
1057
1123
  reasoning: `[${curr}] Net flow ${netFlow.toFixed(1)}/tick. Deflationary. Decrease production cost to ease sink pressure.`
1058
1124
  },
@@ -1084,9 +1150,9 @@ var P13_PotsAreZeroSumAndSelfRegulate = {
1084
1150
  severity: 7,
1085
1151
  evidence: { currency: curr, pool: poolName, poolSize, participants: dominantCount, maxSustainableMultiplier },
1086
1152
  suggestedAction: {
1087
- parameter: "rewardRate",
1153
+ parameterType: "reward",
1088
1154
  direction: "decrease",
1089
- currency: curr,
1155
+ scope: { currency: curr },
1090
1156
  magnitude: 0.15,
1091
1157
  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
1158
  },
@@ -1103,7 +1169,7 @@ var P14_TrackActualInjection = {
1103
1169
  id: "P14",
1104
1170
  name: "Track Actual Currency Injection, Not Value Creation",
1105
1171
  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.',
1172
+ description: 'Counting resource extraction as "currency injected" is misleading. Currency enters through faucet mechanisms (entering, rewards). Fake metrics break every downstream decision.',
1107
1173
  check(metrics, _thresholds) {
1108
1174
  for (const curr of metrics.currencies) {
1109
1175
  const faucetVolume = metrics.faucetVolumeByCurrency[curr] ?? 0;
@@ -1116,9 +1182,9 @@ var P14_TrackActualInjection = {
1116
1182
  severity: 4,
1117
1183
  evidence: { currency: curr, faucetVolume, netFlow, supplyGrowthRate },
1118
1184
  suggestedAction: {
1119
- parameter: "yieldRate",
1185
+ parameterType: "yield",
1120
1186
  direction: "decrease",
1121
- currency: curr,
1187
+ scope: { currency: curr },
1122
1188
  magnitude: 0.1,
1123
1189
  reasoning: `[${curr}] Supply growing at ${(supplyGrowthRate * 100).toFixed(1)}%/tick. Verify currency injection tracking. Resources should not create currency directly.`
1124
1190
  },
@@ -1148,9 +1214,9 @@ var P15_PoolsNeedCapAndDecay = {
1148
1214
  severity: 6,
1149
1215
  evidence: { currency: curr, pool, size, shareOfSupply, cap: poolCapPercent },
1150
1216
  suggestedAction: {
1151
- parameter: "transactionFee",
1217
+ parameterType: "fee",
1152
1218
  direction: "decrease",
1153
- currency: curr,
1219
+ scope: { tags: ["transaction"], currency: curr },
1154
1220
  magnitude: 0.1,
1155
1221
  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
1222
  },
@@ -1180,9 +1246,9 @@ var P16_WithdrawalPenaltyScales = {
1180
1246
  severity: 3,
1181
1247
  evidence: { currency: curr, pool: poolName, poolSize, estimatedStaked: stakedEstimate },
1182
1248
  suggestedAction: {
1183
- parameter: "transactionFee",
1249
+ parameterType: "fee",
1184
1250
  direction: "increase",
1185
- currency: curr,
1251
+ scope: { tags: ["transaction"], currency: curr },
1186
1252
  magnitude: 0.05,
1187
1253
  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
1254
  },
@@ -1212,9 +1278,9 @@ var P32_VelocityAboveSupply = {
1212
1278
  severity: 4,
1213
1279
  evidence: { currency: curr, velocity, totalSupply, totalResources },
1214
1280
  suggestedAction: {
1215
- parameter: "transactionFee",
1281
+ parameterType: "fee",
1216
1282
  direction: "decrease",
1217
- currency: curr,
1283
+ scope: { tags: ["transaction"], currency: curr },
1218
1284
  magnitude: 0.2,
1219
1285
  reasoning: `[${curr}] Velocity ${velocity}/t with ${totalResources} resources in system. Economy stagnant despite available supply. Lower trading friction.`
1220
1286
  },
@@ -1254,9 +1320,9 @@ var P58_NoNaturalNumeraire = {
1254
1320
  meanPrice: mean
1255
1321
  },
1256
1322
  suggestedAction: {
1257
- parameter: "productionCost",
1323
+ parameterType: "cost",
1258
1324
  direction: "increase",
1259
- currency: curr,
1325
+ scope: { currency: curr },
1260
1326
  magnitude: 0.1,
1261
1327
  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
1328
  },
@@ -1291,7 +1357,8 @@ var P17_GracePeriodBeforeIntervention = {
1291
1357
  severity: 7,
1292
1358
  evidence: { tick: metrics.tick, avgSatisfaction: metrics.avgSatisfaction },
1293
1359
  suggestedAction: {
1294
- parameter: "entryFee",
1360
+ parameterType: "fee",
1361
+ scope: { tags: ["entry"] },
1295
1362
  direction: "decrease",
1296
1363
  magnitude: 0.2,
1297
1364
  reasoning: `Very low satisfaction at tick ${metrics.tick}. Intervention may have fired during grace period. Ease all costs to let economy bootstrap.`
@@ -1318,7 +1385,7 @@ var P18_FirstProducerNeedsStartingInventory = {
1318
1385
  severity: 8,
1319
1386
  evidence: { tick: metrics.tick, resource, supply, totalAgents: metrics.totalAgents },
1320
1387
  suggestedAction: {
1321
- parameter: "productionCost",
1388
+ parameterType: "cost",
1322
1389
  direction: "decrease",
1323
1390
  magnitude: 0.5,
1324
1391
  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 +1424,7 @@ var P19_StartingSupplyExceedsDemand = {
1357
1424
  resourcesPerAgent
1358
1425
  },
1359
1426
  suggestedAction: {
1360
- parameter: "rewardRate",
1427
+ parameterType: "reward",
1361
1428
  direction: "increase",
1362
1429
  magnitude: 0.2,
1363
1430
  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 +1458,7 @@ var P20_DecayPreventsAccumulation = {
1391
1458
  severity: 4,
1392
1459
  evidence: { totalResources, resourcesPerAgent, velocity },
1393
1460
  suggestedAction: {
1394
- parameter: "yieldRate",
1461
+ parameterType: "yield",
1395
1462
  direction: "decrease",
1396
1463
  magnitude: 0.1,
1397
1464
  reasoning: `${totalResources.toFixed(0)} resources with velocity ${velocity}/t. Likely hoarding. Reduce yield to increase scarcity and force circulation.`
@@ -1419,7 +1486,8 @@ var P21_PriceFromGlobalSupply = {
1419
1486
  severity: 3,
1420
1487
  evidence: { resource, volatility, supply, price: prices[resource] },
1421
1488
  suggestedAction: {
1422
- parameter: "transactionFee",
1489
+ parameterType: "fee",
1490
+ scope: { tags: ["transaction"] },
1423
1491
  direction: "increase",
1424
1492
  magnitude: 0.05,
1425
1493
  reasoning: `${resource} price volatile (${(volatility * 100).toFixed(0)}%) despite supply ${supply}. Price may not reflect global inventory. Increase trading friction to stabilise.`
@@ -1436,7 +1504,7 @@ var P22_MarketAwarenessPreventsSurplus = {
1436
1504
  id: "P22",
1437
1505
  name: "Market Awareness Prevents Overproduction",
1438
1506
  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.",
1507
+ description: "Producers who produce without checking market prices will create surpluses that crash prices. Agents need to see prices before deciding to produce.",
1440
1508
  check(metrics, _thresholds) {
1441
1509
  const { supplyByResource, prices, productionIndex } = metrics;
1442
1510
  const priceValues = Object.values(prices).filter((p) => p > 0);
@@ -1460,7 +1528,7 @@ var P22_MarketAwarenessPreventsSurplus = {
1460
1528
  productionIndex
1461
1529
  },
1462
1530
  suggestedAction: {
1463
- parameter: "productionCost",
1531
+ parameterType: "cost",
1464
1532
  direction: "increase",
1465
1533
  magnitude: 0.1,
1466
1534
  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 +1555,7 @@ var P23_ProfitabilityFactorsFeasibility = {
1487
1555
  severity: 5,
1488
1556
  evidence: { blockedFraction, blockedAgentCount, avgSatisfaction },
1489
1557
  suggestedAction: {
1490
- parameter: "productionCost",
1558
+ parameterType: "cost",
1491
1559
  direction: "decrease",
1492
1560
  magnitude: 0.15,
1493
1561
  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 +1581,8 @@ var P24_BlockedAgentsDecayFaster = {
1513
1581
  severity: 5,
1514
1582
  evidence: { blockedFraction, blockedAgentCount, churnRate },
1515
1583
  suggestedAction: {
1516
- parameter: "transactionFee",
1584
+ parameterType: "fee",
1585
+ scope: { tags: ["transaction"] },
1517
1586
  direction: "decrease",
1518
1587
  magnitude: 0.15,
1519
1588
  reasoning: `${(blockedFraction * 100).toFixed(0)}% of agents blocked. Blocked agents churn silently, skewing metrics. Lower fees to unblock market participation.`
@@ -1558,7 +1627,7 @@ var P25_CorrectLeversForCorrectProblems = {
1558
1627
  netFlow
1559
1628
  },
1560
1629
  suggestedAction: {
1561
- parameter: "yieldRate",
1630
+ parameterType: "yield",
1562
1631
  direction: "decrease",
1563
1632
  magnitude: 0.15,
1564
1633
  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 +1653,7 @@ var P26_ContinuousPressureBeatsThresholdCuts = {
1584
1653
  severity: 4,
1585
1654
  evidence: { inflationRate },
1586
1655
  suggestedAction: {
1587
- parameter: "productionCost",
1656
+ parameterType: "cost",
1588
1657
  direction: inflationRate > 0 ? "increase" : "decrease",
1589
1658
  magnitude: Math.min(thresholds.maxAdjustmentPercent, 0.05),
1590
1659
  // force smaller step
@@ -1610,7 +1679,8 @@ var P27_AdjustmentsNeedCooldowns = {
1610
1679
  severity: 4,
1611
1680
  evidence: { churnRate, avgSatisfaction },
1612
1681
  suggestedAction: {
1613
- parameter: "entryFee",
1682
+ parameterType: "fee",
1683
+ scope: { tags: ["entry"] },
1614
1684
  direction: "decrease",
1615
1685
  magnitude: 0.05,
1616
1686
  reasoning: `High churn (${(churnRate * 100).toFixed(1)}%) with low satisfaction. Possible oscillation from rapid adjustments. Apply small correction only.`
@@ -1641,7 +1711,7 @@ var P28_StructuralDominanceIsNotPathological = {
1641
1711
  severity: 5,
1642
1712
  evidence: { dominantRole, dominantShare, avgSatisfaction },
1643
1713
  suggestedAction: {
1644
- parameter: "productionCost",
1714
+ parameterType: "cost",
1645
1715
  direction: "decrease",
1646
1716
  magnitude: 0.1,
1647
1717
  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 +1727,7 @@ var P38_CommunicationPreventsRevolt = {
1657
1727
  id: "P38",
1658
1728
  name: "Communication Prevents Revolt",
1659
1729
  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.",
1730
+ 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
1731
  check(metrics, _thresholds) {
1662
1732
  const { churnRate } = metrics;
1663
1733
  if (churnRate > 0.1) {
@@ -1666,7 +1736,7 @@ var P38_CommunicationPreventsRevolt = {
1666
1736
  severity: 3,
1667
1737
  evidence: { churnRate },
1668
1738
  suggestedAction: {
1669
- parameter: "rewardRate",
1739
+ parameterType: "reward",
1670
1740
  direction: "increase",
1671
1741
  magnitude: 0.1,
1672
1742
  reasoning: `High churn (${(churnRate * 100).toFixed(1)}%) \u2014 agents leaving. Ensure all recent adjustments are logged with reasoning to diagnose cause.`
@@ -1687,9 +1757,9 @@ var REGULATOR_PRINCIPLES = [
1687
1757
  ];
1688
1758
 
1689
1759
  // src/principles/market-dynamics.ts
1690
- var P29_PinchPoint = {
1760
+ var P29_BottleneckDetection = {
1691
1761
  id: "P29",
1692
- name: "Pinch Point",
1762
+ name: "Bottleneck Detection",
1693
1763
  category: "market_dynamics",
1694
1764
  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
1765
  check(metrics, _thresholds) {
@@ -1703,7 +1773,7 @@ var P29_PinchPoint = {
1703
1773
  severity: 7,
1704
1774
  evidence: { resource, supply, demand, status },
1705
1775
  suggestedAction: {
1706
- parameter: "productionCost",
1776
+ parameterType: "cost",
1707
1777
  direction: "decrease",
1708
1778
  magnitude: 0.15,
1709
1779
  reasoning: `${resource} is a pinch point and currently SCARCE (supply ${supply}, demand ${demand}). Reduce production cost to increase throughput.`
@@ -1719,7 +1789,7 @@ var P29_PinchPoint = {
1719
1789
  severity: 4,
1720
1790
  evidence: { resource, supply, status },
1721
1791
  suggestedAction: {
1722
- parameter: "productionCost",
1792
+ parameterType: "cost",
1723
1793
  direction: "increase",
1724
1794
  magnitude: 0.1,
1725
1795
  reasoning: `${resource} is a pinch point and OVERSUPPLIED (supply ${supply}). Raise production cost to reduce surplus.`
@@ -1732,11 +1802,11 @@ var P29_PinchPoint = {
1732
1802
  return { violated: false };
1733
1803
  }
1734
1804
  };
1735
- var P30_MovingPinchPoint = {
1805
+ var P30_DynamicBottleneckRotation = {
1736
1806
  id: "P30",
1737
- name: "Moving Pinch Point",
1807
+ name: "Dynamic Bottleneck Rotation",
1738
1808
  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.",
1809
+ 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
1810
  check(metrics, _thresholds) {
1741
1811
  const { capacityUsage, supplyByResource, avgSatisfaction } = metrics;
1742
1812
  const totalResources = Object.values(supplyByResource).reduce((s, v) => s + v, 0);
@@ -1747,7 +1817,7 @@ var P30_MovingPinchPoint = {
1747
1817
  severity: 3,
1748
1818
  evidence: { capacityUsage, resourcesPerAgent, avgSatisfaction },
1749
1819
  suggestedAction: {
1750
- parameter: "productionCost",
1820
+ parameterType: "cost",
1751
1821
  direction: "increase",
1752
1822
  magnitude: 0.1,
1753
1823
  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 +1833,7 @@ var P57_CombinatorialPriceSpace = {
1763
1833
  id: "P57",
1764
1834
  name: "Combinatorial Price Space",
1765
1835
  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.",
1836
+ 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
1837
  check(metrics, thresholds) {
1768
1838
  const { prices, priceVolatility } = metrics;
1769
1839
  const priceKeys = Object.keys(prices);
@@ -1793,7 +1863,8 @@ var P57_CombinatorialPriceSpace = {
1793
1863
  target: thresholds.relativePriceConvergenceTarget
1794
1864
  },
1795
1865
  suggestedAction: {
1796
- parameter: "transactionFee",
1866
+ parameterType: "fee",
1867
+ scope: { tags: ["transaction"] },
1797
1868
  direction: "decrease",
1798
1869
  magnitude: 0.1,
1799
1870
  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.`
@@ -1806,8 +1877,8 @@ var P57_CombinatorialPriceSpace = {
1806
1877
  }
1807
1878
  };
1808
1879
  var MARKET_DYNAMICS_PRINCIPLES = [
1809
- P29_PinchPoint,
1810
- P30_MovingPinchPoint,
1880
+ P29_BottleneckDetection,
1881
+ P30_DynamicBottleneckRotation,
1811
1882
  P57_CombinatorialPriceSpace
1812
1883
  ];
1813
1884
 
@@ -1825,7 +1896,7 @@ var P31_AnchorValueTracking = {
1825
1896
  severity: 5,
1826
1897
  evidence: { anchorRatioDrift, inflationRate },
1827
1898
  suggestedAction: {
1828
- parameter: "productionCost",
1899
+ parameterType: "cost",
1829
1900
  direction: anchorRatioDrift > 0 ? "increase" : "decrease",
1830
1901
  magnitude: 0.1,
1831
1902
  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 +1921,8 @@ var P41_MultiResolutionMonitoring = {
1850
1921
  severity: 4,
1851
1922
  evidence: { giniCoefficient, avgSatisfaction },
1852
1923
  suggestedAction: {
1853
- parameter: "transactionFee",
1924
+ parameterType: "fee",
1925
+ scope: { tags: ["transaction"] },
1854
1926
  direction: "increase",
1855
1927
  magnitude: 0.1,
1856
1928
  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 +1951,8 @@ var P55_ArbitrageThermometer = {
1879
1951
  critical: thresholds.arbitrageIndexCritical
1880
1952
  },
1881
1953
  suggestedAction: {
1882
- parameter: "transactionFee",
1954
+ parameterType: "fee",
1955
+ scope: { tags: ["transaction"] },
1883
1956
  direction: "decrease",
1884
1957
  magnitude: 0.15,
1885
1958
  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 +1970,8 @@ var P55_ArbitrageThermometer = {
1897
1970
  warning: thresholds.arbitrageIndexWarning
1898
1971
  },
1899
1972
  suggestedAction: {
1900
- parameter: "transactionFee",
1973
+ parameterType: "fee",
1974
+ scope: { tags: ["transaction"] },
1901
1975
  direction: "decrease",
1902
1976
  magnitude: 0.08,
1903
1977
  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 +1999,8 @@ var P59_GiftEconomyNoise = {
1925
1999
  threshold: thresholds.giftTradeFilterRatio
1926
2000
  },
1927
2001
  suggestedAction: {
1928
- parameter: "transactionFee",
2002
+ parameterType: "fee",
2003
+ scope: { tags: ["transaction"] },
1929
2004
  direction: "increase",
1930
2005
  magnitude: 0.05,
1931
2006
  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 +2043,9 @@ var P42_TheMedianPrinciple = {
1968
2043
  medianBalance
1969
2044
  },
1970
2045
  suggestedAction: {
1971
- parameter: "transactionFee",
2046
+ parameterType: "fee",
2047
+ scope: { tags: ["transaction"], currency: curr },
1972
2048
  direction: "increase",
1973
- currency: curr,
1974
2049
  magnitude: 0.15,
1975
2050
  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
2051
  },
@@ -1995,7 +2070,7 @@ var P43_SimulationMinimum = {
1995
2070
  severity: 3,
1996
2071
  evidence: { inflationRate, minIterations: thresholds.simulationMinIterations },
1997
2072
  suggestedAction: {
1998
- parameter: "productionCost",
2073
+ parameterType: "cost",
1999
2074
  direction: inflationRate > 0 ? "increase" : "decrease",
2000
2075
  magnitude: 0.05,
2001
2076
  reasoning: `Large inflation rate swing (${(inflationRate * 100).toFixed(0)}%). Ensure all decisions use \u2265${thresholds.simulationMinIterations} simulation iterations. Apply conservative correction.`
@@ -2033,7 +2108,7 @@ var P39_TheLagPrinciple = {
2033
2108
  severity: 5,
2034
2109
  evidence: { inflationRate, netFlow, lagRange: [lagMin, lagMax] },
2035
2110
  suggestedAction: {
2036
- parameter: "productionCost",
2111
+ parameterType: "cost",
2037
2112
  direction: "increase",
2038
2113
  magnitude: 0.03,
2039
2114
  // very small — oscillation means over-adjusting
@@ -2060,7 +2135,8 @@ var P44_ComplexityBudget = {
2060
2135
  severity: 3,
2061
2136
  evidence: { customMetricCount, budgetMax: thresholds.complexityBudgetMax },
2062
2137
  suggestedAction: {
2063
- parameter: "transactionFee",
2138
+ parameterType: "fee",
2139
+ scope: { tags: ["transaction"] },
2064
2140
  direction: "decrease",
2065
2141
  magnitude: 0.01,
2066
2142
  reasoning: `${customMetricCount} custom metrics tracked (budget: ${thresholds.complexityBudgetMax}). Consider pruning low-impact parameters. Applying minimal correction to avoid adding complexity.`
@@ -2092,7 +2168,8 @@ var P35_DestructionCreatesValue = {
2092
2168
  severity: 6,
2093
2169
  evidence: { resource, supply, sinkVolume, netFlow },
2094
2170
  suggestedAction: {
2095
- parameter: "entryFee",
2171
+ parameterType: "fee",
2172
+ scope: { tags: ["entry"] },
2096
2173
  direction: "decrease",
2097
2174
  magnitude: 0.1,
2098
2175
  reasoning: `${resource} supply at ${supply} units with low destruction (sink ${sinkVolume}/t). Resources not being consumed. Lower competitive pool entry to increase resource usage.`
@@ -2109,7 +2186,7 @@ var P40_ReplacementRate = {
2109
2186
  id: "P40",
2110
2187
  name: "Replacement Rate \u2265 2\xD7 Consumption",
2111
2188
  category: "resource",
2112
- description: "Respawn/production rate must be at least 2\xD7 consumption rate for equilibrium. At 1\xD7 you drift toward depletion. At 2\xD7 you have a buffer for demand spikes.",
2189
+ description: "Replacement/production rate must be at least 2\xD7 consumption rate for equilibrium. At 1\xD7 you drift toward depletion. At 2\xD7 you have a buffer for demand spikes.",
2113
2190
  check(metrics, thresholds) {
2114
2191
  const { productionIndex, sinkVolume } = metrics;
2115
2192
  if (sinkVolume > 0 && productionIndex > 0) {
@@ -2120,7 +2197,7 @@ var P40_ReplacementRate = {
2120
2197
  severity: 6,
2121
2198
  evidence: { productionIndex, sinkVolume, replacementRatio },
2122
2199
  suggestedAction: {
2123
- parameter: "yieldRate",
2200
+ parameterType: "yield",
2124
2201
  direction: "increase",
2125
2202
  magnitude: 0.15,
2126
2203
  reasoning: `Replacement rate ${replacementRatio.toFixed(2)} (need \u2265${thresholds.replacementRateMultiplier}). Production below consumption. Resources will deplete. Increase yield.`
@@ -2134,7 +2211,7 @@ var P40_ReplacementRate = {
2134
2211
  severity: 3,
2135
2212
  evidence: { productionIndex, sinkVolume, replacementRatio },
2136
2213
  suggestedAction: {
2137
- parameter: "yieldRate",
2214
+ parameterType: "yield",
2138
2215
  direction: "decrease",
2139
2216
  magnitude: 0.1,
2140
2217
  reasoning: `Replacement rate ${replacementRatio.toFixed(2)} \u2014 overproducing. Production far exceeds consumption. Reduce yield to prevent glut.`
@@ -2160,7 +2237,8 @@ var P49_IdleAssetTax = {
2160
2237
  severity: 5,
2161
2238
  evidence: { giniCoefficient, top10PctShare, velocity },
2162
2239
  suggestedAction: {
2163
- parameter: "transactionFee",
2240
+ parameterType: "fee",
2241
+ scope: { tags: ["transaction"] },
2164
2242
  direction: "increase",
2165
2243
  magnitude: 0.15,
2166
2244
  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 +2256,11 @@ var RESOURCE_MGMT_PRINCIPLES = [
2178
2256
  P49_IdleAssetTax
2179
2257
  ];
2180
2258
 
2181
- // src/principles/player-experience.ts
2259
+ // src/principles/participant-experience.ts
2182
2260
  var P33_FairNotEqual = {
2183
2261
  id: "P33",
2184
2262
  name: "Fair \u2260 Equal",
2185
- category: "player_experience",
2263
+ category: "participant_experience",
2186
2264
  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
2265
  check(metrics, thresholds) {
2188
2266
  for (const curr of metrics.currencies) {
@@ -2193,9 +2271,9 @@ var P33_FairNotEqual = {
2193
2271
  severity: 3,
2194
2272
  evidence: { currency: curr, giniCoefficient },
2195
2273
  suggestedAction: {
2196
- parameter: "rewardRate",
2274
+ parameterType: "reward",
2197
2275
  direction: "increase",
2198
- currency: curr,
2276
+ scope: { currency: curr },
2199
2277
  magnitude: 0.1,
2200
2278
  reasoning: `[${curr}] Gini ${giniCoefficient.toFixed(2)} \u2014 near-perfect equality. Economy lacks stakes. Increase winner rewards to create meaningful spread.`
2201
2279
  },
@@ -2209,9 +2287,9 @@ var P33_FairNotEqual = {
2209
2287
  severity: 7,
2210
2288
  evidence: { currency: curr, giniCoefficient },
2211
2289
  suggestedAction: {
2212
- parameter: "transactionFee",
2290
+ parameterType: "fee",
2213
2291
  direction: "increase",
2214
- currency: curr,
2292
+ scope: { tags: ["transaction"], currency: curr },
2215
2293
  magnitude: 0.2,
2216
2294
  reasoning: `[${curr}] Gini ${giniCoefficient.toFixed(2)} \u2014 oligarchy level. Toxic inequality. Raise transaction fees to redistribute wealth from rich to pool.`
2217
2295
  },
@@ -2225,9 +2303,9 @@ var P33_FairNotEqual = {
2225
2303
  severity: 4,
2226
2304
  evidence: { currency: curr, giniCoefficient },
2227
2305
  suggestedAction: {
2228
- parameter: "transactionFee",
2306
+ parameterType: "fee",
2229
2307
  direction: "increase",
2230
- currency: curr,
2308
+ scope: { tags: ["transaction"], currency: curr },
2231
2309
  magnitude: 0.1,
2232
2310
  reasoning: `[${curr}] Gini ${giniCoefficient.toFixed(2)} \u2014 high inequality warning. Gently raise fees to slow wealth concentration.`
2233
2311
  },
@@ -2241,9 +2319,9 @@ var P33_FairNotEqual = {
2241
2319
  };
2242
2320
  var P36_MechanicFrictionDetector = {
2243
2321
  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.",
2322
+ name: "Mechanism Friction Detector",
2323
+ category: "participant_experience",
2324
+ 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
2325
  check(metrics, _thresholds) {
2248
2326
  const { avgSatisfaction, churnRate, velocity } = metrics;
2249
2327
  if (churnRate > 0.1 && avgSatisfaction < 50 && velocity > 3) {
@@ -2252,10 +2330,10 @@ var P36_MechanicFrictionDetector = {
2252
2330
  severity: 5,
2253
2331
  evidence: { churnRate, avgSatisfaction, velocity },
2254
2332
  suggestedAction: {
2255
- parameter: "rewardRate",
2333
+ parameterType: "reward",
2256
2334
  direction: "increase",
2257
2335
  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."
2336
+ 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
2337
  },
2260
2338
  confidence: 0.55,
2261
2339
  estimatedLag: 15
@@ -2266,8 +2344,8 @@ var P36_MechanicFrictionDetector = {
2266
2344
  };
2267
2345
  var P37_LatecommerProblem = {
2268
2346
  id: "P37",
2269
- name: "Latecomer Problem",
2270
- category: "player_experience",
2347
+ name: "Late Entrant Problem",
2348
+ category: "participant_experience",
2271
2349
  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
2350
  check(metrics, _thresholds) {
2273
2351
  const { timeToValue, avgSatisfaction, churnRate } = metrics;
@@ -2277,7 +2355,7 @@ var P37_LatecommerProblem = {
2277
2355
  severity: 6,
2278
2356
  evidence: { timeToValue, avgSatisfaction, churnRate },
2279
2357
  suggestedAction: {
2280
- parameter: "productionCost",
2358
+ parameterType: "cost",
2281
2359
  direction: "decrease",
2282
2360
  magnitude: 0.15,
2283
2361
  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 +2370,7 @@ var P37_LatecommerProblem = {
2292
2370
  var P45_TimeBudget = {
2293
2371
  id: "P45",
2294
2372
  name: "Time Budget",
2295
- category: "player_experience",
2373
+ category: "participant_experience",
2296
2374
  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
2375
  check(metrics, thresholds) {
2298
2376
  const { timeToValue, avgSatisfaction } = metrics;
@@ -2304,8 +2382,9 @@ var P45_TimeBudget = {
2304
2382
  severity: 5,
2305
2383
  evidence: { timeToValue, avgSatisfaction, timeBudgetRatio: thresholds.timeBudgetRatio },
2306
2384
  suggestedAction: {
2307
- parameter: "entryFee",
2385
+ parameterType: "fee",
2308
2386
  direction: "decrease",
2387
+ scope: { tags: ["entry"] },
2309
2388
  magnitude: 0.15,
2310
2389
  reasoning: `Time-to-value ${timeToValue} ticks with ${avgSatisfaction.toFixed(0)} satisfaction. Economy requires too much time investment. Lower barriers to participation.`
2311
2390
  },
@@ -2319,7 +2398,7 @@ var P45_TimeBudget = {
2319
2398
  var P50_PayPowerRatio = {
2320
2399
  id: "P50",
2321
2400
  name: "Pay-Power Ratio",
2322
- category: "player_experience",
2401
+ category: "participant_experience",
2323
2402
  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
2403
  check(metrics, thresholds) {
2325
2404
  const { top10PctShare, giniCoefficient } = metrics;
@@ -2334,8 +2413,9 @@ var P50_PayPowerRatio = {
2334
2413
  threshold: thresholds.payPowerRatioMax
2335
2414
  },
2336
2415
  suggestedAction: {
2337
- parameter: "transactionFee",
2416
+ parameterType: "fee",
2338
2417
  direction: "increase",
2418
+ scope: { tags: ["transaction"] },
2339
2419
  magnitude: 0.2,
2340
2420
  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
2421
  },
@@ -2346,7 +2426,7 @@ var P50_PayPowerRatio = {
2346
2426
  return { violated: false };
2347
2427
  }
2348
2428
  };
2349
- var PLAYER_EXPERIENCE_PRINCIPLES = [
2429
+ var PARTICIPANT_EXPERIENCE_PRINCIPLES = [
2350
2430
  P33_FairNotEqual,
2351
2431
  P36_MechanicFrictionDetector,
2352
2432
  P37_LatecommerProblem,
@@ -2369,7 +2449,8 @@ var P34_ExtractionRatio = {
2369
2449
  severity: 8,
2370
2450
  evidence: { extractionRatio, threshold: thresholds.extractionRatioRed },
2371
2451
  suggestedAction: {
2372
- parameter: "transactionFee",
2452
+ parameterType: "fee",
2453
+ scope: { tags: ["transaction"] },
2373
2454
  direction: "increase",
2374
2455
  magnitude: 0.25,
2375
2456
  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 +2465,8 @@ var P34_ExtractionRatio = {
2384
2465
  severity: 5,
2385
2466
  evidence: { extractionRatio, threshold: thresholds.extractionRatioYellow },
2386
2467
  suggestedAction: {
2387
- parameter: "transactionFee",
2468
+ parameterType: "fee",
2469
+ scope: { tags: ["transaction"] },
2388
2470
  direction: "increase",
2389
2471
  magnitude: 0.1,
2390
2472
  reasoning: `Extraction ratio ${(extractionRatio * 100).toFixed(0)}% (warning: ${(thresholds.extractionRatioYellow * 100).toFixed(0)}%). Economy trending toward extraction-heavy. Apply early pressure.`
@@ -2410,7 +2492,7 @@ var P47_SmokeTest = {
2410
2492
  severity: 9,
2411
2493
  evidence: { smokeTestRatio, threshold: thresholds.smokeTestCritical },
2412
2494
  suggestedAction: {
2413
- parameter: "rewardRate",
2495
+ parameterType: "reward",
2414
2496
  direction: "increase",
2415
2497
  magnitude: 0.2,
2416
2498
  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 +2507,7 @@ var P47_SmokeTest = {
2425
2507
  severity: 6,
2426
2508
  evidence: { smokeTestRatio, threshold: thresholds.smokeTestWarning },
2427
2509
  suggestedAction: {
2428
- parameter: "rewardRate",
2510
+ parameterType: "reward",
2429
2511
  direction: "increase",
2430
2512
  magnitude: 0.1,
2431
2513
  reasoning: `Utility/market ratio ${(smokeTestRatio * 100).toFixed(0)}% (warning). Economy is >70% speculative. Boost utility rewards to restore intrinsic value anchor.`
@@ -2451,7 +2533,8 @@ var P48_CurrencyInsulation = {
2451
2533
  severity: 6,
2452
2534
  evidence: { currencyInsulation, threshold: thresholds.currencyInsulationMax },
2453
2535
  suggestedAction: {
2454
- parameter: "transactionFee",
2536
+ parameterType: "fee",
2537
+ scope: { tags: ["transaction"] },
2455
2538
  direction: "increase",
2456
2539
  magnitude: 0.1,
2457
2540
  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 +2552,12 @@ var OPEN_ECONOMY_PRINCIPLES = [
2469
2552
  P48_CurrencyInsulation
2470
2553
  ];
2471
2554
 
2472
- // src/principles/liveops.ts
2473
- var P51_SharkTooth = {
2555
+ // src/principles/operations.ts
2556
+ var P51_CyclicalEngagement = {
2474
2557
  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.",
2558
+ name: "Cyclical Engagement Pattern",
2559
+ category: "operations",
2560
+ 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
2561
  check(metrics, thresholds) {
2479
2562
  const { sharkToothPeaks, sharkToothValleys } = metrics;
2480
2563
  if (sharkToothPeaks.length < 2) return { violated: false };
@@ -2491,10 +2574,10 @@ var P51_SharkTooth = {
2491
2574
  threshold: thresholds.sharkToothPeakDecay
2492
2575
  },
2493
2576
  suggestedAction: {
2494
- parameter: "rewardRate",
2577
+ parameterType: "reward",
2495
2578
  direction: "increase",
2496
2579
  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.`
2580
+ 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
2581
  },
2499
2582
  confidence: 0.75,
2500
2583
  estimatedLag: 30
@@ -2509,10 +2592,10 @@ var P51_SharkTooth = {
2509
2592
  severity: 4,
2510
2593
  evidence: { lastValley, prevValley, ratio: lastValley / prevValley },
2511
2594
  suggestedAction: {
2512
- parameter: "productionCost",
2595
+ parameterType: "cost",
2513
2596
  direction: "decrease",
2514
2597
  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."
2598
+ reasoning: "Between-activity engagement declining (deepening valleys). Base economy not sustaining participants between activities. Lower production costs to improve off-activity value."
2516
2599
  },
2517
2600
  confidence: 0.65,
2518
2601
  estimatedLag: 20
@@ -2525,8 +2608,8 @@ var P51_SharkTooth = {
2525
2608
  var P52_EndowmentEffect = {
2526
2609
  id: "P52",
2527
2610
  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).",
2611
+ category: "operations",
2612
+ description: "Participants who never owned premium assets do not value them. Free trial activities that let participants experience premium assets drive conversions because ownership creates perceived value (endowment effect).",
2530
2613
  check(metrics, _thresholds) {
2531
2614
  const { avgSatisfaction, churnRate } = metrics;
2532
2615
  const { eventCompletionRate } = metrics;
@@ -2537,10 +2620,10 @@ var P52_EndowmentEffect = {
2537
2620
  severity: 4,
2538
2621
  evidence: { eventCompletionRate, avgSatisfaction, churnRate },
2539
2622
  suggestedAction: {
2540
- parameter: "rewardRate",
2623
+ parameterType: "reward",
2541
2624
  direction: "increase",
2542
2625
  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.`
2626
+ reasoning: `${(eventCompletionRate * 100).toFixed(0)}% activity completion but satisfaction only ${avgSatisfaction.toFixed(0)}. Activities not creating perceived value. Increase reward quality/quantity.`
2544
2627
  },
2545
2628
  confidence: 0.6,
2546
2629
  estimatedLag: 20
@@ -2551,8 +2634,8 @@ var P52_EndowmentEffect = {
2551
2634
  };
2552
2635
  var P53_EventCompletionRate = {
2553
2636
  id: "P53",
2554
- name: "Event Completion Rate Sweet Spot",
2555
- category: "liveops",
2637
+ name: "Activity Completion Rate Sweet Spot",
2638
+ category: "operations",
2556
2639
  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
2640
  check(metrics, thresholds) {
2558
2641
  const { eventCompletionRate } = metrics;
@@ -2567,10 +2650,10 @@ var P53_EventCompletionRate = {
2567
2650
  max: thresholds.eventCompletionMax
2568
2651
  },
2569
2652
  suggestedAction: {
2570
- parameter: "productionCost",
2653
+ parameterType: "cost",
2571
2654
  direction: "decrease",
2572
2655
  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.`
2656
+ 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
2657
  },
2575
2658
  confidence: 0.8,
2576
2659
  estimatedLag: 10
@@ -2582,10 +2665,11 @@ var P53_EventCompletionRate = {
2582
2665
  severity: 3,
2583
2666
  evidence: { eventCompletionRate, max: thresholds.eventCompletionMax },
2584
2667
  suggestedAction: {
2585
- parameter: "entryFee",
2668
+ parameterType: "fee",
2669
+ scope: { tags: ["entry"] },
2586
2670
  direction: "increase",
2587
2671
  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.`
2672
+ 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
2673
  },
2590
2674
  confidence: 0.55,
2591
2675
  estimatedLag: 10
@@ -2594,11 +2678,11 @@ var P53_EventCompletionRate = {
2594
2678
  return { violated: false };
2595
2679
  }
2596
2680
  };
2597
- var P54_LiveOpsCadence = {
2681
+ var P54_OperationalCadence = {
2598
2682
  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.",
2683
+ name: "Operational Cadence",
2684
+ category: "operations",
2685
+ description: ">50% of activities that are re-wrapped existing supply \u2192 stagnation. The cadence must include genuinely new supply at regular intervals. This is an advisory principle \u2014 AgentE can flag but cannot fix supply.",
2602
2686
  check(metrics, _thresholds) {
2603
2687
  const { velocity, avgSatisfaction } = metrics;
2604
2688
  if (velocity < 2 && avgSatisfaction < 55 && metrics.tick > 100) {
@@ -2607,10 +2691,10 @@ var P54_LiveOpsCadence = {
2607
2691
  severity: 3,
2608
2692
  evidence: { velocity, avgSatisfaction, tick: metrics.tick },
2609
2693
  suggestedAction: {
2610
- parameter: "rewardRate",
2694
+ parameterType: "reward",
2611
2695
  direction: "increase",
2612
2696
  magnitude: 0.1,
2613
- reasoning: "Low velocity and satisfaction after long runtime. Possible content staleness. Increase rewards as bridge while new content is developed (developer action required)."
2697
+ reasoning: "Low velocity and satisfaction after long runtime. Possible supply stagnation. Increase rewards as bridge while new supply is developed (developer action required)."
2614
2698
  },
2615
2699
  confidence: 0.4,
2616
2700
  estimatedLag: 30
@@ -2619,11 +2703,11 @@ var P54_LiveOpsCadence = {
2619
2703
  return { violated: false };
2620
2704
  }
2621
2705
  };
2622
- var P56_ContentDropShock = {
2706
+ var P56_SupplyShockAbsorption = {
2623
2707
  id: "P56",
2624
- name: "Content-Drop Shock",
2625
- category: "liveops",
2626
- 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.",
2708
+ name: "Supply Shock Absorption",
2709
+ category: "operations",
2710
+ description: "Every new-item injection shatters existing price equilibria \u2014 arbitrage spikes as participants re-price. Build stabilization windows for price discovery before measuring post-injection economic health.",
2627
2711
  check(metrics, thresholds) {
2628
2712
  const { contentDropAge, arbitrageIndex } = metrics;
2629
2713
  if (contentDropAge > 0 && contentDropAge <= thresholds.contentDropCooldownTicks) {
@@ -2638,10 +2722,11 @@ var P56_ContentDropShock = {
2638
2722
  postDropMax: thresholds.postDropArbitrageMax
2639
2723
  },
2640
2724
  suggestedAction: {
2641
- parameter: "transactionFee",
2725
+ parameterType: "fee",
2726
+ scope: { tags: ["transaction"] },
2642
2727
  direction: "decrease",
2643
2728
  magnitude: 0.1,
2644
- 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.`
2729
+ reasoning: `Supply injection ${contentDropAge} ticks ago \u2014 arbitrage at ${arbitrageIndex.toFixed(2)} exceeds post-injection max (${thresholds.postDropArbitrageMax}). Price discovery struggling. Lower trading friction temporarily.`
2645
2730
  },
2646
2731
  confidence: 0.6,
2647
2732
  estimatedLag: 5
@@ -2651,12 +2736,12 @@ var P56_ContentDropShock = {
2651
2736
  return { violated: false };
2652
2737
  }
2653
2738
  };
2654
- var LIVEOPS_PRINCIPLES = [
2655
- P51_SharkTooth,
2739
+ var OPERATIONS_PRINCIPLES = [
2740
+ P51_CyclicalEngagement,
2656
2741
  P52_EndowmentEffect,
2657
2742
  P53_EventCompletionRate,
2658
- P54_LiveOpsCadence,
2659
- P56_ContentDropShock
2743
+ P54_OperationalCadence,
2744
+ P56_SupplyShockAbsorption
2660
2745
  ];
2661
2746
 
2662
2747
  // src/principles/index.ts
@@ -2685,23 +2770,24 @@ var ALL_PRINCIPLES = [
2685
2770
  // P39, P44
2686
2771
  ...RESOURCE_MGMT_PRINCIPLES,
2687
2772
  // P35, P40, P49
2688
- ...PLAYER_EXPERIENCE_PRINCIPLES,
2773
+ ...PARTICIPANT_EXPERIENCE_PRINCIPLES,
2689
2774
  // P33, P36, P37, P45, P50
2690
2775
  ...OPEN_ECONOMY_PRINCIPLES,
2691
2776
  // P34, P47-P48
2692
- ...LIVEOPS_PRINCIPLES
2777
+ ...OPERATIONS_PRINCIPLES
2693
2778
  // P51-P54, P56
2694
2779
  ];
2695
2780
 
2696
2781
  // src/Simulator.ts
2697
2782
  var Simulator = class {
2698
- constructor() {
2783
+ constructor(registry) {
2699
2784
  this.diagnoser = new Diagnoser(ALL_PRINCIPLES);
2700
2785
  // Cache beforeViolations for the *current* tick only (one entry max).
2701
2786
  // Using a Map here is intentional but the cache must be bounded — we only
2702
2787
  // care about the tick that is currently being evaluated, so we evict any
2703
2788
  // entries whose key differs from the incoming tick.
2704
2789
  this.beforeViolationsCache = /* @__PURE__ */ new Map();
2790
+ this.registry = registry;
2705
2791
  }
2706
2792
  /**
2707
2793
  * Simulate the effect of applying `action` to the current economy forward `forwardTicks`.
@@ -2768,7 +2854,7 @@ var Simulator = class {
2768
2854
  const multiplier = this.actionMultiplier(action);
2769
2855
  const noise = () => 1 + (Math.random() - 0.5) * 0.1;
2770
2856
  const currencies = metrics.currencies;
2771
- const targetCurrency = action.currency;
2857
+ const targetCurrency = action.scope?.currency;
2772
2858
  const supplies = { ...metrics.totalSupplyByCurrency };
2773
2859
  const netFlows = { ...metrics.netFlowByCurrency };
2774
2860
  const ginis = { ...metrics.giniCoefficientByCurrency };
@@ -2810,26 +2896,54 @@ var Simulator = class {
2810
2896
  return action.direction === "increase" ? 1 + base : 1 - base;
2811
2897
  }
2812
2898
  flowEffect(action, metrics, currency) {
2813
- const { parameter, direction } = action;
2899
+ const { direction } = action;
2814
2900
  const sign = direction === "increase" ? -1 : 1;
2815
2901
  const roleEntries = Object.entries(metrics.populationByRole).sort((a, b) => b[1] - a[1]);
2816
2902
  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;
2825
- }
2826
- if (parameter === "rewardRate") {
2827
- return -sign * dominantRoleCount * 0.3;
2903
+ let impact;
2904
+ if (this.registry) {
2905
+ const resolved = this.registry.resolve(action.parameterType, action.scope);
2906
+ if (resolved) {
2907
+ impact = resolved.flowImpact;
2908
+ }
2828
2909
  }
2829
- if (parameter === "yieldRate") {
2830
- return sign * (metrics.faucetVolumeByCurrency[currency] ?? 0) * 0.15;
2910
+ if (!impact) {
2911
+ impact = this.inferFlowImpact(action.parameterType);
2912
+ }
2913
+ switch (impact) {
2914
+ case "sink":
2915
+ return sign * (metrics.netFlowByCurrency[currency] ?? 0) * 0.2;
2916
+ case "faucet":
2917
+ return -sign * dominantRoleCount * 0.3;
2918
+ case "neutral":
2919
+ return sign * dominantRoleCount * 0.5;
2920
+ case "mixed":
2921
+ return sign * (metrics.faucetVolumeByCurrency[currency] ?? 0) * 0.15;
2922
+ case "friction":
2923
+ return sign * (metrics.netFlowByCurrency[currency] ?? 0) * 0.1;
2924
+ case "redistribution":
2925
+ return sign * dominantRoleCount * 0.05;
2926
+ default:
2927
+ return sign * (metrics.netFlowByCurrency[currency] ?? 0) * 0.1;
2928
+ }
2929
+ }
2930
+ /** Infer flow impact from parameter type when registry is unavailable */
2931
+ inferFlowImpact(parameterType) {
2932
+ switch (parameterType) {
2933
+ case "cost":
2934
+ case "fee":
2935
+ case "penalty":
2936
+ return "sink";
2937
+ case "reward":
2938
+ return "faucet";
2939
+ case "yield":
2940
+ return "mixed";
2941
+ case "cap":
2942
+ case "multiplier":
2943
+ return "neutral";
2944
+ default:
2945
+ return "mixed";
2831
2946
  }
2832
- return sign * (metrics.netFlowByCurrency[currency] ?? 0) * 0.1;
2833
2947
  }
2834
2948
  checkImprovement(before, after, action) {
2835
2949
  const satisfactionImproved = after.avgSatisfaction >= before.avgSatisfaction - 2;
@@ -2891,6 +3005,8 @@ var Planner = class {
2891
3005
  this.constraints = /* @__PURE__ */ new Map();
2892
3006
  this.cooldowns = /* @__PURE__ */ new Map();
2893
3007
  // param → last-applied-tick
3008
+ this.typeCooldowns = /* @__PURE__ */ new Map();
3009
+ // type+scope key → last-applied-tick
2894
3010
  this.activePlanCount = 0;
2895
3011
  }
2896
3012
  lock(param) {
@@ -2909,16 +3025,32 @@ var Planner = class {
2909
3025
  * - parameter is still in cooldown
2910
3026
  * - simulation result failed
2911
3027
  * - complexity budget exceeded
3028
+ * - no matching parameter in registry
2912
3029
  */
2913
- plan(diagnosis, metrics, simulationResult, currentParams, thresholds) {
3030
+ plan(diagnosis, metrics, simulationResult, currentParams, thresholds, registry) {
2914
3031
  const action = diagnosis.violation.suggestedAction;
2915
- const param = action.parameter;
3032
+ const typeKey = this.typeCooldownKey(action.parameterType, action.scope);
3033
+ if (this.isTypeCooldown(typeKey, metrics.tick, thresholds.cooldownTicks)) return null;
3034
+ let param;
3035
+ let resolvedBaseline;
3036
+ let scope;
3037
+ if (registry) {
3038
+ const resolved = registry.resolve(action.parameterType, action.scope);
3039
+ if (!resolved) return null;
3040
+ param = resolved.key;
3041
+ resolvedBaseline = resolved.currentValue;
3042
+ scope = resolved.scope;
3043
+ action.resolvedParameter = param;
3044
+ } else {
3045
+ param = action.resolvedParameter ?? action.parameterType;
3046
+ scope = action.scope;
3047
+ }
2916
3048
  if (this.lockedParams.has(param)) return null;
2917
3049
  if (this.isOnCooldown(param, metrics.tick, thresholds.cooldownTicks)) return null;
2918
3050
  if (!simulationResult.netImprovement) return null;
2919
3051
  if (!simulationResult.noNewProblems) return null;
2920
3052
  if (this.activePlanCount >= thresholds.complexityBudgetMax) return null;
2921
- const currentValue = currentParams[param] ?? 1;
3053
+ const currentValue = resolvedBaseline ?? currentParams[param] ?? action.absoluteValue ?? 1;
2922
3054
  const magnitude = Math.min(action.magnitude ?? 0.1, thresholds.maxAdjustmentPercent);
2923
3055
  let targetValue;
2924
3056
  if (action.direction === "set" && action.absoluteValue !== void 0) {
@@ -2938,7 +3070,7 @@ var Planner = class {
2938
3070
  id: `plan_${metrics.tick}_${param}`,
2939
3071
  diagnosis,
2940
3072
  parameter: param,
2941
- ...action.currency !== void 0 ? { currency: action.currency } : {},
3073
+ ...scope !== void 0 ? { scope } : {},
2942
3074
  currentValue,
2943
3075
  targetValue,
2944
3076
  maxChangePercent: thresholds.maxAdjustmentPercent,
@@ -2947,7 +3079,6 @@ var Planner = class {
2947
3079
  metric: "avgSatisfaction",
2948
3080
  direction: "below",
2949
3081
  threshold: Math.max(20, metrics.avgSatisfaction - 10),
2950
- // rollback if sat drops >10 pts
2951
3082
  checkAfterTick: metrics.tick + estimatedLag + 3
2952
3083
  },
2953
3084
  simulationResult,
@@ -2957,11 +3088,17 @@ var Planner = class {
2957
3088
  }
2958
3089
  recordApplied(plan, tick) {
2959
3090
  this.cooldowns.set(plan.parameter, tick);
3091
+ const action = plan.diagnosis.violation.suggestedAction;
3092
+ const typeKey = this.typeCooldownKey(action.parameterType, action.scope);
3093
+ this.typeCooldowns.set(typeKey, tick);
2960
3094
  this.activePlanCount++;
2961
3095
  }
2962
3096
  recordRolledBack(_plan) {
2963
3097
  this.activePlanCount = Math.max(0, this.activePlanCount - 1);
2964
3098
  }
3099
+ recordSettled(_plan) {
3100
+ this.activePlanCount = Math.max(0, this.activePlanCount - 1);
3101
+ }
2965
3102
  isOnCooldown(param, currentTick, cooldownTicks) {
2966
3103
  const lastApplied = this.cooldowns.get(param);
2967
3104
  if (lastApplied === void 0) return false;
@@ -2970,6 +3107,19 @@ var Planner = class {
2970
3107
  /** Reset all cooldowns (useful for testing) */
2971
3108
  resetCooldowns() {
2972
3109
  this.cooldowns.clear();
3110
+ this.typeCooldowns.clear();
3111
+ }
3112
+ typeCooldownKey(type, scope) {
3113
+ const parts = [type];
3114
+ if (scope?.system) parts.push(`sys:${scope.system}`);
3115
+ if (scope?.currency) parts.push(`cur:${scope.currency}`);
3116
+ if (scope?.tags?.length) parts.push(`tags:${scope.tags.sort().join(",")}`);
3117
+ return parts.join("|");
3118
+ }
3119
+ isTypeCooldown(typeKey, currentTick, cooldownTicks) {
3120
+ const lastApplied = this.typeCooldowns.get(typeKey);
3121
+ if (lastApplied === void 0) return false;
3122
+ return currentTick - lastApplied < cooldownTicks;
2973
3123
  }
2974
3124
  };
2975
3125
 
@@ -2980,40 +3130,54 @@ var Executor = class {
2980
3130
  }
2981
3131
  async apply(plan, adapter, currentParams) {
2982
3132
  const originalValue = currentParams[plan.parameter] ?? plan.currentValue;
2983
- await adapter.setParam(plan.parameter, plan.targetValue, plan.currency);
3133
+ await adapter.setParam(plan.parameter, plan.targetValue, plan.scope);
2984
3134
  plan.appliedAt = plan.diagnosis.tick;
2985
3135
  this.activePlans.push({ plan, originalValue });
2986
3136
  }
2987
3137
  /**
2988
3138
  * Check all active plans for rollback conditions.
2989
- * Called every tick after metrics are computed.
2990
- * Returns list of plans that were rolled back.
3139
+ * Returns { rolledBack, settled } plans that were undone and plans that passed their window.
2991
3140
  */
2992
3141
  async checkRollbacks(metrics, adapter) {
2993
3142
  const rolledBack = [];
3143
+ const settled = [];
2994
3144
  const remaining = [];
2995
3145
  for (const active of this.activePlans) {
2996
3146
  const { plan, originalValue } = active;
2997
3147
  const rc = plan.rollbackCondition;
3148
+ const maxActiveTicks = 200;
3149
+ if (plan.appliedAt !== void 0 && metrics.tick - plan.appliedAt > maxActiveTicks) {
3150
+ settled.push(plan);
3151
+ continue;
3152
+ }
2998
3153
  if (metrics.tick < rc.checkAfterTick) {
2999
3154
  remaining.push(active);
3000
3155
  continue;
3001
3156
  }
3002
3157
  const metricValue = this.getMetricValue(metrics, rc.metric);
3158
+ if (Number.isNaN(metricValue)) {
3159
+ console.warn(
3160
+ `[AgentE] Rollback check: metric path '${rc.metric}' resolved to NaN for plan '${plan.id}'. Triggering rollback as fail-safe.`
3161
+ );
3162
+ await adapter.setParam(plan.parameter, originalValue, plan.scope);
3163
+ rolledBack.push(plan);
3164
+ continue;
3165
+ }
3003
3166
  const shouldRollback = rc.direction === "below" ? metricValue < rc.threshold : metricValue > rc.threshold;
3004
3167
  if (shouldRollback) {
3005
- await adapter.setParam(plan.parameter, originalValue, plan.currency);
3168
+ await adapter.setParam(plan.parameter, originalValue, plan.scope);
3006
3169
  rolledBack.push(plan);
3007
3170
  } else {
3008
3171
  const settledTick = rc.checkAfterTick + 10;
3009
3172
  if (metrics.tick > settledTick) {
3173
+ settled.push(plan);
3010
3174
  } else {
3011
3175
  remaining.push(active);
3012
3176
  }
3013
3177
  }
3014
3178
  }
3015
3179
  this.activePlans = remaining;
3016
- return rolledBack;
3180
+ return { rolledBack, settled };
3017
3181
  }
3018
3182
  getMetricValue(metrics, metricPath) {
3019
3183
  const parts = metricPath.split(".");
@@ -3102,7 +3266,8 @@ var DecisionLog = class {
3102
3266
  return {
3103
3267
  id: `stub_${metrics.tick}`,
3104
3268
  diagnosis,
3105
- parameter: action.parameter,
3269
+ parameter: action.resolvedParameter ?? action.parameterType,
3270
+ ...action.scope !== void 0 ? { scope: action.scope } : {},
3106
3271
  currentValue: 1,
3107
3272
  targetValue: 1,
3108
3273
  maxChangePercent: 0,
@@ -3340,13 +3505,13 @@ var PersonaTracker = class {
3340
3505
  Earner: 0,
3341
3506
  Builder: 0,
3342
3507
  Social: 0,
3343
- Whale: 0,
3508
+ HighValue: 0,
3344
3509
  Influencer: 0
3345
3510
  };
3346
3511
  let total = 0;
3347
3512
  for (const [, history] of this.agentHistory) {
3348
3513
  const persona = this.classify(history);
3349
- counts[persona]++;
3514
+ counts[persona] = (counts[persona] ?? 0) + 1;
3350
3515
  total++;
3351
3516
  }
3352
3517
  if (total === 0) return {};
@@ -3366,7 +3531,7 @@ var PersonaTracker = class {
3366
3531
  const extraction = avg("netExtraction");
3367
3532
  const uniqueItems = avg("uniqueItemsHeld");
3368
3533
  const spend = avg("spendAmount");
3369
- if (spend > 1e3) return "Whale";
3534
+ if (spend > 1e3) return "HighValue";
3370
3535
  if (txRate > 10) return "Trader";
3371
3536
  if (uniqueItems > 5 && extraction < 0) return "Collector";
3372
3537
  if (extraction > 100) return "Earner";
@@ -3375,12 +3540,153 @@ var PersonaTracker = class {
3375
3540
  }
3376
3541
  };
3377
3542
 
3543
+ // src/ParameterRegistry.ts
3544
+ var ParameterRegistry = class {
3545
+ constructor() {
3546
+ this.parameters = /* @__PURE__ */ new Map();
3547
+ }
3548
+ /** Register a parameter. Overwrites if key already exists. */
3549
+ register(param) {
3550
+ this.parameters.set(param.key, { ...param });
3551
+ }
3552
+ /** Register multiple parameters at once. */
3553
+ registerAll(params) {
3554
+ for (const p of params) this.register(p);
3555
+ }
3556
+ /**
3557
+ * Resolve a parameterType + scope to a concrete RegisteredParameter.
3558
+ * Returns the best match, or undefined if no match.
3559
+ *
3560
+ * Matching rules:
3561
+ * 1. Filter candidates by type
3562
+ * 2. Score each by scope specificity (system +10, currency +5, tags +3 each)
3563
+ * 3. Mismatched scope fields disqualify (score = -Infinity)
3564
+ * 4. Ties broken by `priority` (higher wins), then registration order
3565
+ * 5. All disqualified → undefined
3566
+ */
3567
+ resolve(type, scope) {
3568
+ const candidates = this.findByType(type);
3569
+ if (candidates.length === 0) return void 0;
3570
+ if (candidates.length === 1) return candidates[0];
3571
+ let bestScore = -Infinity;
3572
+ let bestPriority = -Infinity;
3573
+ let best;
3574
+ for (const candidate of candidates) {
3575
+ const score = this.scopeSpecificity(candidate.scope, scope);
3576
+ const prio = candidate.priority ?? 0;
3577
+ if (score > bestScore || score === bestScore && prio > bestPriority) {
3578
+ bestScore = score;
3579
+ bestPriority = prio;
3580
+ best = candidate;
3581
+ }
3582
+ }
3583
+ if (bestScore === -Infinity) return void 0;
3584
+ return best;
3585
+ }
3586
+ /** Find all parameters of a given type. */
3587
+ findByType(type) {
3588
+ const results = [];
3589
+ for (const param of this.parameters.values()) {
3590
+ if (param.type === type) results.push(param);
3591
+ }
3592
+ return results;
3593
+ }
3594
+ /** Find all parameters belonging to a given system. */
3595
+ findBySystem(system) {
3596
+ const results = [];
3597
+ for (const param of this.parameters.values()) {
3598
+ if (param.scope?.system === system) results.push(param);
3599
+ }
3600
+ return results;
3601
+ }
3602
+ /** Get a parameter by its concrete key. */
3603
+ get(key) {
3604
+ return this.parameters.get(key);
3605
+ }
3606
+ /** Get the flow impact of a parameter by its concrete key. */
3607
+ getFlowImpact(key) {
3608
+ return this.parameters.get(key)?.flowImpact;
3609
+ }
3610
+ /** Update the current value of a registered parameter. */
3611
+ updateValue(key, value) {
3612
+ const param = this.parameters.get(key);
3613
+ if (param) {
3614
+ param.currentValue = value;
3615
+ }
3616
+ }
3617
+ /** Get all registered parameters. */
3618
+ getAll() {
3619
+ return [...this.parameters.values()];
3620
+ }
3621
+ /** Number of registered parameters. */
3622
+ get size() {
3623
+ return this.parameters.size;
3624
+ }
3625
+ /**
3626
+ * Validate the registry for common misconfigurations.
3627
+ * Returns warnings (non-fatal) and errors (likely broken).
3628
+ */
3629
+ validate() {
3630
+ const warnings = [];
3631
+ const errors = [];
3632
+ const typeMap = /* @__PURE__ */ new Map();
3633
+ for (const param of this.parameters.values()) {
3634
+ const list = typeMap.get(param.type) ?? [];
3635
+ list.push(param);
3636
+ typeMap.set(param.type, list);
3637
+ }
3638
+ for (const [type, params] of typeMap) {
3639
+ if (params.length > 1) {
3640
+ const unscopedCount = params.filter((p) => !p.scope).length;
3641
+ if (unscopedCount > 1) {
3642
+ errors.push(
3643
+ `Type '${type}' has ${unscopedCount} unscoped parameters \u2014 resolve() cannot distinguish them`
3644
+ );
3645
+ }
3646
+ }
3647
+ }
3648
+ for (const param of this.parameters.values()) {
3649
+ if (!param.flowImpact) {
3650
+ warnings.push(`Parameter '${param.key}' has no flowImpact \u2014 Simulator will use inference`);
3651
+ }
3652
+ }
3653
+ return {
3654
+ valid: errors.length === 0,
3655
+ warnings,
3656
+ errors
3657
+ };
3658
+ }
3659
+ // ── Private ─────────────────────────────────────────────────────────────
3660
+ scopeSpecificity(paramScope, queryScope) {
3661
+ if (!queryScope) return 0;
3662
+ if (!paramScope) return 0;
3663
+ let score = 0;
3664
+ if (queryScope.system && paramScope.system) {
3665
+ if (queryScope.system === paramScope.system) score += 10;
3666
+ else return -Infinity;
3667
+ }
3668
+ if (queryScope.currency && paramScope.currency) {
3669
+ if (queryScope.currency === paramScope.currency) score += 5;
3670
+ else return -Infinity;
3671
+ }
3672
+ if (queryScope.tags && queryScope.tags.length > 0 && paramScope.tags && paramScope.tags.length > 0) {
3673
+ const overlap = queryScope.tags.filter((t) => paramScope.tags.includes(t)).length;
3674
+ if (overlap > 0) {
3675
+ score += overlap * 3;
3676
+ } else {
3677
+ return -Infinity;
3678
+ }
3679
+ }
3680
+ return score;
3681
+ }
3682
+ };
3683
+
3378
3684
  // src/AgentE.ts
3379
3685
  var AgentE = class {
3380
3686
  constructor(config) {
3381
- this.simulator = new Simulator();
3382
3687
  this.planner = new Planner();
3383
3688
  this.executor = new Executor();
3689
+ this.registry = new ParameterRegistry();
3384
3690
  // ── State ──
3385
3691
  this.log = new DecisionLog();
3386
3692
  this.personaTracker = new PersonaTracker();
@@ -3401,11 +3707,13 @@ var AgentE = class {
3401
3707
  mode: this.mode,
3402
3708
  dominantRoles: config.dominantRoles ?? [],
3403
3709
  idealDistribution: config.idealDistribution ?? {},
3710
+ validateRegistry: config.validateRegistry ?? true,
3404
3711
  tickConfig: config.tickConfig ?? { duration: 1, unit: "tick" },
3405
3712
  gracePeriod: config.gracePeriod ?? 50,
3406
3713
  checkInterval: config.checkInterval ?? 5,
3407
3714
  maxAdjustmentPercent: config.maxAdjustmentPercent ?? 0.15,
3408
- cooldownTicks: config.cooldownTicks ?? 15
3715
+ cooldownTicks: config.cooldownTicks ?? 15,
3716
+ parameters: config.parameters ?? []
3409
3717
  };
3410
3718
  this.thresholds = {
3411
3719
  ...DEFAULT_THRESHOLDS,
@@ -3417,6 +3725,15 @@ var AgentE = class {
3417
3725
  this.observer = new Observer(tickConfig);
3418
3726
  this.store = new MetricStore(tickConfig);
3419
3727
  this.diagnoser = new Diagnoser(ALL_PRINCIPLES);
3728
+ if (config.parameters) {
3729
+ this.registry.registerAll(config.parameters);
3730
+ }
3731
+ if (config.validateRegistry !== false && this.registry.size > 0) {
3732
+ const validation = this.registry.validate();
3733
+ for (const w of validation.warnings) console.warn(`[AgentE] Registry warning: ${w}`);
3734
+ for (const e of validation.errors) console.error(`[AgentE] Registry error: ${e}`);
3735
+ }
3736
+ this.simulator = new Simulator(this.registry);
3420
3737
  if (config.onDecision) this.on("decision", config.onDecision);
3421
3738
  if (config.onAlert) this.on("alert", config.onAlert);
3422
3739
  if (config.onRollback) this.on("rollback", config.onRollback);
@@ -3454,15 +3771,24 @@ var AgentE = class {
3454
3771
  this.currentTick = currentState.tick;
3455
3772
  const events = [...this.eventBuffer];
3456
3773
  this.eventBuffer = [];
3457
- const metrics = this.observer.compute(currentState, events);
3774
+ let metrics;
3775
+ try {
3776
+ metrics = this.observer.compute(currentState, events);
3777
+ } catch (err) {
3778
+ console.error(`[AgentE] Observer.compute() failed at tick ${currentState.tick}:`, err);
3779
+ return;
3780
+ }
3458
3781
  this.store.record(metrics);
3459
3782
  this.personaTracker.update(currentState);
3460
3783
  metrics.personaDistribution = this.personaTracker.getDistribution();
3461
- const rolledBack = await this.executor.checkRollbacks(metrics, this.adapter);
3784
+ const { rolledBack, settled } = await this.executor.checkRollbacks(metrics, this.adapter);
3462
3785
  for (const plan2 of rolledBack) {
3463
3786
  this.planner.recordRolledBack(plan2);
3464
3787
  this.emit("rollback", plan2, "rollback condition triggered");
3465
3788
  }
3789
+ for (const plan2 of settled) {
3790
+ this.planner.recordSettled(plan2);
3791
+ }
3466
3792
  if (metrics.tick < this.config.gracePeriod) return;
3467
3793
  if (metrics.tick % this.config.checkInterval !== 0) return;
3468
3794
  const diagnoses = this.diagnoser.diagnose(metrics, this.thresholds);
@@ -3484,7 +3810,8 @@ var AgentE = class {
3484
3810
  metrics,
3485
3811
  simulationResult,
3486
3812
  this.params,
3487
- this.thresholds
3813
+ this.thresholds,
3814
+ this.registry
3488
3815
  );
3489
3816
  if (!plan) {
3490
3817
  let reason = "skipped_cooldown";
@@ -3504,6 +3831,7 @@ var AgentE = class {
3504
3831
  }
3505
3832
  await this.executor.apply(plan, this.adapter, this.params);
3506
3833
  this.params[plan.parameter] = plan.targetValue;
3834
+ this.registry.updateValue(plan.parameter, plan.targetValue);
3507
3835
  this.planner.recordApplied(plan, metrics.tick);
3508
3836
  const entry = this.log.record(topDiagnosis, plan, "applied", metrics);
3509
3837
  this.emit("decision", entry);
@@ -3513,6 +3841,7 @@ var AgentE = class {
3513
3841
  async apply(plan) {
3514
3842
  await this.executor.apply(plan, this.adapter, this.params);
3515
3843
  this.params[plan.parameter] = plan.targetValue;
3844
+ this.registry.updateValue(plan.parameter, plan.targetValue);
3516
3845
  this.planner.recordApplied(plan, this.currentTick);
3517
3846
  }
3518
3847
  // ── Developer API ───────────────────────────────────────────────────────────
@@ -3537,6 +3866,12 @@ var AgentE = class {
3537
3866
  removePrinciple(id) {
3538
3867
  this.diagnoser.removePrinciple(id);
3539
3868
  }
3869
+ registerParameter(param) {
3870
+ this.registry.register(param);
3871
+ }
3872
+ getRegistry() {
3873
+ return this.registry;
3874
+ }
3540
3875
  registerCustomMetric(name, fn) {
3541
3876
  this.observer.registerCustomMetric(name, fn);
3542
3877
  }
@@ -3594,6 +3929,31 @@ var AgentE = class {
3594
3929
  }
3595
3930
  };
3596
3931
 
3932
+ // src/utils.ts
3933
+ function findWorstSystem(metrics, check, tolerancePercent = 0) {
3934
+ const systems = metrics.systems;
3935
+ if (systems.length === 0) return void 0;
3936
+ let worstSystem;
3937
+ let worstScore = -Infinity;
3938
+ let totalScore = 0;
3939
+ for (const sys of systems) {
3940
+ const score = check(sys, metrics);
3941
+ totalScore += score;
3942
+ if (score > worstScore) {
3943
+ worstScore = score;
3944
+ worstSystem = sys;
3945
+ }
3946
+ }
3947
+ if (!worstSystem) return void 0;
3948
+ if (tolerancePercent > 0 && systems.length > 1) {
3949
+ const avg = totalScore / systems.length;
3950
+ if (avg === 0) return { system: worstSystem, score: worstScore };
3951
+ const excessPercent = (worstScore - avg) / Math.abs(avg) * 100;
3952
+ if (excessPercent < tolerancePercent) return void 0;
3953
+ }
3954
+ return { system: worstSystem, score: worstScore };
3955
+ }
3956
+
3597
3957
  // src/StateValidator.ts
3598
3958
  function validateEconomyState(state) {
3599
3959
  const errors = [];
@@ -3938,13 +4298,13 @@ export {
3938
4298
  Executor,
3939
4299
  FEEDBACK_LOOP_PRINCIPLES,
3940
4300
  INCENTIVE_PRINCIPLES,
3941
- LIVEOPS_PRINCIPLES,
3942
4301
  MARKET_DYNAMICS_PRINCIPLES,
3943
4302
  MEASUREMENT_PRINCIPLES,
3944
4303
  MetricStore,
3945
4304
  OPEN_ECONOMY_PRINCIPLES,
4305
+ OPERATIONS_PRINCIPLES,
3946
4306
  Observer,
3947
- P10_SpawnWeightingUsesInversePopulation,
4307
+ P10_EntryWeightingUsesInversePopulation,
3948
4308
  P11_TwoTierPressure,
3949
4309
  P12_OnePrimaryFaucet,
3950
4310
  P13_PotsAreZeroSumAndSelfRegulate,
@@ -3964,9 +4324,9 @@ export {
3964
4324
  P26_ContinuousPressureBeatsThresholdCuts,
3965
4325
  P27_AdjustmentsNeedCooldowns,
3966
4326
  P28_StructuralDominanceIsNotPathological,
3967
- P29_PinchPoint,
4327
+ P29_BottleneckDetection,
3968
4328
  P2_ClosedLoopsNeedDirectHandoff,
3969
- P30_MovingPinchPoint,
4329
+ P30_DynamicBottleneckRotation,
3970
4330
  P31_AnchorValueTracking,
3971
4331
  P32_VelocityAboveSupply,
3972
4332
  P33_FairNotEqual,
@@ -3989,12 +4349,12 @@ export {
3989
4349
  P49_IdleAssetTax,
3990
4350
  P4_MaterialsFlowFasterThanCooldown,
3991
4351
  P50_PayPowerRatio,
3992
- P51_SharkTooth,
4352
+ P51_CyclicalEngagement,
3993
4353
  P52_EndowmentEffect,
3994
4354
  P53_EventCompletionRate,
3995
- P54_LiveOpsCadence,
4355
+ P54_OperationalCadence,
3996
4356
  P55_ArbitrageThermometer,
3997
- P56_ContentDropShock,
4357
+ P56_SupplyShockAbsorption,
3998
4358
  P57_CombinatorialPriceSpace,
3999
4359
  P58_NoNaturalNumeraire,
4000
4360
  P59_GiftEconomyNoise,
@@ -4004,9 +4364,10 @@ export {
4004
4364
  P7_NonSpecialistsSubsidiseSpecialists,
4005
4365
  P8_RegulatorCannotFightDesign,
4006
4366
  P9_RoleSwitchingNeedsFriction,
4367
+ PARTICIPANT_EXPERIENCE_PRINCIPLES,
4007
4368
  PERSONA_HEALTHY_RANGES,
4008
- PLAYER_EXPERIENCE_PRINCIPLES,
4009
4369
  POPULATION_PRINCIPLES,
4370
+ ParameterRegistry,
4010
4371
  PersonaTracker,
4011
4372
  Planner,
4012
4373
  REGULATOR_PRINCIPLES,
@@ -4016,6 +4377,7 @@ export {
4016
4377
  SYSTEM_DYNAMICS_PRINCIPLES,
4017
4378
  Simulator,
4018
4379
  emptyMetrics,
4380
+ findWorstSystem,
4019
4381
  validateEconomyState
4020
4382
  };
4021
4383
  //# sourceMappingURL=index.mjs.map