@vue-skuilder/db 0.2.16 → 0.2.17

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.
@@ -1644,6 +1644,30 @@ var init_SrsDebugger = __esm({
1644
1644
  }
1645
1645
  });
1646
1646
 
1647
+ // src/core/navigators/StrategyPressureDebugger.ts
1648
+ var StrategyPressureDebugger_exports = {};
1649
+ __export(StrategyPressureDebugger_exports, {
1650
+ captureStrategyPressure: () => captureStrategyPressure,
1651
+ clearStrategyPressureDebug: () => clearStrategyPressureDebug,
1652
+ getStrategyPressureDebug: () => getStrategyPressureDebug
1653
+ });
1654
+ function captureStrategyPressure(snapshot) {
1655
+ snapshots2.set(`${snapshot.source}:${snapshot.courseId}`, snapshot);
1656
+ }
1657
+ function getStrategyPressureDebug() {
1658
+ return [...snapshots2.values()].sort((a, b) => b.timestamp - a.timestamp);
1659
+ }
1660
+ function clearStrategyPressureDebug() {
1661
+ snapshots2.clear();
1662
+ }
1663
+ var snapshots2;
1664
+ var init_StrategyPressureDebugger = __esm({
1665
+ "src/core/navigators/StrategyPressureDebugger.ts"() {
1666
+ "use strict";
1667
+ snapshots2 = /* @__PURE__ */ new Map();
1668
+ }
1669
+ });
1670
+
1647
1671
  // src/core/navigators/generators/CompositeGenerator.ts
1648
1672
  var CompositeGenerator_exports = {};
1649
1673
  __export(CompositeGenerator_exports, {
@@ -2008,6 +2032,13 @@ function shuffleInPlace(arr) {
2008
2032
  [arr[i], arr[j]] = [arr[j], arr[i]];
2009
2033
  }
2010
2034
  }
2035
+ function formatAge(ms) {
2036
+ const minutes = Math.max(0, Math.round(ms / 6e4));
2037
+ if (minutes < 60) return `${minutes}m`;
2038
+ const hours = Math.round(minutes / 60);
2039
+ if (hours < 24) return `${hours}h`;
2040
+ return `${Math.round(hours / 24)}d`;
2041
+ }
2011
2042
  function pickTopByScore(cards, limit) {
2012
2043
  return [...cards].sort((a, b) => b.score - a.score || a.cardId.localeCompare(b.cardId)).slice(0, limit);
2013
2044
  }
@@ -2016,6 +2047,7 @@ var init_prescribed = __esm({
2016
2047
  "src/core/navigators/generators/prescribed.ts"() {
2017
2048
  "use strict";
2018
2049
  init_navigators();
2050
+ init_StrategyPressureDebugger();
2019
2051
  init_logger();
2020
2052
  DEFAULT_FRESHNESS_WINDOW = 3;
2021
2053
  DEFAULT_MAX_DIRECT_PER_RUN = 3;
@@ -2092,6 +2124,7 @@ var init_prescribed = __esm({
2092
2124
  const groupRuntimes = [];
2093
2125
  const priorPracticeDebt = progress.practiceDebt ?? {};
2094
2126
  const nextPracticeDebt = {};
2127
+ const practiceDebtsByGroup = /* @__PURE__ */ new Map();
2095
2128
  for (const group of this.config.groups) {
2096
2129
  const runtime = this.buildGroupRuntimeState({
2097
2130
  group,
@@ -2140,7 +2173,7 @@ var init_prescribed = __esm({
2140
2173
  courseId,
2141
2174
  emittedIds
2142
2175
  );
2143
- const practiceCards = this.buildPracticeCards({
2176
+ const practice = this.buildPracticeCards({
2144
2177
  group,
2145
2178
  courseId,
2146
2179
  emittedIds,
@@ -2153,7 +2186,8 @@ var init_prescribed = __esm({
2153
2186
  priorPracticeDebt,
2154
2187
  nextPracticeDebt
2155
2188
  });
2156
- emitted.push(...directCards, ...supportCards, ...discoveredSupportCards, ...practiceCards);
2189
+ practiceDebtsByGroup.set(group.id, practice.debts);
2190
+ emitted.push(...directCards, ...supportCards, ...discoveredSupportCards, ...practice.cards);
2157
2191
  }
2158
2192
  nextState.practiceDebt = nextPracticeDebt;
2159
2193
  const hintSummary = this.buildSupportHintSummary(groupRuntimes);
@@ -2169,6 +2203,7 @@ var init_prescribed = __esm({
2169
2203
  } else {
2170
2204
  logger.info("[Prescribed] No hints to emit (no blocked targets or no support tags)");
2171
2205
  }
2206
+ this.capturePressureSnapshot(courseId, groupRuntimes, practiceDebtsByGroup, emitted, hints);
2172
2207
  if (emitted.length === 0) {
2173
2208
  logger.info(
2174
2209
  "[Prescribed] 0 cards emitted (all targets blocked, authored/discovered support candidates exhausted)" + (hints ? " \u2014 boost hints emitted but may not survive filters" : "")
@@ -2230,6 +2265,60 @@ var init_prescribed = __esm({
2230
2265
  supportTags: [...supportTags].sort()
2231
2266
  };
2232
2267
  }
2268
+ /**
2269
+ * Translate this run's per-group runtimes and practice debts into gauges on
2270
+ * the generic strategy-pressure debug channel (rendered by the live session
2271
+ * overlay's "strategy backpressure" section).
2272
+ */
2273
+ capturePressureSnapshot(courseId, groupRuntimes, practiceDebtsByGroup, emitted, hints) {
2274
+ const now = Date.now();
2275
+ const gauges = [];
2276
+ for (const runtime of groupRuntimes) {
2277
+ const group = runtime.group;
2278
+ const window2 = group.freshnessWindowSessions ?? DEFAULT_FRESHNESS_WINDOW;
2279
+ gauges.push({
2280
+ id: `group:${group.id}:target`,
2281
+ label: `${group.id} targets`,
2282
+ multiplier: runtime.pressureMultiplier,
2283
+ max: MAX_TARGET_MULTIPLIER,
2284
+ detail: `${runtime.pendingTargets.length} pending (${runtime.surfaceableTargets.length} surfaceable, ${runtime.blockedTargets.length} blocked) \xB7 sinceSurfaced ${runtime.sessionsSinceSurfaced}/${window2}`,
2285
+ items: runtime.blockedTargets.map((cardId) => ({ label: cardId, value: "blocked" }))
2286
+ });
2287
+ if (runtime.blockedTargets.length > 0) {
2288
+ const mode = runtime.supportCandidates.length > 0 ? "direct-support" : runtime.discoveredSupportCandidates.length > 0 ? "inserted-support" : "boost-only";
2289
+ gauges.push({
2290
+ id: `group:${group.id}:support`,
2291
+ label: `${group.id} support`,
2292
+ multiplier: runtime.supportMultiplier,
2293
+ max: MAX_SUPPORT_MULTIPLIER,
2294
+ detail: `mode=${mode} \xB7 ${runtime.supportTags.length} support tag(s)`,
2295
+ items: runtime.supportTags.map((tag) => ({ label: tag }))
2296
+ });
2297
+ }
2298
+ const debts = practiceDebtsByGroup.get(group.id) ?? [];
2299
+ if (debts.length > 0) {
2300
+ gauges.push({
2301
+ id: `group:${group.id}:practice-debt`,
2302
+ label: `${group.id} practice debt`,
2303
+ multiplier: Math.max(...debts.map((d) => d.multiplier)),
2304
+ max: MAX_PRACTICE_MULTIPLIER,
2305
+ detail: `${debts.length} under-practiced skill(s)`,
2306
+ items: debts.map((d) => ({
2307
+ label: d.tag,
2308
+ value: `\xD7${d.multiplier.toFixed(2)} \xB7 open ${formatAge(now - new Date(d.firstOwedAt).getTime())}`
2309
+ }))
2310
+ });
2311
+ }
2312
+ }
2313
+ captureStrategyPressure({
2314
+ source: "prescribed",
2315
+ courseId,
2316
+ gauges,
2317
+ topScore: emitted.length > 0 ? Math.max(...emitted.map((c) => c.score)) : null,
2318
+ hintsLabel: hints?._label,
2319
+ timestamp: now
2320
+ });
2321
+ }
2233
2322
  parseConfig(serializedData) {
2234
2323
  try {
2235
2324
  const parsed = JSON.parse(serializedData);
@@ -2386,6 +2475,7 @@ var init_prescribed = __esm({
2386
2475
  supportTags: [...supportTags],
2387
2476
  pressureMultiplier,
2388
2477
  supportMultiplier,
2478
+ sessionsSinceSurfaced,
2389
2479
  debugVersion: PRESCRIBED_DEBUG_VERSION
2390
2480
  };
2391
2481
  }
@@ -2518,13 +2608,13 @@ var init_prescribed = __esm({
2518
2608
  nextPracticeDebt
2519
2609
  } = args;
2520
2610
  const patterns = group.practiceTagPatterns ?? [];
2521
- if (patterns.length === 0) return [];
2611
+ if (patterns.length === 0) return { cards: [], debts: [] };
2522
2612
  const practiceMinCount = group.practiceMinCount ?? DEFAULT_PRACTICE_MIN_COUNT;
2523
2613
  const maxPractice = group.maxPracticeCardsPerRun ?? DEFAULT_MAX_PRACTICE_PER_RUN;
2524
2614
  const practiceTags = [...cardsByTag.keys()].filter(
2525
2615
  (tag) => patterns.some((p) => matchesTagPattern(tag, p)) && this.isUnlockedGatedSkill(tag, hierarchyConfigs, userTagElo, userGlobalElo) && (userTagElo[tag]?.count ?? 0) < practiceMinCount
2526
2616
  );
2527
- if (practiceTags.length === 0) return [];
2617
+ if (practiceTags.length === 0) return { cards: [], debts: [] };
2528
2618
  const now = Date.now();
2529
2619
  const DAY_MS = 24 * 60 * 60 * 1e3;
2530
2620
  const tagMultiplier = /* @__PURE__ */ new Map();
@@ -2539,6 +2629,11 @@ var init_prescribed = __esm({
2539
2629
  );
2540
2630
  tagMultiplier.set(tag, mult);
2541
2631
  }
2632
+ const debts = practiceTags.map((tag) => ({
2633
+ tag,
2634
+ multiplier: tagMultiplier.get(tag) ?? PRACTICE_BASE_MULT,
2635
+ firstOwedAt: nextPracticeDebt[tag]
2636
+ })).sort((a, b) => b.multiplier - a.multiplier || a.tag.localeCompare(b.tag));
2542
2637
  const practiceCardIds = this.findDiscoveredSupportCards({
2543
2638
  supportTags: practiceTags,
2544
2639
  cardsByTag,
@@ -2547,7 +2642,7 @@ var init_prescribed = __esm({
2547
2642
  excludedIds: emittedIds,
2548
2643
  limit: maxPractice
2549
2644
  });
2550
- if (practiceCardIds.length === 0) return [];
2645
+ if (practiceCardIds.length === 0) return { cards: [], debts };
2551
2646
  logger.info(
2552
2647
  `[Prescribed] Group '${group.id}' practice: ${practiceTags.length} unlocked under-practiced skill(s), emitting ${practiceCardIds.length} drill card(s)`
2553
2648
  );
@@ -2577,7 +2672,7 @@ var init_prescribed = __esm({
2577
2672
  ]
2578
2673
  });
2579
2674
  }
2580
- return cards;
2675
+ return { cards, debts };
2581
2676
  }
2582
2677
  /**
2583
2678
  * True for a skill that was *gated and is now reached*: it has at least one
@@ -5254,6 +5349,7 @@ var init_3 = __esm({
5254
5349
  "./PipelineAssembler.ts": () => Promise.resolve().then(() => (init_PipelineAssembler(), PipelineAssembler_exports)),
5255
5350
  "./PipelineDebugger.ts": () => Promise.resolve().then(() => (init_PipelineDebugger(), PipelineDebugger_exports)),
5256
5351
  "./SrsDebugger.ts": () => Promise.resolve().then(() => (init_SrsDebugger(), SrsDebugger_exports)),
5352
+ "./StrategyPressureDebugger.ts": () => Promise.resolve().then(() => (init_StrategyPressureDebugger(), StrategyPressureDebugger_exports)),
5257
5353
  "./defaults.ts": () => Promise.resolve().then(() => (init_defaults(), defaults_exports)),
5258
5354
  "./diversityRerank.ts": () => Promise.resolve().then(() => (init_diversityRerank(), diversityRerank_exports)),
5259
5355
  "./filters/WeightedFilter.ts": () => Promise.resolve().then(() => (init_WeightedFilter(), WeightedFilter_exports)),
@@ -5286,7 +5382,9 @@ __export(navigators_exports, {
5286
5382
  NavigatorRole: () => NavigatorRole,
5287
5383
  NavigatorRoles: () => NavigatorRoles,
5288
5384
  Navigators: () => Navigators,
5385
+ captureStrategyPressure: () => captureStrategyPressure,
5289
5386
  clearSrsBacklogDebug: () => clearSrsBacklogDebug,
5387
+ clearStrategyPressureDebug: () => clearStrategyPressureDebug,
5290
5388
  diversityRerank: () => diversityRerank,
5291
5389
  getActivePipeline: () => getActivePipeline,
5292
5390
  getCardOrigin: () => getCardOrigin,
@@ -5294,6 +5392,7 @@ __export(navigators_exports, {
5294
5392
  getRegisteredNavigatorNames: () => getRegisteredNavigatorNames,
5295
5393
  getRegisteredNavigatorRole: () => getRegisteredNavigatorRole,
5296
5394
  getSrsBacklogDebug: () => getSrsBacklogDebug,
5395
+ getStrategyPressureDebug: () => getStrategyPressureDebug,
5297
5396
  hasRegisteredNavigator: () => hasRegisteredNavigator,
5298
5397
  initializeNavigatorRegistry: () => initializeNavigatorRegistry,
5299
5398
  isFilter: () => isFilter,
@@ -5376,6 +5475,7 @@ var init_navigators = __esm({
5376
5475
  init_diversityRerank();
5377
5476
  init_PipelineDebugger();
5378
5477
  init_SrsDebugger();
5478
+ init_StrategyPressureDebugger();
5379
5479
  init_logger();
5380
5480
  init_();
5381
5481
  init_2();
@@ -8416,7 +8516,9 @@ export {
8416
8516
  aggregateOutcomesForGradient,
8417
8517
  areQuestionRecords,
8418
8518
  buildStrategyStateId,
8519
+ captureStrategyPressure,
8419
8520
  clearSrsBacklogDebug,
8521
+ clearStrategyPressureDebug,
8420
8522
  computeDeviation,
8421
8523
  computeEffectiveWeight,
8422
8524
  computeOutcomeSignal,
@@ -8433,6 +8535,7 @@ export {
8433
8535
  getRegisteredNavigatorNames,
8434
8536
  getRegisteredNavigatorRole,
8435
8537
  getSrsBacklogDebug,
8538
+ getStrategyPressureDebug,
8436
8539
  getStudySource,
8437
8540
  hasRegisteredNavigator,
8438
8541
  importParsedCards,