@idlekit/cli 0.3.0 → 0.3.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/main.js CHANGED
@@ -147,7 +147,7 @@ var require_pad_end = __commonJS(function(exports, module) {
147
147
  var require_package = __commonJS(function(exports, module) {
148
148
  module.exports = {
149
149
  name: "@idlekit/cli",
150
- version: "0.3.0",
150
+ version: "0.3.1",
151
151
  description: "Bun-first idk CLI for idle game scenario validation, simulation, tuning, and reporting.",
152
152
  license: "MIT",
153
153
  type: "module",
@@ -200,7 +200,7 @@ var require_package = __commonJS(function(exports, module) {
200
200
  "@clack/prompts": "1.8.1",
201
201
  "@gunshi/plugin-completion": "0.37.3",
202
202
  "@gunshi/plugin-suggestion": "0.37.3",
203
- "@idlekit/core": "0.3.0",
203
+ "@idlekit/core": "0.3.1",
204
204
  ajv: "^8.20.0",
205
205
  gunshi: "0.37.3",
206
206
  yaml: "^2.9.1",
@@ -19292,7 +19292,8 @@ function parseMoney(E, input, opts) {
19292
19292
  throw new Error(`Unknown suffix: ${suffixToken}`);
19293
19293
  }
19294
19294
  const exp3 = suffixIndex * 3;
19295
- const scaled = exp3 > 0 ? E.mulN(E.from(numeric), E.from(`1e${exp3}`)) : E.from(numeric);
19295
+ const [mantissa, exponent = "0"] = numeric.split(/e/i);
19296
+ const scaled = E.from(exp3 > 0 ? `${mantissa}e${BigInt(exponent) + BigInt(exp3)}` : numeric);
19296
19297
  const unit = opts.unit ?? (inlineUnit ? { code: inlineUnit } : undefined);
19297
19298
  if (!unit) {
19298
19299
  throw new Error("Unit is required. Provide opts.unit or unit in input string.");
@@ -21146,26 +21147,31 @@ function modelCreateParams(args) {
21146
21147
  function assertScenarioPlanInputs(args) {
21147
21148
  const startT = args.scenario.initial.t ?? 0;
21148
21149
  const durationSec = args.scenario.clock.durationSec;
21149
- for (const stepSec of args.stepSecs ?? [args.scenario.clock.stepSec]) {
21150
+ const stepSecs = args.stepSecs ?? [args.scenario.clock.stepSec];
21151
+ for (const stepSec of stepSecs) {
21150
21152
  assertSimulationClock("scenario clock", { stepSec, durationSec, startT });
21151
- const firstDt = durationSec !== undefined ? Math.min(stepSec, durationSec) : stepSec;
21152
- if (firstDt > 0 && !(startT + firstDt > startT)) {
21153
- throw new Error(`scenario clock tick did not advance state.t (start t: ${startT}, step: ${firstDt}); state.t is too large for stepSec ${stepSec}`);
21154
- }
21155
21153
  }
21156
- buildInitialState({
21154
+ const unit = buildUnit({ scenario: args.scenario });
21155
+ const initial = buildInitialState({
21157
21156
  E: args.E,
21158
21157
  scenario: args.scenario,
21159
- unit: buildUnit({ scenario: args.scenario }),
21158
+ unit,
21160
21159
  allowSuffixNotation: args.allowSuffixNotation ?? true
21161
21160
  });
21162
- compileUntilExpr({
21161
+ const until = compileUntilExpr({
21163
21162
  expr: args.scenario.clock.untilExpr,
21164
21163
  E: args.E,
21165
- unit: buildUnit({ scenario: args.scenario }),
21164
+ unit,
21166
21165
  allowSuffixNotation: args.allowSuffixNotation ?? true,
21167
21166
  allowUnsafe: false
21168
21167
  });
21168
+ const untilMet = until?.(initial) ?? false;
21169
+ for (const stepSec of stepSecs) {
21170
+ const first = nextBoundary({ elapsedSec: 0, steps: 0, stepSec, durationSec, untilMet, hasUntil: !!until });
21171
+ if (first.kind === "step") {
21172
+ assertTickAdvanced("scenario clock", startT, startT + first.dt, first.dt, stepSec, startT);
21173
+ }
21174
+ }
21169
21175
  }
21170
21176
  function buildUnit(args) {
21171
21177
  return args.unitFactory ? args.unitFactory(args.scenario.unit.code) : { code: args.scenario.unit.code, symbol: args.scenario.unit.symbol };
@@ -22137,13 +22143,16 @@ function resetSnapshot(ctx, state) {
22137
22143
  count: state.prestige.count
22138
22144
  };
22139
22145
  }
22140
- function wallNow(ctx, t) {
22146
+ function wallNow(ctx, t, explicitWallT) {
22141
22147
  const clocks = ctx.clocks;
22142
- if (!clocks || typeof clocks.wallT !== "number" || !Number.isFinite(clocks.wallT))
22143
- return null;
22144
- const offset = Number.isFinite(clocks.rewardT) ? Math.max(0, t - clocks.rewardT) : 0;
22145
- const wall = clocks.wallT + offset;
22146
- return Number.isFinite(clocks.wallEndT) ? Math.min(wall, clocks.wallEndT) : wall;
22148
+ let wall = explicitWallT;
22149
+ if (wall === undefined) {
22150
+ if (!clocks || typeof clocks.wallT !== "number" || !Number.isFinite(clocks.wallT))
22151
+ return null;
22152
+ const offset = Number.isFinite(clocks.rewardT) ? Math.max(0, t - clocks.rewardT) : 0;
22153
+ wall = clocks.wallT + offset;
22154
+ }
22155
+ return clocks && Number.isFinite(clocks.wallEndT) ? Math.min(wall, clocks.wallEndT) : wall;
22147
22156
  }
22148
22157
  function applyDecisions(args) {
22149
22158
  const { ctx, model, constraints } = args;
@@ -22304,7 +22313,7 @@ function applyDecisions(args) {
22304
22313
  actionId: action.id,
22305
22314
  kind: action.kind,
22306
22315
  t: decisionT,
22307
- wallT: args.wallT ?? wallNow(ctx, decisionT),
22316
+ wallT: wallNow(ctx, decisionT, args.wallT ?? undefined),
22308
22317
  cost: paid ? { unit: paid.unit.code, decimal: E.toString(paid.amount) } : null,
22309
22318
  rewardDelta: E.toString(E.sub(next.prestige.points, pointsBefore)),
22310
22319
  before,
@@ -28984,6 +28993,7 @@ function renderExperienceMarkdown(args) {
28984
28993
  `- Scenario: \`${args.scenarioPath}\``,
28985
28994
  `- Intent: ${args.intent ?? "unspecified"}`,
28986
28995
  `- Mode: ${args.mode}`,
28996
+ ...args.mode === "monte-carlo" ? ["- Snapshot scope: one recorded session (end state, growth, milestones, and perceived progression)"] : [],
28987
28997
  `- Session pattern: \`${snapshot.session.pattern.id}\` for ${snapshot.session.pattern.days} day(s)`,
28988
28998
  `- Active blocks: ${snapshot.session.activeBlocks}${snapshot.session.budgetStops > 0 ? ` (${snapshot.session.budgetStops} cut short by maxSteps)` : ""}`,
28989
28999
  `- Active / offline: ${formatMetric(snapshot.session.totalActiveSec, 0)}s / ${formatMetric(snapshot.session.totalOfflineSec, 0)}s`,
@@ -29037,8 +29047,47 @@ function renderExperienceMarkdown(args) {
29037
29047
  ...snapshot.session.budgetStops > 0 ? ["- Active play was cut short by maxSteps; read these windows and progression rates with that time budget in mind."] : []
29038
29048
  ];
29039
29049
  if (monteCarlo) {
29040
- lines.push("", "## Monte Carlo", "", `- Draws: ${monteCarlo.draws}`, `- Seed: ${monteCarlo.seed}`, `- Net worth log10 mean: ${formatMetric(monteCarlo.endNetWorthLog10.mean, 3)}`, `- Visible changes / minute mean: ${formatMetric(monteCarlo.visibleChangesPerMinute.mean, 3)}`, `- No-reward gap mean: ${formatMetric(monteCarlo.maxNoRewardGapSec.mean, 2)}s`, `- First visible completion: ${monteCarlo.firstVisible.accounting.completionRate ?? "unavailable"}`, `- First visible population p95: ${monteCarlo.firstVisible.population.status === "observed" ? `${monteCarlo.firstVisible.population.value}s` : monteCarlo.firstVisible.population.status}`);
29050
+ lines.push("", "## Monte Carlo", "", `- Draws: ${monteCarlo.draws}`, `- Seed: ${monteCarlo.seed}`, `- Net worth log10 mean: ${formatMetric(monteCarlo.endNetWorthLog10.mean, 3)}`, `- Visible changes / minute mean: ${formatMetric(monteCarlo.visibleChangesPerMinute.mean, 3)}`, `- No-reward gap mean: ${formatMetric(monteCarlo.maxNoRewardGapSec.mean, 2)}s`, `- First visible completion: ${monteCarlo.firstVisible.accounting.completionRate ?? "unavailable"}`, `- First visible observed / right-censored / failed / unsupported: ${monteCarlo.firstVisible.accounting.observed} / ${monteCarlo.firstVisible.accounting.rightCensored} / ${monteCarlo.firstVisible.accounting.failed} / ${monteCarlo.firstVisible.accounting.unsupported}`, `- First visible population p95: ${monteCarlo.firstVisible.population.status === "observed" ? `${monteCarlo.firstVisible.population.value}s` : monteCarlo.firstVisible.population.status}`);
29051
+ }
29052
+ const checks = [
29053
+ "- Compare the same initial state, horizon, session pattern, and seed policy when attributing a change to a strategy."
29054
+ ];
29055
+ if (args.mode === "monte-carlo") {
29056
+ checks.push("- The snapshot checks concern the recorded session. Use completion and population summaries when deciding about the draw population.");
29057
+ }
29058
+ if (snapshot.session.budgetStops > 0) {
29059
+ checks.push("- Increase the step budget and rerun the same horizon and session pattern before treating cut active blocks as complete play.");
29060
+ }
29061
+ if (coverage !== "complete") {
29062
+ checks.push("- Rerun with complete milestone observation before comparing exact milestone keys. Read any retained first-time summary separately from key coverage.");
29063
+ }
29064
+ if (snapshot.perceived.activeSeconds <= 0) {
29065
+ checks.push("- Collect an active-play observation window before judging visible progress or purchase feedback.");
29066
+ } else if (snapshot.perceived.visibleChangeCount === 0) {
29067
+ checks.push("- No visible change was observed in the active window. Check the feedback rules and rerun a matched longer window before interpreting the pace.");
29068
+ }
29069
+ if (snapshot.session.lostRewardSec > 0) {
29070
+ checks.push("- Compare matched offline runs with the cap or decay changed to measure the effect of lost reward credit.");
29071
+ }
29072
+ if (slowWindows.length > 0) {
29073
+ checks.push("- Inspect actions and income around the sampled stall or softcap windows, then compare a matched counterfactual to test the suspected cause.");
29074
+ }
29075
+ if (firstPrestige !== undefined) {
29076
+ checks.push("- Compare prestige with a matched run without that reset to estimate its net benefit.");
29077
+ }
29078
+ if (monteCarlo) {
29079
+ checks.push("- Check first-visible completion and right-censored, failed, and unsupported counts before reading conditional timing statistics.");
29080
+ if (monteCarlo.firstVisible.accounting.rightCensored > 0) {
29081
+ checks.push(`- ${monteCarlo.firstVisible.accounting.rightCensored} draw(s) had no visible change through the last active observation on the reward-time clock. This can differ from the wall horizon. Extend matched active observation to investigate those censored observations.`);
29082
+ }
29083
+ if (monteCarlo.firstVisible.population.status !== "observed") {
29084
+ checks.push("- The population p95 is unresolved. Conditional observed-change timings describe only the observations that completed.");
29085
+ }
29086
+ if (monteCarlo.firstVisible.accounting.failed > 0 || monteCarlo.firstVisible.accounting.unsupported > 0) {
29087
+ checks.push("- Resolve failed or unsupported draws before relying on timing statistics for the full cohort.");
29088
+ }
29041
29089
  }
29090
+ lines.push("", "## Next checks", "", ...checks);
29042
29091
  return `${lines.join(`
29043
29092
  `)}
29044
29093
  `;
@@ -33077,6 +33126,21 @@ function renderCompareMarkdown(args) {
33077
33126
  if (args.results.some((result) => result.metric === "timeToMilestone")) {
33078
33127
  lines.push("- Time-to-milestone scores can include an unreached penalty beyond the session horizon; that penalty is not an observed milestone time.");
33079
33128
  }
33129
+ lines.push("", "## Choosing between scenarios", "");
33130
+ if (preferences.a.length && preferences.b.length) {
33131
+ lines.push("- The selected metrics prefer different scenarios. Choose the metric that matches the design intent and inspect the opposing cost before deciding.");
33132
+ } else if (preferences.a.length || preferences.b.length) {
33133
+ const side = preferences.a.length ? "A" : "B";
33134
+ lines.push(`- ${side} is preferred on the selected decisive metrics. Check unselected costs and benefits before choosing the full strategy.`);
33135
+ } else if (args.results.length && args.results.every((result) => result.better === "tie")) {
33136
+ lines.push("- All selected metrics tie. Use another intent-relevant measurement to distinguish the scenarios.");
33137
+ } else {
33138
+ lines.push("- Select intent-relevant metrics with available measurements and decisions before choosing a scenario.");
33139
+ }
33140
+ if (args.results.some((result) => result.better !== "a" && result.better !== "b" && result.better !== "tie")) {
33141
+ lines.push("- Resolve undetermined measurements or decisions before relying on those metrics.");
33142
+ }
33143
+ lines.push("- For a strategy comparison, use matching horizons, initial states, session patterns, and seed policies to interpret the measured differences.");
33080
33144
  return `${lines.join(`
33081
33145
  `)}
33082
33146
  `;
@@ -38188,4 +38252,4 @@ export {
38188
38252
  cli2 as cli
38189
38253
  };
38190
38254
 
38191
- //# debugId=1DCC70029C55193864756E2164756E21
38255
+ //# debugId=1FC7442F0ACD246C64756E2164756E21