@agent-e/core 1.4.4 → 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.js CHANGED
@@ -30,11 +30,11 @@ __export(index_exports, {
30
30
  Executor: () => Executor,
31
31
  FEEDBACK_LOOP_PRINCIPLES: () => FEEDBACK_LOOP_PRINCIPLES,
32
32
  INCENTIVE_PRINCIPLES: () => INCENTIVE_PRINCIPLES,
33
- LIVEOPS_PRINCIPLES: () => LIVEOPS_PRINCIPLES,
34
33
  MARKET_DYNAMICS_PRINCIPLES: () => MARKET_DYNAMICS_PRINCIPLES,
35
34
  MEASUREMENT_PRINCIPLES: () => MEASUREMENT_PRINCIPLES,
36
35
  MetricStore: () => MetricStore,
37
36
  OPEN_ECONOMY_PRINCIPLES: () => OPEN_ECONOMY_PRINCIPLES,
37
+ OPERATIONS_PRINCIPLES: () => OPERATIONS_PRINCIPLES,
38
38
  Observer: () => Observer,
39
39
  P10_SpawnWeightingUsesInversePopulation: () => P10_SpawnWeightingUsesInversePopulation,
40
40
  P11_TwoTierPressure: () => P11_TwoTierPressure,
@@ -96,9 +96,10 @@ __export(index_exports, {
96
96
  P7_NonSpecialistsSubsidiseSpecialists: () => P7_NonSpecialistsSubsidiseSpecialists,
97
97
  P8_RegulatorCannotFightDesign: () => P8_RegulatorCannotFightDesign,
98
98
  P9_RoleSwitchingNeedsFriction: () => P9_RoleSwitchingNeedsFriction,
99
+ PARTICIPANT_EXPERIENCE_PRINCIPLES: () => PARTICIPANT_EXPERIENCE_PRINCIPLES,
99
100
  PERSONA_HEALTHY_RANGES: () => PERSONA_HEALTHY_RANGES,
100
- PLAYER_EXPERIENCE_PRINCIPLES: () => PLAYER_EXPERIENCE_PRINCIPLES,
101
101
  POPULATION_PRINCIPLES: () => POPULATION_PRINCIPLES,
102
+ ParameterRegistry: () => ParameterRegistry,
102
103
  PersonaTracker: () => PersonaTracker,
103
104
  Planner: () => Planner,
104
105
  REGULATOR_PRINCIPLES: () => REGULATOR_PRINCIPLES,
@@ -126,11 +127,11 @@ var DEFAULT_THRESHOLDS = {
126
127
  smokeTestWarning: 0.3,
127
128
  smokeTestCritical: 0.1,
128
129
  currencyInsulationMax: 0.5,
129
- // Player Experience (P45, P50)
130
+ // Participant Experience (P45, P50)
130
131
  timeBudgetRatio: 0.8,
131
132
  payPowerRatioMax: 2,
132
133
  payPowerRatioTarget: 1.5,
133
- // LiveOps (P51, P53)
134
+ // Operations (P51, P53)
134
135
  sharkToothPeakDecay: 0.95,
135
136
  sharkToothValleyDecay: 0.9,
136
137
  eventCompletionMin: 0.4,
@@ -184,7 +185,7 @@ var PERSONA_HEALTHY_RANGES = {
184
185
  Earner: { min: 0, max: 0.15 },
185
186
  Builder: { min: 0.05, max: 0.15 },
186
187
  Social: { min: 0.1, max: 0.2 },
187
- Whale: { min: 0, max: 0.05 },
188
+ HighValue: { min: 0, max: 0.05 },
188
189
  Influencer: { min: 0, max: 0.05 }
189
190
  };
190
191
  var DEFAULT_TICK_CONFIG = {
@@ -221,7 +222,7 @@ var Observer = class {
221
222
  const curr = e.currency ?? defaultCurrency;
222
223
  switch (e.type) {
223
224
  case "mint":
224
- case "spawn":
225
+ case "enter":
225
226
  faucetVolumeByCurrency[curr] = (faucetVolumeByCurrency[curr] ?? 0) + (e.amount ?? 0);
226
227
  break;
227
228
  case "burn":
@@ -243,6 +244,46 @@ var Observer = class {
243
244
  break;
244
245
  }
245
246
  }
247
+ const flowBySystem = {};
248
+ const activityBySystem = {};
249
+ const actorsBySystem = {};
250
+ const flowBySource = {};
251
+ const flowBySink = {};
252
+ for (const e of recentEvents) {
253
+ if (e.system) {
254
+ activityBySystem[e.system] = (activityBySystem[e.system] ?? 0) + 1;
255
+ if (!actorsBySystem[e.system]) actorsBySystem[e.system] = /* @__PURE__ */ new Set();
256
+ actorsBySystem[e.system].add(e.actor);
257
+ const amt = e.amount ?? 0;
258
+ if (e.type === "mint" || e.type === "enter") {
259
+ flowBySystem[e.system] = (flowBySystem[e.system] ?? 0) + amt;
260
+ } else if (e.type === "burn" || e.type === "consume") {
261
+ flowBySystem[e.system] = (flowBySystem[e.system] ?? 0) - amt;
262
+ }
263
+ }
264
+ if (e.sourceOrSink) {
265
+ const amt = e.amount ?? 0;
266
+ if (e.type === "mint" || e.type === "enter") {
267
+ flowBySource[e.sourceOrSink] = (flowBySource[e.sourceOrSink] ?? 0) + amt;
268
+ } else if (e.type === "burn" || e.type === "consume") {
269
+ flowBySink[e.sourceOrSink] = (flowBySink[e.sourceOrSink] ?? 0) + amt;
270
+ }
271
+ }
272
+ }
273
+ const participantsBySystem = {};
274
+ for (const [sys, actors] of Object.entries(actorsBySystem)) {
275
+ participantsBySystem[sys] = actors.size;
276
+ }
277
+ const totalSourceFlow = Object.values(flowBySource).reduce((s, v) => s + v, 0);
278
+ const sourceShare = {};
279
+ for (const [src, vol] of Object.entries(flowBySource)) {
280
+ sourceShare[src] = totalSourceFlow > 0 ? vol / totalSourceFlow : 0;
281
+ }
282
+ const totalSinkFlow = Object.values(flowBySink).reduce((s, v) => s + v, 0);
283
+ const sinkShare = {};
284
+ for (const [snk, vol] of Object.entries(flowBySink)) {
285
+ sinkShare[snk] = totalSinkFlow > 0 ? vol / totalSinkFlow : 0;
286
+ }
246
287
  const currencies = state.currencies;
247
288
  const totalSupplyByCurrency = {};
248
289
  const balancesByCurrency = {};
@@ -452,7 +493,8 @@ var Observer = class {
452
493
  for (const [name, fn] of Object.entries(this.customMetricFns)) {
453
494
  try {
454
495
  custom[name] = fn(state);
455
- } catch {
496
+ } catch (err) {
497
+ console.warn(`[AgentE] Custom metric '${name}' threw an error:`, err);
456
498
  custom[name] = NaN;
457
499
  }
458
500
  }
@@ -529,6 +571,13 @@ var Observer = class {
529
571
  sharkToothValleys: this.previousMetrics?.sharkToothValleys ?? [],
530
572
  eventCompletionRate: NaN,
531
573
  contentDropAge,
574
+ flowBySystem,
575
+ activityBySystem,
576
+ participantsBySystem,
577
+ flowBySource,
578
+ flowBySink,
579
+ sourceShare,
580
+ sinkShare,
532
581
  custom
533
582
  };
534
583
  this.previousMetrics = metrics;
@@ -671,6 +720,13 @@ function emptyMetrics(tick = 0) {
671
720
  sharkToothValleys: [],
672
721
  eventCompletionRate: NaN,
673
722
  contentDropAge: 0,
723
+ flowBySystem: {},
724
+ activityBySystem: {},
725
+ participantsBySystem: {},
726
+ flowBySource: {},
727
+ flowBySink: {},
728
+ sourceShare: {},
729
+ sinkShare: {},
674
730
  custom: {}
675
731
  };
676
732
  }
@@ -703,7 +759,7 @@ var P1_ProductionMatchesConsumption = {
703
759
  severity: 7,
704
760
  evidence: { scarceResources: violations, dominantRole: dominantRole?.[0], dominantShare },
705
761
  suggestedAction: {
706
- parameter: "productionCost",
762
+ parameterType: "cost",
707
763
  direction: "decrease",
708
764
  magnitude: 0.15,
709
765
  reasoning: "Lower production cost to incentivise more production."
@@ -737,7 +793,8 @@ var P2_ClosedLoopsNeedDirectHandoff = {
737
793
  severity: 5,
738
794
  evidence: { backlogResources, velocity },
739
795
  suggestedAction: {
740
- parameter: "transactionFee",
796
+ parameterType: "fee",
797
+ scope: { tags: ["transaction"] },
741
798
  direction: "increase",
742
799
  magnitude: 0.2,
743
800
  reasoning: "Raise market fees to discourage raw material listings. Direct hand-off at production zones is the correct channel."
@@ -767,7 +824,7 @@ var P3_BootstrapCapitalCoversFirstTransaction = {
767
824
  severity: 8,
768
825
  evidence: { resource, totalProducers, supply },
769
826
  suggestedAction: {
770
- parameter: "productionCost",
827
+ parameterType: "cost",
771
828
  direction: "decrease",
772
829
  magnitude: 0.3,
773
830
  reasoning: "Producers cannot complete first transaction. Lower production cost to unblock bootstrap."
@@ -786,7 +843,7 @@ var P4_MaterialsFlowFasterThanCooldown = {
786
843
  id: "P4",
787
844
  name: "Materials Flow Faster Than Cooldown",
788
845
  category: "supply_chain",
789
- 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.",
846
+ 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.",
790
847
  check(metrics, _thresholds) {
791
848
  const { supplyByResource, populationByRole, velocity, totalAgents } = metrics;
792
849
  const totalSupply = Object.values(supplyByResource).reduce((s, v) => s + v, 0);
@@ -799,7 +856,7 @@ var P4_MaterialsFlowFasterThanCooldown = {
799
856
  severity: 5,
800
857
  evidence: { avgSupplyPerAgent, velocity, totalRoles },
801
858
  suggestedAction: {
802
- parameter: "yieldRate",
859
+ parameterType: "yield",
803
860
  direction: "increase",
804
861
  magnitude: 0.15,
805
862
  reasoning: "Low supply per agent with stagnant velocity. Increase yield to compensate."
@@ -814,7 +871,7 @@ var P4_MaterialsFlowFasterThanCooldown = {
814
871
  severity: 4,
815
872
  evidence: { avgSupplyPerAgent, totalSupply, totalAgents },
816
873
  suggestedAction: {
817
- parameter: "yieldRate",
874
+ parameterType: "yield",
818
875
  direction: "decrease",
819
876
  magnitude: 0.2,
820
877
  reasoning: "Raw materials piling up. Extractors outpacing producers."
@@ -842,7 +899,7 @@ var P60_SurplusDisposalAsymmetry = {
842
899
  discount: thresholds.disposalTradeWeightDiscount
843
900
  },
844
901
  suggestedAction: {
845
- parameter: "productionCost",
902
+ parameterType: "cost",
846
903
  direction: "decrease",
847
904
  magnitude: 0.1,
848
905
  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.`
@@ -885,7 +942,8 @@ var P5_ProfitabilityIsCompetitive = {
885
942
  population: populationByRole[dominantRole]
886
943
  },
887
944
  suggestedAction: {
888
- parameter: "transactionFee",
945
+ parameterType: "fee",
946
+ scope: { tags: ["transaction"] },
889
947
  direction: "increase",
890
948
  magnitude: thresholds.maxAdjustmentPercent,
891
949
  reasoning: `${dominantRole} share at ${((roleShares[dominantRole] ?? 0) * 100).toFixed(0)}%. Likely stampede from non-competitive profitability formula. Raise market friction to slow role accumulation.`
@@ -911,7 +969,7 @@ var P6_CrowdingMultiplierOnAllRoles = {
911
969
  severity: 5,
912
970
  evidence: { role, share },
913
971
  suggestedAction: {
914
- parameter: "productionCost",
972
+ parameterType: "cost",
915
973
  direction: "increase",
916
974
  magnitude: 0.1,
917
975
  reasoning: `${role} at ${(share * 100).toFixed(0)}% \u2014 no crowding pressure detected. Apply role-specific cost increase to simulate saturation.`
@@ -946,7 +1004,8 @@ var P7_NonSpecialistsSubsidiseSpecialists = {
946
1004
  severity: 6,
947
1005
  evidence: { poolName, poolSize, dominantRole, dominantShare },
948
1006
  suggestedAction: {
949
- parameter: "entryFee",
1007
+ parameterType: "fee",
1008
+ scope: { tags: ["entry"] },
950
1009
  direction: "decrease",
951
1010
  magnitude: 0.1,
952
1011
  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.`
@@ -974,7 +1033,7 @@ var P8_RegulatorCannotFightDesign = {
974
1033
  severity: 4,
975
1034
  evidence: { dominantRole: dominantRole[0], share: dominantRole[1], avgSatisfaction },
976
1035
  suggestedAction: {
977
- parameter: "rewardRate",
1036
+ parameterType: "reward",
978
1037
  direction: "increase",
979
1038
  magnitude: 0.1,
980
1039
  reasoning: `Low satisfaction with ${dominantRole[0]} dominant. Regulator may be suppressing a structurally necessary role. Ease pressure on dominant role rewards.`
@@ -1009,7 +1068,7 @@ var P9_RoleSwitchingNeedsFriction = {
1009
1068
  severity: 5,
1010
1069
  evidence: { totalChurnRate: totalChurn, churnByRole },
1011
1070
  suggestedAction: {
1012
- parameter: "productionCost",
1071
+ parameterType: "cost",
1013
1072
  direction: "increase",
1014
1073
  magnitude: 0.05,
1015
1074
  reasoning: `Role switch rate ${(totalChurn * 100).toFixed(1)}% exceeds friction threshold. Increase production costs to slow herd movement.`
@@ -1024,9 +1083,9 @@ var P9_RoleSwitchingNeedsFriction = {
1024
1083
  };
1025
1084
  var P10_SpawnWeightingUsesInversePopulation = {
1026
1085
  id: "P10",
1027
- name: "Spawn Weighting Uses Inverse Population",
1086
+ name: "Entry Weighting Uses Inverse Population",
1028
1087
  category: "population",
1029
- description: "New agents should preferentially fill the least-populated roles. Flat entry probability causes initial imbalances to compound.",
1088
+ description: "New entrants should preferentially fill the least-populated roles. Flat entry probability causes initial imbalances to compound.",
1030
1089
  check(metrics, _thresholds) {
1031
1090
  const { roleShares } = metrics;
1032
1091
  if (Object.keys(roleShares).length === 0) return { violated: false };
@@ -1041,7 +1100,7 @@ var P10_SpawnWeightingUsesInversePopulation = {
1041
1100
  severity: 4,
1042
1101
  evidence: { roleShares, stdDev, leastPopulatedRole: minRole?.[0] },
1043
1102
  suggestedAction: {
1044
- parameter: "yieldRate",
1103
+ parameterType: "yield",
1045
1104
  direction: "increase",
1046
1105
  magnitude: 0.05,
1047
1106
  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.`
@@ -1067,7 +1126,8 @@ var P11_TwoTierPressure = {
1067
1126
  severity: 6,
1068
1127
  evidence: { role, share },
1069
1128
  suggestedAction: {
1070
- parameter: "transactionFee",
1129
+ parameterType: "fee",
1130
+ scope: { tags: ["transaction"] },
1071
1131
  direction: "increase",
1072
1132
  magnitude: 0.15,
1073
1133
  reasoning: `${role} at ${(share * 100).toFixed(0)}% \u2014 continuous pressure was insufficient. Hard intervention needed alongside resumed continuous pressure.`
@@ -1095,7 +1155,7 @@ var P46_PersonaDiversity = {
1095
1155
  severity: 5,
1096
1156
  evidence: { dominantPersona: persona, share, personaDistribution },
1097
1157
  suggestedAction: {
1098
- parameter: "rewardRate",
1158
+ parameterType: "reward",
1099
1159
  direction: "increase",
1100
1160
  magnitude: 0.1,
1101
1161
  reasoning: `${persona} persona at ${(share * 100).toFixed(0)}% \u2014 behavioral monoculture. Diversify reward structures to attract other persona types.`
@@ -1112,7 +1172,8 @@ var P46_PersonaDiversity = {
1112
1172
  severity: 3,
1113
1173
  evidence: { significantClusters, required: thresholds.personaMinClusters },
1114
1174
  suggestedAction: {
1115
- parameter: "transactionFee",
1175
+ parameterType: "fee",
1176
+ scope: { tags: ["transaction"] },
1116
1177
  direction: "decrease",
1117
1178
  magnitude: 0.05,
1118
1179
  reasoning: `Only ${significantClusters} significant persona clusters (need ${thresholds.personaMinClusters}). Lower trade barriers to attract non-dominant persona types.`
@@ -1136,7 +1197,7 @@ var P12_OnePrimaryFaucet = {
1136
1197
  id: "P12",
1137
1198
  name: "One Primary Faucet",
1138
1199
  category: "currency",
1139
- description: "Multiple independent currency sources (gathering + production + quests) each creating currency causes uncontrolled inflation. One clear primary faucet makes the economy predictable and auditable.",
1200
+ description: "Multiple independent currency sources (gathering + production + activities) each creating currency causes uncontrolled inflation. One clear primary faucet makes the economy predictable and auditable.",
1140
1201
  check(metrics, thresholds) {
1141
1202
  for (const curr of metrics.currencies) {
1142
1203
  const netFlow = metrics.netFlowByCurrency[curr] ?? 0;
@@ -1148,9 +1209,9 @@ var P12_OnePrimaryFaucet = {
1148
1209
  severity: 5,
1149
1210
  evidence: { currency: curr, netFlow, faucetVolume, sinkVolume },
1150
1211
  suggestedAction: {
1151
- parameter: "productionCost",
1212
+ parameterType: "cost",
1152
1213
  direction: "increase",
1153
- currency: curr,
1214
+ scope: { currency: curr },
1154
1215
  magnitude: 0.15,
1155
1216
  reasoning: `[${curr}] Net flow +${netFlow.toFixed(1)}/tick. Inflationary. Increase production cost (primary sink) to balance faucet output.`
1156
1217
  },
@@ -1164,9 +1225,9 @@ var P12_OnePrimaryFaucet = {
1164
1225
  severity: 4,
1165
1226
  evidence: { currency: curr, netFlow, faucetVolume, sinkVolume },
1166
1227
  suggestedAction: {
1167
- parameter: "productionCost",
1228
+ parameterType: "cost",
1168
1229
  direction: "decrease",
1169
- currency: curr,
1230
+ scope: { currency: curr },
1170
1231
  magnitude: 0.15,
1171
1232
  reasoning: `[${curr}] Net flow ${netFlow.toFixed(1)}/tick. Deflationary. Decrease production cost to ease sink pressure.`
1172
1233
  },
@@ -1198,9 +1259,9 @@ var P13_PotsAreZeroSumAndSelfRegulate = {
1198
1259
  severity: 7,
1199
1260
  evidence: { currency: curr, pool: poolName, poolSize, participants: dominantCount, maxSustainableMultiplier },
1200
1261
  suggestedAction: {
1201
- parameter: "rewardRate",
1262
+ parameterType: "reward",
1202
1263
  direction: "decrease",
1203
- currency: curr,
1264
+ scope: { currency: curr },
1204
1265
  magnitude: 0.15,
1205
1266
  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.`
1206
1267
  },
@@ -1217,7 +1278,7 @@ var P14_TrackActualInjection = {
1217
1278
  id: "P14",
1218
1279
  name: "Track Actual Currency Injection, Not Value Creation",
1219
1280
  category: "currency",
1220
- description: 'Counting resource gathering as "currency injected" is misleading. Currency enters through faucet mechanisms (spawning, rewards). Fake metrics break every downstream decision.',
1281
+ description: 'Counting resource gathering as "currency injected" is misleading. Currency enters through faucet mechanisms (entering, rewards). Fake metrics break every downstream decision.',
1221
1282
  check(metrics, _thresholds) {
1222
1283
  for (const curr of metrics.currencies) {
1223
1284
  const faucetVolume = metrics.faucetVolumeByCurrency[curr] ?? 0;
@@ -1230,9 +1291,9 @@ var P14_TrackActualInjection = {
1230
1291
  severity: 4,
1231
1292
  evidence: { currency: curr, faucetVolume, netFlow, supplyGrowthRate },
1232
1293
  suggestedAction: {
1233
- parameter: "yieldRate",
1294
+ parameterType: "yield",
1234
1295
  direction: "decrease",
1235
- currency: curr,
1296
+ scope: { currency: curr },
1236
1297
  magnitude: 0.1,
1237
1298
  reasoning: `[${curr}] Supply growing at ${(supplyGrowthRate * 100).toFixed(1)}%/tick. Verify currency injection tracking. Resources should not create currency directly.`
1238
1299
  },
@@ -1262,9 +1323,9 @@ var P15_PoolsNeedCapAndDecay = {
1262
1323
  severity: 6,
1263
1324
  evidence: { currency: curr, pool, size, shareOfSupply, cap: poolCapPercent },
1264
1325
  suggestedAction: {
1265
- parameter: "transactionFee",
1326
+ parameterType: "fee",
1266
1327
  direction: "decrease",
1267
- currency: curr,
1328
+ scope: { tags: ["transaction"], currency: curr },
1268
1329
  magnitude: 0.1,
1269
1330
  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.`
1270
1331
  },
@@ -1294,9 +1355,9 @@ var P16_WithdrawalPenaltyScales = {
1294
1355
  severity: 3,
1295
1356
  evidence: { currency: curr, pool: poolName, poolSize, estimatedStaked: stakedEstimate },
1296
1357
  suggestedAction: {
1297
- parameter: "transactionFee",
1358
+ parameterType: "fee",
1298
1359
  direction: "increase",
1299
- currency: curr,
1360
+ scope: { tags: ["transaction"], currency: curr },
1300
1361
  magnitude: 0.05,
1301
1362
  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.`
1302
1363
  },
@@ -1326,9 +1387,9 @@ var P32_VelocityAboveSupply = {
1326
1387
  severity: 4,
1327
1388
  evidence: { currency: curr, velocity, totalSupply, totalResources },
1328
1389
  suggestedAction: {
1329
- parameter: "transactionFee",
1390
+ parameterType: "fee",
1330
1391
  direction: "decrease",
1331
- currency: curr,
1392
+ scope: { tags: ["transaction"], currency: curr },
1332
1393
  magnitude: 0.2,
1333
1394
  reasoning: `[${curr}] Velocity ${velocity}/t with ${totalResources} resources in system. Economy stagnant despite available supply. Lower trading friction.`
1334
1395
  },
@@ -1368,9 +1429,9 @@ var P58_NoNaturalNumeraire = {
1368
1429
  meanPrice: mean
1369
1430
  },
1370
1431
  suggestedAction: {
1371
- parameter: "productionCost",
1432
+ parameterType: "cost",
1372
1433
  direction: "increase",
1373
- currency: curr,
1434
+ scope: { currency: curr },
1374
1435
  magnitude: 0.1,
1375
1436
  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.`
1376
1437
  },
@@ -1405,7 +1466,8 @@ var P17_GracePeriodBeforeIntervention = {
1405
1466
  severity: 7,
1406
1467
  evidence: { tick: metrics.tick, avgSatisfaction: metrics.avgSatisfaction },
1407
1468
  suggestedAction: {
1408
- parameter: "entryFee",
1469
+ parameterType: "fee",
1470
+ scope: { tags: ["entry"] },
1409
1471
  direction: "decrease",
1410
1472
  magnitude: 0.2,
1411
1473
  reasoning: `Very low satisfaction at tick ${metrics.tick}. Intervention may have fired during grace period. Ease all costs to let economy bootstrap.`
@@ -1432,7 +1494,7 @@ var P18_FirstProducerNeedsStartingInventory = {
1432
1494
  severity: 8,
1433
1495
  evidence: { tick: metrics.tick, resource, supply, totalAgents: metrics.totalAgents },
1434
1496
  suggestedAction: {
1435
- parameter: "productionCost",
1497
+ parameterType: "cost",
1436
1498
  direction: "decrease",
1437
1499
  magnitude: 0.5,
1438
1500
  reasoning: `Bootstrap failure: ${resource} supply is 0 at tick ${metrics.tick} with ${metrics.totalAgents} agents. Drastically reduce production cost to allow immediate output.`
@@ -1471,7 +1533,7 @@ var P19_StartingSupplyExceedsDemand = {
1471
1533
  resourcesPerAgent
1472
1534
  },
1473
1535
  suggestedAction: {
1474
- parameter: "rewardRate",
1536
+ parameterType: "reward",
1475
1537
  direction: "increase",
1476
1538
  magnitude: 0.2,
1477
1539
  reasoning: `${mostPopulatedRole} (${population} agents) has insufficient resources (${resourcesPerAgent.toFixed(2)} per agent). Cold-start scarcity. Boost competitive pool reward to attract participation despite scarcity.`
@@ -1505,7 +1567,7 @@ var P20_DecayPreventsAccumulation = {
1505
1567
  severity: 4,
1506
1568
  evidence: { totalResources, resourcesPerAgent, velocity },
1507
1569
  suggestedAction: {
1508
- parameter: "yieldRate",
1570
+ parameterType: "yield",
1509
1571
  direction: "decrease",
1510
1572
  magnitude: 0.1,
1511
1573
  reasoning: `${totalResources.toFixed(0)} resources with velocity ${velocity}/t. Likely hoarding. Reduce yield to increase scarcity and force circulation.`
@@ -1533,7 +1595,8 @@ var P21_PriceFromGlobalSupply = {
1533
1595
  severity: 3,
1534
1596
  evidence: { resource, volatility, supply, price: prices[resource] },
1535
1597
  suggestedAction: {
1536
- parameter: "transactionFee",
1598
+ parameterType: "fee",
1599
+ scope: { tags: ["transaction"] },
1537
1600
  direction: "increase",
1538
1601
  magnitude: 0.05,
1539
1602
  reasoning: `${resource} price volatile (${(volatility * 100).toFixed(0)}%) despite supply ${supply}. Price may not reflect global inventory. Increase trading friction to stabilise.`
@@ -1550,7 +1613,7 @@ var P22_MarketAwarenessPreventsSurplus = {
1550
1613
  id: "P22",
1551
1614
  name: "Market Awareness Prevents Overproduction",
1552
1615
  category: "feedback",
1553
- description: "Producers who craft without checking market prices will create surpluses that crash prices. Agents need to see prices before deciding to produce.",
1616
+ description: "Producers who produce without checking market prices will create surpluses that crash prices. Agents need to see prices before deciding to produce.",
1554
1617
  check(metrics, _thresholds) {
1555
1618
  const { supplyByResource, prices, productionIndex } = metrics;
1556
1619
  const priceValues = Object.values(prices).filter((p) => p > 0);
@@ -1574,7 +1637,7 @@ var P22_MarketAwarenessPreventsSurplus = {
1574
1637
  productionIndex
1575
1638
  },
1576
1639
  suggestedAction: {
1577
- parameter: "productionCost",
1640
+ parameterType: "cost",
1578
1641
  direction: "increase",
1579
1642
  magnitude: 0.1,
1580
1643
  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.`
@@ -1601,7 +1664,7 @@ var P23_ProfitabilityFactorsFeasibility = {
1601
1664
  severity: 5,
1602
1665
  evidence: { blockedFraction, blockedAgentCount, avgSatisfaction },
1603
1666
  suggestedAction: {
1604
- parameter: "productionCost",
1667
+ parameterType: "cost",
1605
1668
  direction: "decrease",
1606
1669
  magnitude: 0.15,
1607
1670
  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.`
@@ -1627,7 +1690,8 @@ var P24_BlockedAgentsDecayFaster = {
1627
1690
  severity: 5,
1628
1691
  evidence: { blockedFraction, blockedAgentCount, churnRate },
1629
1692
  suggestedAction: {
1630
- parameter: "transactionFee",
1693
+ parameterType: "fee",
1694
+ scope: { tags: ["transaction"] },
1631
1695
  direction: "decrease",
1632
1696
  magnitude: 0.15,
1633
1697
  reasoning: `${(blockedFraction * 100).toFixed(0)}% of agents blocked. Blocked agents churn silently, skewing metrics. Lower fees to unblock market participation.`
@@ -1672,7 +1736,7 @@ var P25_CorrectLeversForCorrectProblems = {
1672
1736
  netFlow
1673
1737
  },
1674
1738
  suggestedAction: {
1675
- parameter: "yieldRate",
1739
+ parameterType: "yield",
1676
1740
  direction: "decrease",
1677
1741
  magnitude: 0.15,
1678
1742
  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.`
@@ -1698,7 +1762,7 @@ var P26_ContinuousPressureBeatsThresholdCuts = {
1698
1762
  severity: 4,
1699
1763
  evidence: { inflationRate },
1700
1764
  suggestedAction: {
1701
- parameter: "productionCost",
1765
+ parameterType: "cost",
1702
1766
  direction: inflationRate > 0 ? "increase" : "decrease",
1703
1767
  magnitude: Math.min(thresholds.maxAdjustmentPercent, 0.05),
1704
1768
  // force smaller step
@@ -1724,7 +1788,8 @@ var P27_AdjustmentsNeedCooldowns = {
1724
1788
  severity: 4,
1725
1789
  evidence: { churnRate, avgSatisfaction },
1726
1790
  suggestedAction: {
1727
- parameter: "entryFee",
1791
+ parameterType: "fee",
1792
+ scope: { tags: ["entry"] },
1728
1793
  direction: "decrease",
1729
1794
  magnitude: 0.05,
1730
1795
  reasoning: `High churn (${(churnRate * 100).toFixed(1)}%) with low satisfaction. Possible oscillation from rapid adjustments. Apply small correction only.`
@@ -1755,7 +1820,7 @@ var P28_StructuralDominanceIsNotPathological = {
1755
1820
  severity: 5,
1756
1821
  evidence: { dominantRole, dominantShare, avgSatisfaction },
1757
1822
  suggestedAction: {
1758
- parameter: "productionCost",
1823
+ parameterType: "cost",
1759
1824
  direction: "decrease",
1760
1825
  magnitude: 0.1,
1761
1826
  reasoning: `${dominantRole} dominant (${(dominantShare * 100).toFixed(0)}%) with low satisfaction. Pathological dominance \u2014 agents trapped, not thriving. Ease costs to allow role switching.`
@@ -1771,7 +1836,7 @@ var P38_CommunicationPreventsRevolt = {
1771
1836
  id: "P38",
1772
1837
  name: "Communication Prevents Revolt",
1773
1838
  category: "regulator",
1774
- 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.",
1839
+ 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.",
1775
1840
  check(metrics, _thresholds) {
1776
1841
  const { churnRate } = metrics;
1777
1842
  if (churnRate > 0.1) {
@@ -1780,7 +1845,7 @@ var P38_CommunicationPreventsRevolt = {
1780
1845
  severity: 3,
1781
1846
  evidence: { churnRate },
1782
1847
  suggestedAction: {
1783
- parameter: "rewardRate",
1848
+ parameterType: "reward",
1784
1849
  direction: "increase",
1785
1850
  magnitude: 0.1,
1786
1851
  reasoning: `High churn (${(churnRate * 100).toFixed(1)}%) \u2014 agents leaving. Ensure all recent adjustments are logged with reasoning to diagnose cause.`
@@ -1803,7 +1868,7 @@ var REGULATOR_PRINCIPLES = [
1803
1868
  // src/principles/market-dynamics.ts
1804
1869
  var P29_PinchPoint = {
1805
1870
  id: "P29",
1806
- name: "Pinch Point",
1871
+ name: "Bottleneck Detection",
1807
1872
  category: "market_dynamics",
1808
1873
  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.",
1809
1874
  check(metrics, _thresholds) {
@@ -1817,7 +1882,7 @@ var P29_PinchPoint = {
1817
1882
  severity: 7,
1818
1883
  evidence: { resource, supply, demand, status },
1819
1884
  suggestedAction: {
1820
- parameter: "productionCost",
1885
+ parameterType: "cost",
1821
1886
  direction: "decrease",
1822
1887
  magnitude: 0.15,
1823
1888
  reasoning: `${resource} is a pinch point and currently SCARCE (supply ${supply}, demand ${demand}). Reduce production cost to increase throughput.`
@@ -1833,7 +1898,7 @@ var P29_PinchPoint = {
1833
1898
  severity: 4,
1834
1899
  evidence: { resource, supply, status },
1835
1900
  suggestedAction: {
1836
- parameter: "productionCost",
1901
+ parameterType: "cost",
1837
1902
  direction: "increase",
1838
1903
  magnitude: 0.1,
1839
1904
  reasoning: `${resource} is a pinch point and OVERSUPPLIED (supply ${supply}). Raise production cost to reduce surplus.`
@@ -1848,9 +1913,9 @@ var P29_PinchPoint = {
1848
1913
  };
1849
1914
  var P30_MovingPinchPoint = {
1850
1915
  id: "P30",
1851
- name: "Moving Pinch Point",
1916
+ name: "Dynamic Bottleneck Rotation",
1852
1917
  category: "market_dynamics",
1853
- 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.",
1918
+ 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.",
1854
1919
  check(metrics, _thresholds) {
1855
1920
  const { capacityUsage, supplyByResource, avgSatisfaction } = metrics;
1856
1921
  const totalResources = Object.values(supplyByResource).reduce((s, v) => s + v, 0);
@@ -1861,7 +1926,7 @@ var P30_MovingPinchPoint = {
1861
1926
  severity: 3,
1862
1927
  evidence: { capacityUsage, resourcesPerAgent, avgSatisfaction },
1863
1928
  suggestedAction: {
1864
- parameter: "productionCost",
1929
+ parameterType: "cost",
1865
1930
  direction: "increase",
1866
1931
  magnitude: 0.1,
1867
1932
  reasoning: "Economy operating at full capacity with abundant resources and high satisfaction. Pinch point may have been cleared. Increase production cost to restore scarcity."
@@ -1877,7 +1942,7 @@ var P57_CombinatorialPriceSpace = {
1877
1942
  id: "P57",
1878
1943
  name: "Combinatorial Price Space",
1879
1944
  category: "market_dynamics",
1880
- 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.",
1945
+ 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.",
1881
1946
  check(metrics, thresholds) {
1882
1947
  const { prices, priceVolatility } = metrics;
1883
1948
  const priceKeys = Object.keys(prices);
@@ -1907,7 +1972,8 @@ var P57_CombinatorialPriceSpace = {
1907
1972
  target: thresholds.relativePriceConvergenceTarget
1908
1973
  },
1909
1974
  suggestedAction: {
1910
- parameter: "transactionFee",
1975
+ parameterType: "fee",
1976
+ scope: { tags: ["transaction"] },
1911
1977
  direction: "decrease",
1912
1978
  magnitude: 0.1,
1913
1979
  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.`
@@ -1939,7 +2005,7 @@ var P31_AnchorValueTracking = {
1939
2005
  severity: 5,
1940
2006
  evidence: { anchorRatioDrift, inflationRate },
1941
2007
  suggestedAction: {
1942
- parameter: "productionCost",
2008
+ parameterType: "cost",
1943
2009
  direction: anchorRatioDrift > 0 ? "increase" : "decrease",
1944
2010
  magnitude: 0.1,
1945
2011
  reasoning: `Anchor ratio has drifted ${(anchorRatioDrift * 100).toFixed(0)}% from baseline. Time-to-value for participants is changing. Adjust production costs to restore.`
@@ -1964,7 +2030,8 @@ var P41_MultiResolutionMonitoring = {
1964
2030
  severity: 4,
1965
2031
  evidence: { giniCoefficient, avgSatisfaction },
1966
2032
  suggestedAction: {
1967
- parameter: "transactionFee",
2033
+ parameterType: "fee",
2034
+ scope: { tags: ["transaction"] },
1968
2035
  direction: "increase",
1969
2036
  magnitude: 0.1,
1970
2037
  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.`
@@ -1993,7 +2060,8 @@ var P55_ArbitrageThermometer = {
1993
2060
  critical: thresholds.arbitrageIndexCritical
1994
2061
  },
1995
2062
  suggestedAction: {
1996
- parameter: "transactionFee",
2063
+ parameterType: "fee",
2064
+ scope: { tags: ["transaction"] },
1997
2065
  direction: "decrease",
1998
2066
  magnitude: 0.15,
1999
2067
  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.`
@@ -2011,7 +2079,8 @@ var P55_ArbitrageThermometer = {
2011
2079
  warning: thresholds.arbitrageIndexWarning
2012
2080
  },
2013
2081
  suggestedAction: {
2014
- parameter: "transactionFee",
2082
+ parameterType: "fee",
2083
+ scope: { tags: ["transaction"] },
2015
2084
  direction: "decrease",
2016
2085
  magnitude: 0.08,
2017
2086
  reasoning: `Arbitrage index ${arbitrageIndex.toFixed(2)} above warning threshold (${thresholds.arbitrageIndexWarning}). Early sign of price divergence. Gently reduce friction to support self-correction.`
@@ -2039,7 +2108,8 @@ var P59_GiftEconomyNoise = {
2039
2108
  threshold: thresholds.giftTradeFilterRatio
2040
2109
  },
2041
2110
  suggestedAction: {
2042
- parameter: "transactionFee",
2111
+ parameterType: "fee",
2112
+ scope: { tags: ["transaction"] },
2043
2113
  direction: "increase",
2044
2114
  magnitude: 0.05,
2045
2115
  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.`
@@ -2082,9 +2152,9 @@ var P42_TheMedianPrinciple = {
2082
2152
  medianBalance
2083
2153
  },
2084
2154
  suggestedAction: {
2085
- parameter: "transactionFee",
2155
+ parameterType: "fee",
2156
+ scope: { tags: ["transaction"], currency: curr },
2086
2157
  direction: "increase",
2087
- currency: curr,
2088
2158
  magnitude: 0.15,
2089
2159
  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.`
2090
2160
  },
@@ -2109,7 +2179,7 @@ var P43_SimulationMinimum = {
2109
2179
  severity: 3,
2110
2180
  evidence: { inflationRate, minIterations: thresholds.simulationMinIterations },
2111
2181
  suggestedAction: {
2112
- parameter: "productionCost",
2182
+ parameterType: "cost",
2113
2183
  direction: inflationRate > 0 ? "increase" : "decrease",
2114
2184
  magnitude: 0.05,
2115
2185
  reasoning: `Large inflation rate swing (${(inflationRate * 100).toFixed(0)}%). Ensure all decisions use \u2265${thresholds.simulationMinIterations} simulation iterations. Apply conservative correction.`
@@ -2147,7 +2217,7 @@ var P39_TheLagPrinciple = {
2147
2217
  severity: 5,
2148
2218
  evidence: { inflationRate, netFlow, lagRange: [lagMin, lagMax] },
2149
2219
  suggestedAction: {
2150
- parameter: "productionCost",
2220
+ parameterType: "cost",
2151
2221
  direction: "increase",
2152
2222
  magnitude: 0.03,
2153
2223
  // very small — oscillation means over-adjusting
@@ -2174,7 +2244,8 @@ var P44_ComplexityBudget = {
2174
2244
  severity: 3,
2175
2245
  evidence: { customMetricCount, budgetMax: thresholds.complexityBudgetMax },
2176
2246
  suggestedAction: {
2177
- parameter: "transactionFee",
2247
+ parameterType: "fee",
2248
+ scope: { tags: ["transaction"] },
2178
2249
  direction: "decrease",
2179
2250
  magnitude: 0.01,
2180
2251
  reasoning: `${customMetricCount} custom metrics tracked (budget: ${thresholds.complexityBudgetMax}). Consider pruning low-impact parameters. Applying minimal correction to avoid adding complexity.`
@@ -2206,7 +2277,8 @@ var P35_DestructionCreatesValue = {
2206
2277
  severity: 6,
2207
2278
  evidence: { resource, supply, sinkVolume, netFlow },
2208
2279
  suggestedAction: {
2209
- parameter: "entryFee",
2280
+ parameterType: "fee",
2281
+ scope: { tags: ["entry"] },
2210
2282
  direction: "decrease",
2211
2283
  magnitude: 0.1,
2212
2284
  reasoning: `${resource} supply at ${supply} units with low destruction (sink ${sinkVolume}/t). Resources not being consumed. Lower competitive pool entry to increase resource usage.`
@@ -2234,7 +2306,7 @@ var P40_ReplacementRate = {
2234
2306
  severity: 6,
2235
2307
  evidence: { productionIndex, sinkVolume, replacementRatio },
2236
2308
  suggestedAction: {
2237
- parameter: "yieldRate",
2309
+ parameterType: "yield",
2238
2310
  direction: "increase",
2239
2311
  magnitude: 0.15,
2240
2312
  reasoning: `Replacement rate ${replacementRatio.toFixed(2)} (need \u2265${thresholds.replacementRateMultiplier}). Production below consumption. Resources will deplete. Increase yield.`
@@ -2248,7 +2320,7 @@ var P40_ReplacementRate = {
2248
2320
  severity: 3,
2249
2321
  evidence: { productionIndex, sinkVolume, replacementRatio },
2250
2322
  suggestedAction: {
2251
- parameter: "yieldRate",
2323
+ parameterType: "yield",
2252
2324
  direction: "decrease",
2253
2325
  magnitude: 0.1,
2254
2326
  reasoning: `Replacement rate ${replacementRatio.toFixed(2)} \u2014 overproducing. Production far exceeds consumption. Reduce yield to prevent glut.`
@@ -2274,7 +2346,8 @@ var P49_IdleAssetTax = {
2274
2346
  severity: 5,
2275
2347
  evidence: { giniCoefficient, top10PctShare, velocity },
2276
2348
  suggestedAction: {
2277
- parameter: "transactionFee",
2349
+ parameterType: "fee",
2350
+ scope: { tags: ["transaction"] },
2278
2351
  direction: "increase",
2279
2352
  magnitude: 0.15,
2280
2353
  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.`
@@ -2292,11 +2365,11 @@ var RESOURCE_MGMT_PRINCIPLES = [
2292
2365
  P49_IdleAssetTax
2293
2366
  ];
2294
2367
 
2295
- // src/principles/player-experience.ts
2368
+ // src/principles/participant-experience.ts
2296
2369
  var P33_FairNotEqual = {
2297
2370
  id: "P33",
2298
2371
  name: "Fair \u2260 Equal",
2299
- category: "player_experience",
2372
+ category: "participant_experience",
2300
2373
  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.",
2301
2374
  check(metrics, thresholds) {
2302
2375
  for (const curr of metrics.currencies) {
@@ -2307,9 +2380,9 @@ var P33_FairNotEqual = {
2307
2380
  severity: 3,
2308
2381
  evidence: { currency: curr, giniCoefficient },
2309
2382
  suggestedAction: {
2310
- parameter: "rewardRate",
2383
+ parameterType: "reward",
2311
2384
  direction: "increase",
2312
- currency: curr,
2385
+ scope: { currency: curr },
2313
2386
  magnitude: 0.1,
2314
2387
  reasoning: `[${curr}] Gini ${giniCoefficient.toFixed(2)} \u2014 near-perfect equality. Economy lacks stakes. Increase winner rewards to create meaningful spread.`
2315
2388
  },
@@ -2323,9 +2396,9 @@ var P33_FairNotEqual = {
2323
2396
  severity: 7,
2324
2397
  evidence: { currency: curr, giniCoefficient },
2325
2398
  suggestedAction: {
2326
- parameter: "transactionFee",
2399
+ parameterType: "fee",
2327
2400
  direction: "increase",
2328
- currency: curr,
2401
+ scope: { tags: ["transaction"], currency: curr },
2329
2402
  magnitude: 0.2,
2330
2403
  reasoning: `[${curr}] Gini ${giniCoefficient.toFixed(2)} \u2014 oligarchy level. Toxic inequality. Raise transaction fees to redistribute wealth from rich to pool.`
2331
2404
  },
@@ -2339,9 +2412,9 @@ var P33_FairNotEqual = {
2339
2412
  severity: 4,
2340
2413
  evidence: { currency: curr, giniCoefficient },
2341
2414
  suggestedAction: {
2342
- parameter: "transactionFee",
2415
+ parameterType: "fee",
2343
2416
  direction: "increase",
2344
- currency: curr,
2417
+ scope: { tags: ["transaction"], currency: curr },
2345
2418
  magnitude: 0.1,
2346
2419
  reasoning: `[${curr}] Gini ${giniCoefficient.toFixed(2)} \u2014 high inequality warning. Gently raise fees to slow wealth concentration.`
2347
2420
  },
@@ -2355,9 +2428,9 @@ var P33_FairNotEqual = {
2355
2428
  };
2356
2429
  var P36_MechanicFrictionDetector = {
2357
2430
  id: "P36",
2358
- name: "Mechanic Friction Detector",
2359
- category: "player_experience",
2360
- 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.",
2431
+ name: "Mechanism Friction Detector",
2432
+ category: "participant_experience",
2433
+ 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.",
2361
2434
  check(metrics, _thresholds) {
2362
2435
  const { avgSatisfaction, churnRate, velocity } = metrics;
2363
2436
  if (churnRate > 0.1 && avgSatisfaction < 50 && velocity > 3) {
@@ -2366,10 +2439,10 @@ var P36_MechanicFrictionDetector = {
2366
2439
  severity: 5,
2367
2440
  evidence: { churnRate, avgSatisfaction, velocity },
2368
2441
  suggestedAction: {
2369
- parameter: "rewardRate",
2442
+ parameterType: "reward",
2370
2443
  direction: "increase",
2371
2444
  magnitude: 0.15,
2372
- 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."
2445
+ 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."
2373
2446
  },
2374
2447
  confidence: 0.55,
2375
2448
  estimatedLag: 15
@@ -2380,8 +2453,8 @@ var P36_MechanicFrictionDetector = {
2380
2453
  };
2381
2454
  var P37_LatecommerProblem = {
2382
2455
  id: "P37",
2383
- name: "Latecomer Problem",
2384
- category: "player_experience",
2456
+ name: "Late Entrant Problem",
2457
+ category: "participant_experience",
2385
2458
  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.",
2386
2459
  check(metrics, _thresholds) {
2387
2460
  const { timeToValue, avgSatisfaction, churnRate } = metrics;
@@ -2391,7 +2464,7 @@ var P37_LatecommerProblem = {
2391
2464
  severity: 6,
2392
2465
  evidence: { timeToValue, avgSatisfaction, churnRate },
2393
2466
  suggestedAction: {
2394
- parameter: "productionCost",
2467
+ parameterType: "cost",
2395
2468
  direction: "decrease",
2396
2469
  magnitude: 0.15,
2397
2470
  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.`
@@ -2406,7 +2479,7 @@ var P37_LatecommerProblem = {
2406
2479
  var P45_TimeBudget = {
2407
2480
  id: "P45",
2408
2481
  name: "Time Budget",
2409
- category: "player_experience",
2482
+ category: "participant_experience",
2410
2483
  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.",
2411
2484
  check(metrics, thresholds) {
2412
2485
  const { timeToValue, avgSatisfaction } = metrics;
@@ -2418,8 +2491,9 @@ var P45_TimeBudget = {
2418
2491
  severity: 5,
2419
2492
  evidence: { timeToValue, avgSatisfaction, timeBudgetRatio: thresholds.timeBudgetRatio },
2420
2493
  suggestedAction: {
2421
- parameter: "entryFee",
2494
+ parameterType: "fee",
2422
2495
  direction: "decrease",
2496
+ scope: { tags: ["entry"] },
2423
2497
  magnitude: 0.15,
2424
2498
  reasoning: `Time-to-value ${timeToValue} ticks with ${avgSatisfaction.toFixed(0)} satisfaction. Economy requires too much time investment. Lower barriers to participation.`
2425
2499
  },
@@ -2433,7 +2507,7 @@ var P45_TimeBudget = {
2433
2507
  var P50_PayPowerRatio = {
2434
2508
  id: "P50",
2435
2509
  name: "Pay-Power Ratio",
2436
- category: "player_experience",
2510
+ category: "participant_experience",
2437
2511
  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.",
2438
2512
  check(metrics, thresholds) {
2439
2513
  const { top10PctShare, giniCoefficient } = metrics;
@@ -2448,8 +2522,9 @@ var P50_PayPowerRatio = {
2448
2522
  threshold: thresholds.payPowerRatioMax
2449
2523
  },
2450
2524
  suggestedAction: {
2451
- parameter: "transactionFee",
2525
+ parameterType: "fee",
2452
2526
  direction: "increase",
2527
+ scope: { tags: ["transaction"] },
2453
2528
  magnitude: 0.2,
2454
2529
  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.`
2455
2530
  },
@@ -2460,7 +2535,7 @@ var P50_PayPowerRatio = {
2460
2535
  return { violated: false };
2461
2536
  }
2462
2537
  };
2463
- var PLAYER_EXPERIENCE_PRINCIPLES = [
2538
+ var PARTICIPANT_EXPERIENCE_PRINCIPLES = [
2464
2539
  P33_FairNotEqual,
2465
2540
  P36_MechanicFrictionDetector,
2466
2541
  P37_LatecommerProblem,
@@ -2483,7 +2558,8 @@ var P34_ExtractionRatio = {
2483
2558
  severity: 8,
2484
2559
  evidence: { extractionRatio, threshold: thresholds.extractionRatioRed },
2485
2560
  suggestedAction: {
2486
- parameter: "transactionFee",
2561
+ parameterType: "fee",
2562
+ scope: { tags: ["transaction"] },
2487
2563
  direction: "increase",
2488
2564
  magnitude: 0.25,
2489
2565
  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.`
@@ -2498,7 +2574,8 @@ var P34_ExtractionRatio = {
2498
2574
  severity: 5,
2499
2575
  evidence: { extractionRatio, threshold: thresholds.extractionRatioYellow },
2500
2576
  suggestedAction: {
2501
- parameter: "transactionFee",
2577
+ parameterType: "fee",
2578
+ scope: { tags: ["transaction"] },
2502
2579
  direction: "increase",
2503
2580
  magnitude: 0.1,
2504
2581
  reasoning: `Extraction ratio ${(extractionRatio * 100).toFixed(0)}% (warning: ${(thresholds.extractionRatioYellow * 100).toFixed(0)}%). Economy trending toward extraction-heavy. Apply early pressure.`
@@ -2524,7 +2601,7 @@ var P47_SmokeTest = {
2524
2601
  severity: 9,
2525
2602
  evidence: { smokeTestRatio, threshold: thresholds.smokeTestCritical },
2526
2603
  suggestedAction: {
2527
- parameter: "rewardRate",
2604
+ parameterType: "reward",
2528
2605
  direction: "increase",
2529
2606
  magnitude: 0.2,
2530
2607
  reasoning: `Utility/market ratio ${(smokeTestRatio * 100).toFixed(0)}% (critical). Economy is >90% speculative. Collapse risk is extreme. Increase utility rewards to anchor real value.`
@@ -2539,7 +2616,7 @@ var P47_SmokeTest = {
2539
2616
  severity: 6,
2540
2617
  evidence: { smokeTestRatio, threshold: thresholds.smokeTestWarning },
2541
2618
  suggestedAction: {
2542
- parameter: "rewardRate",
2619
+ parameterType: "reward",
2543
2620
  direction: "increase",
2544
2621
  magnitude: 0.1,
2545
2622
  reasoning: `Utility/market ratio ${(smokeTestRatio * 100).toFixed(0)}% (warning). Economy is >70% speculative. Boost utility rewards to restore intrinsic value anchor.`
@@ -2565,7 +2642,8 @@ var P48_CurrencyInsulation = {
2565
2642
  severity: 6,
2566
2643
  evidence: { currencyInsulation, threshold: thresholds.currencyInsulationMax },
2567
2644
  suggestedAction: {
2568
- parameter: "transactionFee",
2645
+ parameterType: "fee",
2646
+ scope: { tags: ["transaction"] },
2569
2647
  direction: "increase",
2570
2648
  magnitude: 0.1,
2571
2649
  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.`
@@ -2583,12 +2661,12 @@ var OPEN_ECONOMY_PRINCIPLES = [
2583
2661
  P48_CurrencyInsulation
2584
2662
  ];
2585
2663
 
2586
- // src/principles/liveops.ts
2664
+ // src/principles/operations.ts
2587
2665
  var P51_SharkTooth = {
2588
2666
  id: "P51",
2589
- name: "Shark Tooth Pattern",
2590
- category: "liveops",
2591
- 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.",
2667
+ name: "Cyclical Engagement Pattern",
2668
+ category: "operations",
2669
+ 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.",
2592
2670
  check(metrics, thresholds) {
2593
2671
  const { sharkToothPeaks, sharkToothValleys } = metrics;
2594
2672
  if (sharkToothPeaks.length < 2) return { violated: false };
@@ -2605,10 +2683,10 @@ var P51_SharkTooth = {
2605
2683
  threshold: thresholds.sharkToothPeakDecay
2606
2684
  },
2607
2685
  suggestedAction: {
2608
- parameter: "rewardRate",
2686
+ parameterType: "reward",
2609
2687
  direction: "increase",
2610
2688
  magnitude: 0.1,
2611
- 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.`
2689
+ 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.`
2612
2690
  },
2613
2691
  confidence: 0.75,
2614
2692
  estimatedLag: 30
@@ -2623,10 +2701,10 @@ var P51_SharkTooth = {
2623
2701
  severity: 4,
2624
2702
  evidence: { lastValley, prevValley, ratio: lastValley / prevValley },
2625
2703
  suggestedAction: {
2626
- parameter: "productionCost",
2704
+ parameterType: "cost",
2627
2705
  direction: "decrease",
2628
2706
  magnitude: 0.1,
2629
- reasoning: "Between-event engagement declining (deepening valleys). Base economy not sustaining participants between events. Lower production costs to improve off-event value."
2707
+ reasoning: "Between-activity engagement declining (deepening valleys). Base economy not sustaining participants between activities. Lower production costs to improve off-activity value."
2630
2708
  },
2631
2709
  confidence: 0.65,
2632
2710
  estimatedLag: 20
@@ -2639,8 +2717,8 @@ var P51_SharkTooth = {
2639
2717
  var P52_EndowmentEffect = {
2640
2718
  id: "P52",
2641
2719
  name: "Endowment Effect",
2642
- category: "liveops",
2643
- 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).",
2720
+ category: "operations",
2721
+ 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).",
2644
2722
  check(metrics, _thresholds) {
2645
2723
  const { avgSatisfaction, churnRate } = metrics;
2646
2724
  const { eventCompletionRate } = metrics;
@@ -2651,10 +2729,10 @@ var P52_EndowmentEffect = {
2651
2729
  severity: 4,
2652
2730
  evidence: { eventCompletionRate, avgSatisfaction, churnRate },
2653
2731
  suggestedAction: {
2654
- parameter: "rewardRate",
2732
+ parameterType: "reward",
2655
2733
  direction: "increase",
2656
2734
  magnitude: 0.15,
2657
- reasoning: `${(eventCompletionRate * 100).toFixed(0)}% event completion but satisfaction only ${avgSatisfaction.toFixed(0)}. Events not creating perceived value. Increase reward quality/quantity.`
2735
+ reasoning: `${(eventCompletionRate * 100).toFixed(0)}% activity completion but satisfaction only ${avgSatisfaction.toFixed(0)}. Activities not creating perceived value. Increase reward quality/quantity.`
2658
2736
  },
2659
2737
  confidence: 0.6,
2660
2738
  estimatedLag: 20
@@ -2665,8 +2743,8 @@ var P52_EndowmentEffect = {
2665
2743
  };
2666
2744
  var P53_EventCompletionRate = {
2667
2745
  id: "P53",
2668
- name: "Event Completion Rate Sweet Spot",
2669
- category: "liveops",
2746
+ name: "Activity Completion Rate Sweet Spot",
2747
+ category: "operations",
2670
2748
  description: "Free completion at 60-80% is the sweet spot. <40% = predatory design. >80% = no monetization pressure. 100% free = zero reason to ever spend.",
2671
2749
  check(metrics, thresholds) {
2672
2750
  const { eventCompletionRate } = metrics;
@@ -2681,10 +2759,10 @@ var P53_EventCompletionRate = {
2681
2759
  max: thresholds.eventCompletionMax
2682
2760
  },
2683
2761
  suggestedAction: {
2684
- parameter: "productionCost",
2762
+ parameterType: "cost",
2685
2763
  direction: "decrease",
2686
2764
  magnitude: 0.15,
2687
- 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.`
2765
+ 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.`
2688
2766
  },
2689
2767
  confidence: 0.8,
2690
2768
  estimatedLag: 10
@@ -2696,10 +2774,11 @@ var P53_EventCompletionRate = {
2696
2774
  severity: 3,
2697
2775
  evidence: { eventCompletionRate, max: thresholds.eventCompletionMax },
2698
2776
  suggestedAction: {
2699
- parameter: "entryFee",
2777
+ parameterType: "fee",
2778
+ scope: { tags: ["entry"] },
2700
2779
  direction: "increase",
2701
2780
  magnitude: 0.05,
2702
- 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.`
2781
+ 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.`
2703
2782
  },
2704
2783
  confidence: 0.55,
2705
2784
  estimatedLag: 10
@@ -2710,9 +2789,9 @@ var P53_EventCompletionRate = {
2710
2789
  };
2711
2790
  var P54_LiveOpsCadence = {
2712
2791
  id: "P54",
2713
- name: "LiveOps Cadence",
2714
- category: "liveops",
2715
- 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.",
2792
+ name: "Operational Cadence",
2793
+ category: "operations",
2794
+ 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.",
2716
2795
  check(metrics, _thresholds) {
2717
2796
  const { velocity, avgSatisfaction } = metrics;
2718
2797
  if (velocity < 2 && avgSatisfaction < 55 && metrics.tick > 100) {
@@ -2721,7 +2800,7 @@ var P54_LiveOpsCadence = {
2721
2800
  severity: 3,
2722
2801
  evidence: { velocity, avgSatisfaction, tick: metrics.tick },
2723
2802
  suggestedAction: {
2724
- parameter: "rewardRate",
2803
+ parameterType: "reward",
2725
2804
  direction: "increase",
2726
2805
  magnitude: 0.1,
2727
2806
  reasoning: "Low velocity and satisfaction after long runtime. Possible content staleness. Increase rewards as bridge while new content is developed (developer action required)."
@@ -2735,8 +2814,8 @@ var P54_LiveOpsCadence = {
2735
2814
  };
2736
2815
  var P56_ContentDropShock = {
2737
2816
  id: "P56",
2738
- name: "Content-Drop Shock",
2739
- category: "liveops",
2817
+ name: "Supply Shock Absorption",
2818
+ category: "operations",
2740
2819
  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.",
2741
2820
  check(metrics, thresholds) {
2742
2821
  const { contentDropAge, arbitrageIndex } = metrics;
@@ -2752,7 +2831,8 @@ var P56_ContentDropShock = {
2752
2831
  postDropMax: thresholds.postDropArbitrageMax
2753
2832
  },
2754
2833
  suggestedAction: {
2755
- parameter: "transactionFee",
2834
+ parameterType: "fee",
2835
+ scope: { tags: ["transaction"] },
2756
2836
  direction: "decrease",
2757
2837
  magnitude: 0.1,
2758
2838
  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.`
@@ -2765,7 +2845,7 @@ var P56_ContentDropShock = {
2765
2845
  return { violated: false };
2766
2846
  }
2767
2847
  };
2768
- var LIVEOPS_PRINCIPLES = [
2848
+ var OPERATIONS_PRINCIPLES = [
2769
2849
  P51_SharkTooth,
2770
2850
  P52_EndowmentEffect,
2771
2851
  P53_EventCompletionRate,
@@ -2799,23 +2879,24 @@ var ALL_PRINCIPLES = [
2799
2879
  // P39, P44
2800
2880
  ...RESOURCE_MGMT_PRINCIPLES,
2801
2881
  // P35, P40, P49
2802
- ...PLAYER_EXPERIENCE_PRINCIPLES,
2882
+ ...PARTICIPANT_EXPERIENCE_PRINCIPLES,
2803
2883
  // P33, P36, P37, P45, P50
2804
2884
  ...OPEN_ECONOMY_PRINCIPLES,
2805
2885
  // P34, P47-P48
2806
- ...LIVEOPS_PRINCIPLES
2886
+ ...OPERATIONS_PRINCIPLES
2807
2887
  // P51-P54, P56
2808
2888
  ];
2809
2889
 
2810
2890
  // src/Simulator.ts
2811
2891
  var Simulator = class {
2812
- constructor() {
2892
+ constructor(registry) {
2813
2893
  this.diagnoser = new Diagnoser(ALL_PRINCIPLES);
2814
2894
  // Cache beforeViolations for the *current* tick only (one entry max).
2815
2895
  // Using a Map here is intentional but the cache must be bounded — we only
2816
2896
  // care about the tick that is currently being evaluated, so we evict any
2817
2897
  // entries whose key differs from the incoming tick.
2818
2898
  this.beforeViolationsCache = /* @__PURE__ */ new Map();
2899
+ this.registry = registry;
2819
2900
  }
2820
2901
  /**
2821
2902
  * Simulate the effect of applying `action` to the current economy forward `forwardTicks`.
@@ -2882,7 +2963,7 @@ var Simulator = class {
2882
2963
  const multiplier = this.actionMultiplier(action);
2883
2964
  const noise = () => 1 + (Math.random() - 0.5) * 0.1;
2884
2965
  const currencies = metrics.currencies;
2885
- const targetCurrency = action.currency;
2966
+ const targetCurrency = action.scope?.currency;
2886
2967
  const supplies = { ...metrics.totalSupplyByCurrency };
2887
2968
  const netFlows = { ...metrics.netFlowByCurrency };
2888
2969
  const ginis = { ...metrics.giniCoefficientByCurrency };
@@ -2924,26 +3005,48 @@ var Simulator = class {
2924
3005
  return action.direction === "increase" ? 1 + base : 1 - base;
2925
3006
  }
2926
3007
  flowEffect(action, metrics, currency) {
2927
- const { parameter, direction } = action;
3008
+ const { direction } = action;
2928
3009
  const sign = direction === "increase" ? -1 : 1;
2929
3010
  const roleEntries = Object.entries(metrics.populationByRole).sort((a, b) => b[1] - a[1]);
2930
3011
  const dominantRoleCount = roleEntries[0]?.[1] ?? 0;
2931
- if (parameter === "productionCost") {
2932
- return sign * (metrics.netFlowByCurrency[currency] ?? 0) * 0.2;
2933
- }
2934
- if (parameter === "transactionFee") {
2935
- return sign * (metrics.velocityByCurrency[currency] ?? 0) * 10 * 0.1;
2936
- }
2937
- if (parameter === "entryFee") {
2938
- return sign * dominantRoleCount * 0.5;
3012
+ const resolvedKey = action.resolvedParameter;
3013
+ let impact;
3014
+ if (resolvedKey && this.registry) {
3015
+ impact = this.registry.getFlowImpact(resolvedKey);
3016
+ }
3017
+ if (!impact) {
3018
+ impact = this.inferFlowImpact(action.parameterType);
3019
+ }
3020
+ switch (impact) {
3021
+ case "sink":
3022
+ return sign * (metrics.netFlowByCurrency[currency] ?? 0) * 0.2;
3023
+ case "faucet":
3024
+ return -sign * dominantRoleCount * 0.3;
3025
+ case "neutral":
3026
+ return sign * dominantRoleCount * 0.5;
3027
+ case "mixed":
3028
+ return sign * (metrics.faucetVolumeByCurrency[currency] ?? 0) * 0.15;
3029
+ default:
3030
+ return sign * (metrics.netFlowByCurrency[currency] ?? 0) * 0.1;
3031
+ }
3032
+ }
3033
+ /** Infer flow impact from parameter type when registry is unavailable */
3034
+ inferFlowImpact(parameterType) {
3035
+ switch (parameterType) {
3036
+ case "cost":
3037
+ case "fee":
3038
+ case "penalty":
3039
+ return "sink";
3040
+ case "reward":
3041
+ return "faucet";
3042
+ case "yield":
3043
+ return "mixed";
3044
+ case "cap":
3045
+ case "multiplier":
3046
+ return "neutral";
3047
+ default:
3048
+ return "mixed";
2939
3049
  }
2940
- if (parameter === "rewardRate") {
2941
- return -sign * dominantRoleCount * 0.3;
2942
- }
2943
- if (parameter === "yieldRate") {
2944
- return sign * (metrics.faucetVolumeByCurrency[currency] ?? 0) * 0.15;
2945
- }
2946
- return sign * (metrics.netFlowByCurrency[currency] ?? 0) * 0.1;
2947
3050
  }
2948
3051
  checkImprovement(before, after, action) {
2949
3052
  const satisfactionImproved = after.avgSatisfaction >= before.avgSatisfaction - 2;
@@ -3023,16 +3126,30 @@ var Planner = class {
3023
3126
  * - parameter is still in cooldown
3024
3127
  * - simulation result failed
3025
3128
  * - complexity budget exceeded
3129
+ * - no matching parameter in registry
3026
3130
  */
3027
- plan(diagnosis, metrics, simulationResult, currentParams, thresholds) {
3131
+ plan(diagnosis, metrics, simulationResult, currentParams, thresholds, registry) {
3028
3132
  const action = diagnosis.violation.suggestedAction;
3029
- const param = action.parameter;
3133
+ let param;
3134
+ let resolvedBaseline;
3135
+ let scope;
3136
+ if (registry) {
3137
+ const resolved = registry.resolve(action.parameterType, action.scope);
3138
+ if (!resolved) return null;
3139
+ param = resolved.key;
3140
+ resolvedBaseline = resolved.currentValue;
3141
+ scope = resolved.scope;
3142
+ action.resolvedParameter = param;
3143
+ } else {
3144
+ param = action.resolvedParameter ?? action.parameterType;
3145
+ scope = action.scope;
3146
+ }
3030
3147
  if (this.lockedParams.has(param)) return null;
3031
3148
  if (this.isOnCooldown(param, metrics.tick, thresholds.cooldownTicks)) return null;
3032
3149
  if (!simulationResult.netImprovement) return null;
3033
3150
  if (!simulationResult.noNewProblems) return null;
3034
3151
  if (this.activePlanCount >= thresholds.complexityBudgetMax) return null;
3035
- const currentValue = currentParams[param] ?? 1;
3152
+ const currentValue = resolvedBaseline ?? currentParams[param] ?? action.absoluteValue ?? 1;
3036
3153
  const magnitude = Math.min(action.magnitude ?? 0.1, thresholds.maxAdjustmentPercent);
3037
3154
  let targetValue;
3038
3155
  if (action.direction === "set" && action.absoluteValue !== void 0) {
@@ -3052,7 +3169,7 @@ var Planner = class {
3052
3169
  id: `plan_${metrics.tick}_${param}`,
3053
3170
  diagnosis,
3054
3171
  parameter: param,
3055
- ...action.currency !== void 0 ? { currency: action.currency } : {},
3172
+ ...scope !== void 0 ? { scope } : {},
3056
3173
  currentValue,
3057
3174
  targetValue,
3058
3175
  maxChangePercent: thresholds.maxAdjustmentPercent,
@@ -3061,7 +3178,6 @@ var Planner = class {
3061
3178
  metric: "avgSatisfaction",
3062
3179
  direction: "below",
3063
3180
  threshold: Math.max(20, metrics.avgSatisfaction - 10),
3064
- // rollback if sat drops >10 pts
3065
3181
  checkAfterTick: metrics.tick + estimatedLag + 3
3066
3182
  },
3067
3183
  simulationResult,
@@ -3076,6 +3192,9 @@ var Planner = class {
3076
3192
  recordRolledBack(_plan) {
3077
3193
  this.activePlanCount = Math.max(0, this.activePlanCount - 1);
3078
3194
  }
3195
+ recordSettled(_plan) {
3196
+ this.activePlanCount = Math.max(0, this.activePlanCount - 1);
3197
+ }
3079
3198
  isOnCooldown(param, currentTick, cooldownTicks) {
3080
3199
  const lastApplied = this.cooldowns.get(param);
3081
3200
  if (lastApplied === void 0) return false;
@@ -3094,40 +3213,54 @@ var Executor = class {
3094
3213
  }
3095
3214
  async apply(plan, adapter, currentParams) {
3096
3215
  const originalValue = currentParams[plan.parameter] ?? plan.currentValue;
3097
- await adapter.setParam(plan.parameter, plan.targetValue, plan.currency);
3216
+ await adapter.setParam(plan.parameter, plan.targetValue, plan.scope);
3098
3217
  plan.appliedAt = plan.diagnosis.tick;
3099
3218
  this.activePlans.push({ plan, originalValue });
3100
3219
  }
3101
3220
  /**
3102
3221
  * Check all active plans for rollback conditions.
3103
- * Called every tick after metrics are computed.
3104
- * Returns list of plans that were rolled back.
3222
+ * Returns { rolledBack, settled } plans that were undone and plans that passed their window.
3105
3223
  */
3106
3224
  async checkRollbacks(metrics, adapter) {
3107
3225
  const rolledBack = [];
3226
+ const settled = [];
3108
3227
  const remaining = [];
3109
3228
  for (const active of this.activePlans) {
3110
3229
  const { plan, originalValue } = active;
3111
3230
  const rc = plan.rollbackCondition;
3231
+ const maxActiveTicks = 200;
3232
+ if (plan.appliedAt !== void 0 && metrics.tick - plan.appliedAt > maxActiveTicks) {
3233
+ settled.push(plan);
3234
+ continue;
3235
+ }
3112
3236
  if (metrics.tick < rc.checkAfterTick) {
3113
3237
  remaining.push(active);
3114
3238
  continue;
3115
3239
  }
3116
3240
  const metricValue = this.getMetricValue(metrics, rc.metric);
3241
+ if (Number.isNaN(metricValue)) {
3242
+ console.warn(
3243
+ `[AgentE] Rollback check: metric path '${rc.metric}' resolved to NaN for plan '${plan.id}'. Triggering rollback as fail-safe.`
3244
+ );
3245
+ await adapter.setParam(plan.parameter, originalValue, plan.scope);
3246
+ rolledBack.push(plan);
3247
+ continue;
3248
+ }
3117
3249
  const shouldRollback = rc.direction === "below" ? metricValue < rc.threshold : metricValue > rc.threshold;
3118
3250
  if (shouldRollback) {
3119
- await adapter.setParam(plan.parameter, originalValue, plan.currency);
3251
+ await adapter.setParam(plan.parameter, originalValue, plan.scope);
3120
3252
  rolledBack.push(plan);
3121
3253
  } else {
3122
3254
  const settledTick = rc.checkAfterTick + 10;
3123
3255
  if (metrics.tick > settledTick) {
3256
+ settled.push(plan);
3124
3257
  } else {
3125
3258
  remaining.push(active);
3126
3259
  }
3127
3260
  }
3128
3261
  }
3129
3262
  this.activePlans = remaining;
3130
- return rolledBack;
3263
+ return { rolledBack, settled };
3131
3264
  }
3132
3265
  getMetricValue(metrics, metricPath) {
3133
3266
  const parts = metricPath.split(".");
@@ -3216,7 +3349,8 @@ var DecisionLog = class {
3216
3349
  return {
3217
3350
  id: `stub_${metrics.tick}`,
3218
3351
  diagnosis,
3219
- parameter: action.parameter,
3352
+ parameter: action.resolvedParameter ?? action.parameterType,
3353
+ ...action.scope !== void 0 ? { scope: action.scope } : {},
3220
3354
  currentValue: 1,
3221
3355
  targetValue: 1,
3222
3356
  maxChangePercent: 0,
@@ -3454,13 +3588,13 @@ var PersonaTracker = class {
3454
3588
  Earner: 0,
3455
3589
  Builder: 0,
3456
3590
  Social: 0,
3457
- Whale: 0,
3591
+ HighValue: 0,
3458
3592
  Influencer: 0
3459
3593
  };
3460
3594
  let total = 0;
3461
3595
  for (const [, history] of this.agentHistory) {
3462
3596
  const persona = this.classify(history);
3463
- counts[persona]++;
3597
+ counts[persona] = (counts[persona] ?? 0) + 1;
3464
3598
  total++;
3465
3599
  }
3466
3600
  if (total === 0) return {};
@@ -3480,7 +3614,7 @@ var PersonaTracker = class {
3480
3614
  const extraction = avg("netExtraction");
3481
3615
  const uniqueItems = avg("uniqueItemsHeld");
3482
3616
  const spend = avg("spendAmount");
3483
- if (spend > 1e3) return "Whale";
3617
+ if (spend > 1e3) return "HighValue";
3484
3618
  if (txRate > 10) return "Trader";
3485
3619
  if (uniqueItems > 5 && extraction < 0) return "Collector";
3486
3620
  if (extraction > 100) return "Earner";
@@ -3489,12 +3623,116 @@ var PersonaTracker = class {
3489
3623
  }
3490
3624
  };
3491
3625
 
3626
+ // src/ParameterRegistry.ts
3627
+ var ParameterRegistry = class {
3628
+ constructor() {
3629
+ this.parameters = /* @__PURE__ */ new Map();
3630
+ }
3631
+ /** Register a parameter. Overwrites if key already exists. */
3632
+ register(param) {
3633
+ this.parameters.set(param.key, { ...param });
3634
+ }
3635
+ /** Register multiple parameters at once. */
3636
+ registerAll(params) {
3637
+ for (const p of params) this.register(p);
3638
+ }
3639
+ /**
3640
+ * Resolve a parameterType + scope to a concrete RegisteredParameter.
3641
+ * Returns the best match, or undefined if no match.
3642
+ *
3643
+ * Matching rules (in priority order):
3644
+ * 1. Exact type match + all scope fields match
3645
+ * 2. Exact type match + partial scope match (tags overlap)
3646
+ * 3. Exact type match + no scope constraints
3647
+ * 4. undefined (no match)
3648
+ */
3649
+ resolve(type, scope) {
3650
+ const candidates = this.findByType(type);
3651
+ if (candidates.length === 0) return void 0;
3652
+ if (candidates.length === 1) return candidates[0];
3653
+ let bestScore = -1;
3654
+ let best;
3655
+ for (const candidate of candidates) {
3656
+ const score = this.scopeMatchScore(candidate.scope, scope);
3657
+ if (score > bestScore) {
3658
+ bestScore = score;
3659
+ best = candidate;
3660
+ }
3661
+ }
3662
+ return best;
3663
+ }
3664
+ /** Find all parameters of a given type. */
3665
+ findByType(type) {
3666
+ const results = [];
3667
+ for (const param of this.parameters.values()) {
3668
+ if (param.type === type) results.push(param);
3669
+ }
3670
+ return results;
3671
+ }
3672
+ /** Find all parameters belonging to a given system. */
3673
+ findBySystem(system) {
3674
+ const results = [];
3675
+ for (const param of this.parameters.values()) {
3676
+ if (param.scope?.system === system) results.push(param);
3677
+ }
3678
+ return results;
3679
+ }
3680
+ /** Get a parameter by its concrete key. */
3681
+ get(key) {
3682
+ return this.parameters.get(key);
3683
+ }
3684
+ /** Get the flow impact of a parameter by its concrete key. */
3685
+ getFlowImpact(key) {
3686
+ return this.parameters.get(key)?.flowImpact;
3687
+ }
3688
+ /** Update the current value of a registered parameter. */
3689
+ updateValue(key, value) {
3690
+ const param = this.parameters.get(key);
3691
+ if (param) {
3692
+ param.currentValue = value;
3693
+ }
3694
+ }
3695
+ /** Get all registered parameters. */
3696
+ getAll() {
3697
+ return [...this.parameters.values()];
3698
+ }
3699
+ /** Number of registered parameters. */
3700
+ get size() {
3701
+ return this.parameters.size;
3702
+ }
3703
+ // ── Private ─────────────────────────────────────────────────────────────
3704
+ scopeMatchScore(paramScope, queryScope) {
3705
+ if (!queryScope) return 0;
3706
+ if (!paramScope) return 0;
3707
+ let score = 0;
3708
+ if (queryScope.system && paramScope.system) {
3709
+ if (queryScope.system === paramScope.system) score += 10;
3710
+ else return -1;
3711
+ }
3712
+ if (queryScope.currency && paramScope.currency) {
3713
+ if (queryScope.currency === paramScope.currency) score += 5;
3714
+ else return -1;
3715
+ }
3716
+ if (queryScope.tags && queryScope.tags.length > 0 && paramScope.tags && paramScope.tags.length > 0) {
3717
+ const overlap = queryScope.tags.filter((t) => paramScope.tags.includes(t)).length;
3718
+ if (overlap > 0) {
3719
+ score += overlap * 3;
3720
+ } else {
3721
+ return -1;
3722
+ }
3723
+ } else if (queryScope.tags && queryScope.tags.length > 0 && paramScope.tags && paramScope.tags.length > 0) {
3724
+ return -1;
3725
+ }
3726
+ return score;
3727
+ }
3728
+ };
3729
+
3492
3730
  // src/AgentE.ts
3493
3731
  var AgentE = class {
3494
3732
  constructor(config) {
3495
- this.simulator = new Simulator();
3496
3733
  this.planner = new Planner();
3497
3734
  this.executor = new Executor();
3735
+ this.registry = new ParameterRegistry();
3498
3736
  // ── State ──
3499
3737
  this.log = new DecisionLog();
3500
3738
  this.personaTracker = new PersonaTracker();
@@ -3519,7 +3757,8 @@ var AgentE = class {
3519
3757
  gracePeriod: config.gracePeriod ?? 50,
3520
3758
  checkInterval: config.checkInterval ?? 5,
3521
3759
  maxAdjustmentPercent: config.maxAdjustmentPercent ?? 0.15,
3522
- cooldownTicks: config.cooldownTicks ?? 15
3760
+ cooldownTicks: config.cooldownTicks ?? 15,
3761
+ parameters: config.parameters ?? []
3523
3762
  };
3524
3763
  this.thresholds = {
3525
3764
  ...DEFAULT_THRESHOLDS,
@@ -3531,6 +3770,10 @@ var AgentE = class {
3531
3770
  this.observer = new Observer(tickConfig);
3532
3771
  this.store = new MetricStore(tickConfig);
3533
3772
  this.diagnoser = new Diagnoser(ALL_PRINCIPLES);
3773
+ if (config.parameters) {
3774
+ this.registry.registerAll(config.parameters);
3775
+ }
3776
+ this.simulator = new Simulator(this.registry);
3534
3777
  if (config.onDecision) this.on("decision", config.onDecision);
3535
3778
  if (config.onAlert) this.on("alert", config.onAlert);
3536
3779
  if (config.onRollback) this.on("rollback", config.onRollback);
@@ -3568,15 +3811,24 @@ var AgentE = class {
3568
3811
  this.currentTick = currentState.tick;
3569
3812
  const events = [...this.eventBuffer];
3570
3813
  this.eventBuffer = [];
3571
- const metrics = this.observer.compute(currentState, events);
3814
+ let metrics;
3815
+ try {
3816
+ metrics = this.observer.compute(currentState, events);
3817
+ } catch (err) {
3818
+ console.error(`[AgentE] Observer.compute() failed at tick ${currentState.tick}:`, err);
3819
+ return;
3820
+ }
3572
3821
  this.store.record(metrics);
3573
3822
  this.personaTracker.update(currentState);
3574
3823
  metrics.personaDistribution = this.personaTracker.getDistribution();
3575
- const rolledBack = await this.executor.checkRollbacks(metrics, this.adapter);
3824
+ const { rolledBack, settled } = await this.executor.checkRollbacks(metrics, this.adapter);
3576
3825
  for (const plan2 of rolledBack) {
3577
3826
  this.planner.recordRolledBack(plan2);
3578
3827
  this.emit("rollback", plan2, "rollback condition triggered");
3579
3828
  }
3829
+ for (const plan2 of settled) {
3830
+ this.planner.recordSettled(plan2);
3831
+ }
3580
3832
  if (metrics.tick < this.config.gracePeriod) return;
3581
3833
  if (metrics.tick % this.config.checkInterval !== 0) return;
3582
3834
  const diagnoses = this.diagnoser.diagnose(metrics, this.thresholds);
@@ -3598,7 +3850,8 @@ var AgentE = class {
3598
3850
  metrics,
3599
3851
  simulationResult,
3600
3852
  this.params,
3601
- this.thresholds
3853
+ this.thresholds,
3854
+ this.registry
3602
3855
  );
3603
3856
  if (!plan) {
3604
3857
  let reason = "skipped_cooldown";
@@ -3618,6 +3871,7 @@ var AgentE = class {
3618
3871
  }
3619
3872
  await this.executor.apply(plan, this.adapter, this.params);
3620
3873
  this.params[plan.parameter] = plan.targetValue;
3874
+ this.registry.updateValue(plan.parameter, plan.targetValue);
3621
3875
  this.planner.recordApplied(plan, metrics.tick);
3622
3876
  const entry = this.log.record(topDiagnosis, plan, "applied", metrics);
3623
3877
  this.emit("decision", entry);
@@ -3627,6 +3881,7 @@ var AgentE = class {
3627
3881
  async apply(plan) {
3628
3882
  await this.executor.apply(plan, this.adapter, this.params);
3629
3883
  this.params[plan.parameter] = plan.targetValue;
3884
+ this.registry.updateValue(plan.parameter, plan.targetValue);
3630
3885
  this.planner.recordApplied(plan, this.currentTick);
3631
3886
  }
3632
3887
  // ── Developer API ───────────────────────────────────────────────────────────
@@ -3651,6 +3906,12 @@ var AgentE = class {
3651
3906
  removePrinciple(id) {
3652
3907
  this.diagnoser.removePrinciple(id);
3653
3908
  }
3909
+ registerParameter(param) {
3910
+ this.registry.register(param);
3911
+ }
3912
+ getRegistry() {
3913
+ return this.registry;
3914
+ }
3654
3915
  registerCustomMetric(name, fn) {
3655
3916
  this.observer.registerCustomMetric(name, fn);
3656
3917
  }
@@ -4053,11 +4314,11 @@ function describeValue(value) {
4053
4314
  Executor,
4054
4315
  FEEDBACK_LOOP_PRINCIPLES,
4055
4316
  INCENTIVE_PRINCIPLES,
4056
- LIVEOPS_PRINCIPLES,
4057
4317
  MARKET_DYNAMICS_PRINCIPLES,
4058
4318
  MEASUREMENT_PRINCIPLES,
4059
4319
  MetricStore,
4060
4320
  OPEN_ECONOMY_PRINCIPLES,
4321
+ OPERATIONS_PRINCIPLES,
4061
4322
  Observer,
4062
4323
  P10_SpawnWeightingUsesInversePopulation,
4063
4324
  P11_TwoTierPressure,
@@ -4119,9 +4380,10 @@ function describeValue(value) {
4119
4380
  P7_NonSpecialistsSubsidiseSpecialists,
4120
4381
  P8_RegulatorCannotFightDesign,
4121
4382
  P9_RoleSwitchingNeedsFriction,
4383
+ PARTICIPANT_EXPERIENCE_PRINCIPLES,
4122
4384
  PERSONA_HEALTHY_RANGES,
4123
- PLAYER_EXPERIENCE_PRINCIPLES,
4124
4385
  POPULATION_PRINCIPLES,
4386
+ ParameterRegistry,
4125
4387
  PersonaTracker,
4126
4388
  Planner,
4127
4389
  REGULATOR_PRINCIPLES,