@vue-skuilder/platform-ui 0.2.29 → 0.2.30
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/assets/{About-CY5R6hcy.js → About-CrJNgazV.js} +2 -2
- package/dist/assets/{About-CY5R6hcy.js.map → About-CrJNgazV.js.map} +1 -1
- package/dist/assets/{AdminDashboard-BCf45Fv8.js → AdminDashboard-_gUrXsjy.js} +2 -2
- package/dist/assets/{AdminDashboard-BCf45Fv8.js.map → AdminDashboard-_gUrXsjy.js.map} +1 -1
- package/dist/assets/{AdminLearner-DkLKZPDK.js → AdminLearner-jMx6wPSq.js} +2 -2
- package/dist/assets/{AdminLearner-DkLKZPDK.js.map → AdminLearner-jMx6wPSq.js.map} +1 -1
- package/dist/assets/{ClassroomCtrlPanel-DgeiJJj-.js → ClassroomCtrlPanel-BnJklToj.js} +2 -2
- package/dist/assets/{ClassroomCtrlPanel-DgeiJJj-.js.map → ClassroomCtrlPanel-BnJklToj.js.map} +1 -1
- package/dist/assets/{Classrooms-By8_HZk7.js → Classrooms-D0IyR9Ts.js} +2 -2
- package/dist/assets/{Classrooms-By8_HZk7.js.map → Classrooms-D0IyR9Ts.js.map} +1 -1
- package/dist/assets/{CourseRouter-B0wsb_SJ.js → CourseRouter-PJfaOntu.js} +2 -2
- package/dist/assets/{CourseRouter-B0wsb_SJ.js.map → CourseRouter-PJfaOntu.js.map} +1 -1
- package/dist/assets/{CourseWare-BTFRjgBR-C5EKZq5q.js → CourseWare-BTFRjgBR-BjfKCi5w.js} +3 -3
- package/dist/assets/{CourseWare-BTFRjgBR-C5EKZq5q.js.map → CourseWare-BTFRjgBR-BjfKCi5w.js.map} +1 -1
- package/dist/assets/{Courses-HDdIzJ-P.js → Courses-DdvywKO_.js} +2 -2
- package/dist/assets/{Courses-HDdIzJ-P.js.map → Courses-DdvywKO_.js.map} +1 -1
- package/dist/assets/{EloModeration-C_gG6Y5k.js → EloModeration-WIBzqT9T.js} +2 -2
- package/dist/assets/{EloModeration-C_gG6Y5k.js.map → EloModeration-WIBzqT9T.js.map} +1 -1
- package/dist/assets/{JoinCode-WS6Hg8YR.js → JoinCode-C0bb6WJX.js} +2 -2
- package/dist/assets/{JoinCode-WS6Hg8YR.js.map → JoinCode-C0bb6WJX.js.map} +1 -1
- package/dist/assets/{NewCourseDialog-CY2XRDQA.js → NewCourseDialog-CiHuk-CK.js} +2 -2
- package/dist/assets/{NewCourseDialog-CY2XRDQA.js.map → NewCourseDialog-CiHuk-CK.js.map} +1 -1
- package/dist/assets/{ReleaseNotes-Bs2G8mQk.js → ReleaseNotes-DyZ37Cn2.js} +2 -2
- package/dist/assets/{ReleaseNotes-Bs2G8mQk.js.map → ReleaseNotes-DyZ37Cn2.js.map} +1 -1
- package/dist/assets/{RequestPasswordReset-Abu5C0PS.js → RequestPasswordReset-Mj_5AAYv.js} +2 -2
- package/dist/assets/{RequestPasswordReset-Abu5C0PS.js.map → RequestPasswordReset-Mj_5AAYv.js.map} +1 -1
- package/dist/assets/{ResetPassword-S4d7TZP3.js → ResetPassword-C7q91ujt.js} +2 -2
- package/dist/assets/{ResetPassword-S4d7TZP3.js.map → ResetPassword-C7q91ujt.js.map} +1 -1
- package/dist/assets/{Study-D29QwlyX.js → Study-BE7SFvWB.js} +2 -2
- package/dist/assets/{Study-D29QwlyX.js.map → Study-BE7SFvWB.js.map} +1 -1
- package/dist/assets/{TagInformation-DfxG__6Q.js → TagInformation-BcJ8NZNR.js} +2 -2
- package/dist/assets/{TagInformation-DfxG__6Q.js.map → TagInformation-BcJ8NZNR.js.map} +1 -1
- package/dist/assets/{User-DOCNnUcq.js → User-COnAN935.js} +2 -2
- package/dist/assets/{User-DOCNnUcq.js.map → User-COnAN935.js.map} +1 -1
- package/dist/assets/{UserStats-DfDpe-Tt.js → UserStats-OsqXRz9i.js} +2 -2
- package/dist/assets/{UserStats-DfDpe-Tt.js.map → UserStats-OsqXRz9i.js.map} +1 -1
- package/dist/assets/{VerifyEmail-DIvEVYE3.js → VerifyEmail-CLHHs6rK.js} +2 -2
- package/dist/assets/{VerifyEmail-DIvEVYE3.js.map → VerifyEmail-CLHHs6rK.js.map} +1 -1
- package/dist/assets/{admin-C1kbA23q.js → admin-CcfAXvvk.js} +2 -2
- package/dist/assets/{admin-C1kbA23q.js.map → admin-CcfAXvvk.js.map} +1 -1
- package/dist/assets/{chess-E2uOAyQS-DGFsCQt6.js → chess-E2uOAyQS-DosjXDE3.js} +2 -2
- package/dist/assets/{chess-E2uOAyQS-DGFsCQt6.js.map → chess-E2uOAyQS-DosjXDE3.js.map} +1 -1
- package/dist/assets/chess-Lfm7Fsvs-BXlftmOY.js +1 -0
- package/dist/assets/{common-ui.es-Kjr2m0-r.js → common-ui.es-Dxrfz5ZH.js} +1 -1
- package/dist/assets/{common-ui.es-BKEt3ead.js → common-ui.es-R3wgrUY1.js} +4 -4
- package/dist/assets/{common-ui.es-BKEt3ead.js.map → common-ui.es-R3wgrUY1.js.map} +1 -1
- package/dist/assets/dist-BfUpMink.js +1 -0
- package/dist/assets/{dist-BrFW2xcq.js → dist-CkXk63ji.js} +3 -3
- package/dist/assets/{dist-BrFW2xcq.js.map → dist-CkXk63ji.js.map} +1 -1
- package/dist/assets/{dist-5pzU2QGs.js → dist-D8fjT9uC.js} +2 -2
- package/dist/assets/dist-D8fjT9uC.js.map +1 -0
- package/dist/assets/dist-JDViCd82.js +1 -0
- package/dist/assets/{edit-ui.es-P-QQ-hsA.js → edit-ui.es-BikxzlfL.js} +2 -2
- package/dist/assets/{edit-ui.es-P-QQ-hsA.js.map → edit-ui.es-BikxzlfL.js.map} +1 -1
- package/dist/assets/edit-ui.es-aGJuCWTg.js +1 -0
- package/dist/assets/{french-Dk7YG8Td-oI_Y9Yn7.js → french-Dk7YG8Td-ZhATjUdj.js} +2 -2
- package/dist/assets/{french-Dk7YG8Td-oI_Y9Yn7.js.map → french-Dk7YG8Td-ZhATjUdj.js.map} +1 -1
- package/dist/assets/french-evUMlbbq-7dOIzdT-.js +1 -0
- package/dist/assets/{index-001F1CnH.js → index-ka0EHkhr.js} +4 -4
- package/dist/assets/{index-001F1CnH.js.map → index-ka0EHkhr.js.map} +1 -1
- package/dist/assets/{math-B4HbgYf6-DZW-sQzL.js → math-B4HbgYf6-Cq1UueAM.js} +2 -2
- package/dist/assets/{math-B4HbgYf6-DZW-sQzL.js.map → math-B4HbgYf6-Cq1UueAM.js.map} +1 -1
- package/dist/assets/math-DYni7rRl-DHiIEnUF.js +1 -0
- package/dist/assets/piano-BN5Btq91-Be0rOHlf.js +1 -0
- package/dist/assets/{piano-DF1g6yaX-D4bbUsyA.js → piano-DF1g6yaX-BcqYZZxL.js} +2 -2
- package/dist/assets/{piano-DF1g6yaX-D4bbUsyA.js.map → piano-DF1g6yaX-BcqYZZxL.js.map} +1 -1
- package/dist/assets/{pitch-CgGJFkZ1-CKqgrwBw.js → pitch-CgGJFkZ1-Brw1svYU.js} +2 -2
- package/dist/assets/{pitch-CgGJFkZ1-CKqgrwBw.js.map → pitch-CgGJFkZ1-Brw1svYU.js.map} +1 -1
- package/dist/assets/pitch-Dn0iNqiS-Gyoh8a8i.js +1 -0
- package/dist/assets/{server-D4EXUyhH.js → server-CaBn-8e8.js} +2 -2
- package/dist/assets/{server-D4EXUyhH.js.map → server-CaBn-8e8.js.map} +1 -1
- package/dist/assets/{sightsing-BFQ7HRig-3JyNWqf-.js → sightsing-BFQ7HRig-D7sLhTpT.js} +2 -2
- package/dist/assets/{sightsing-BFQ7HRig-3JyNWqf-.js.map → sightsing-BFQ7HRig-D7sLhTpT.js.map} +1 -1
- package/dist/assets/sightsing-CJX3k0Fd-Doax-Wov.js +1 -0
- package/dist/assets/{typing-BevtfWlp-DqPA0VeF.js → typing-BevtfWlp-KG7YuAST.js} +2 -2
- package/dist/assets/{typing-BevtfWlp-DqPA0VeF.js.map → typing-BevtfWlp-KG7YuAST.js.map} +1 -1
- package/dist/assets/typing-k-ojjO7G-BvSsa8XD.js +1 -0
- package/dist/assets/{word-work-BOnRlZgd-BoR5qGg1.js → word-work-BOnRlZgd-A6Il1bs_.js} +2 -2
- package/dist/assets/{word-work-BOnRlZgd-BoR5qGg1.js.map → word-work-BOnRlZgd-A6Il1bs_.js.map} +1 -1
- package/dist/assets/word-work-Dd6tIKrt-C8pnTzi3.js +1 -0
- package/dist/index.html +3 -3
- package/dist/sw.js +1 -1
- package/dist/sw.js.map +1 -1
- package/package.json +7 -7
- package/dist/assets/chess-Lfm7Fsvs-Dg_zv7tG.js +0 -1
- package/dist/assets/dist-5pzU2QGs.js.map +0 -1
- package/dist/assets/dist-BuuMJSO8.js +0 -1
- package/dist/assets/dist-CoJktbks.js +0 -1
- package/dist/assets/edit-ui.es-B-WRQSob.js +0 -1
- package/dist/assets/french-evUMlbbq--804zfgk.js +0 -1
- package/dist/assets/math-DYni7rRl-BiMckB4D.js +0 -1
- package/dist/assets/piano-BN5Btq91-BIOLr8tm.js +0 -1
- package/dist/assets/pitch-Dn0iNqiS-Dz5Ffxl4.js +0 -1
- package/dist/assets/sightsing-CJX3k0Fd-C5Fwkk2A.js +0 -1
- package/dist/assets/typing-k-ojjO7G-6WUzbhh3.js +0 -1
- package/dist/assets/word-work-Dd6tIKrt-0BhZ2cDk.js +0 -1
|
@@ -245,7 +245,7 @@ Example:
|
|
|
245
245
|
window.skuilder.pipeline.showLastRun()
|
|
246
246
|
window.skuilder.pipeline.showRun(1)
|
|
247
247
|
await window.skuilder.pipeline.diagnoseCardSpace()
|
|
248
|
-
`)}},mountPipelineDebugger()}}),SrsDebugger_exports={};__export(SrsDebugger_exports,{captureSrsBacklog:()=>captureSrsBacklog,clearSrsBacklogDebug:()=>clearSrsBacklogDebug,getSrsBacklogDebug:()=>getSrsBacklogDebug,noteReviewPressure:()=>noteReviewPressure});function captureSrsBacklog(e){snapshots.set(e.courseId,e)}function noteReviewPressure(e,t){let n=snapshots.get(e);n&&snapshots.set(e,{...n,reviewMass:t.mass,healthyBacklog:t.healthyMass,backlogGrowthRate:t.rate,backlogMultiplier:t.multiplier})}function getSrsBacklogDebug(){return[...snapshots.values()].sort((e,t)=>t.timestamp-e.timestamp)}function clearSrsBacklogDebug(){snapshots.clear()}var snapshots,init_SrsDebugger=__esm({"src/core/navigators/SrsDebugger.ts"(){snapshots=new Map}}),StrategyPressureDebugger_exports={};__export(StrategyPressureDebugger_exports,{captureStrategyPressure:()=>captureStrategyPressure,clearStrategyPressureDebug:()=>clearStrategyPressureDebug,getStrategyPressureDebug:()=>getStrategyPressureDebug});function captureStrategyPressure(e){snapshots2.set(`${e.source}:${e.courseId}`,e)}function getStrategyPressureDebug(){return[...snapshots2.values()].sort((e,t)=>t.timestamp-e.timestamp)}function clearStrategyPressureDebug(){snapshots2.clear()}var snapshots2,init_StrategyPressureDebugger=__esm({"src/core/navigators/StrategyPressureDebugger.ts"(){snapshots2=new Map}}),CompositeGenerator_exports={};__export(CompositeGenerator_exports,{AggregationMode:()=>AggregationMode,default:()=>CompositeGenerator});function mergeHints(e){let t=e.filter(e=>e!==void 0);if(t.length===0)return;let n={},r={};for(let e of t)for(let[t,n]of Object.entries(e.boostTags??{}))r[t]=(r[t]??1)*n;Object.keys(r).length>0&&(n.boostTags=r);let a={};for(let e of t)for(let[t,n]of Object.entries(e.boostCards??{}))a[t]=(a[t]??1)*n;Object.keys(a).length>0&&(n.boostCards=a);let concatUnique=e=>{let r=t.flatMap(t=>t[e]??[]);r.length>0&&(n[e]=[...new Set(r)])};concatUnique(`requireTags`),concatUnique(`requireCards`),concatUnique(`excludeTags`),concatUnique(`excludeCards`);let o=t.map(e=>e._label).filter(Boolean);return o.length>0&&(n._label=o.join(`; `)),Object.keys(n).length>0?n:void 0}var AggregationMode,DEFAULT_AGGREGATION_MODE,FREQUENCY_BOOST_FACTOR,CompositeGenerator,init_CompositeGenerator=__esm({"src/core/navigators/generators/CompositeGenerator.ts"(){init_navigators(),init_logger(),AggregationMode=(e=>(e.MAX=`max`,e.AVERAGE=`average`,e.FREQUENCY_BOOST=`frequencyBoost`,e))(AggregationMode||{}),DEFAULT_AGGREGATION_MODE=`frequencyBoost`,FREQUENCY_BOOST_FACTOR=.1,CompositeGenerator=class _CompositeGenerator extends ContentNavigator{constructor(e,t=DEFAULT_AGGREGATION_MODE){if(super(),_defineProperty(this,`name`,`Composite Generator`),_defineProperty(this,`generators`,void 0),_defineProperty(this,`aggregationMode`,void 0),this.generators=e,this.aggregationMode=t,e.length===0)throw Error(`CompositeGenerator requires at least one generator`);logger.debug(`[CompositeGenerator] Created with ${e.length} generators, mode: ${t}`)}static async fromStrategies(e,t,n,r=DEFAULT_AGGREGATION_MODE){return new _CompositeGenerator(await Promise.all(n.map(n=>ContentNavigator.create(e,t,n))),r)}async getWeightedCards(e,t){if(!t)throw Error(`CompositeGenerator.getWeightedCards requires a GeneratorContext. It should be called via Pipeline, not directly.`);let n=await Promise.all(this.generators.map(n=>n.getWeightedCards(e,t))),r=[];n.forEach((e,t)=>{let n=e.cards,a=this.generators[t].name||`Generator ${t}`,o=n.filter(e=>e.provenance[0]?.reason?.includes(`new card`)),s=n.filter(e=>e.provenance[0]?.reason?.includes(`review`));if(n.length>0){let e=Math.max(...n.map(e=>e.score)).toFixed(2),t=[];o.length>0&&t.push(`${o.length} new`),s.length>0&&t.push(`${s.length} reviews`);let c=t.length>0?t.join(`, `):`${n.length} cards`;r.push(`${a}: ${c} (top: ${e})`)}else r.push(`${a}: 0 cards`)}),logger.info(`[Composite] Generator breakdown: ${r.join(` | `)}`);let a=new Map;n.forEach((e,n)=>{let r=e.cards,o=this.generators[n],s=o.learnable?.weight??1,c;if(o.learnable&&!o.staticWeight&&t.orchestration){let e=o.strategyId;e&&(s=t.orchestration.getEffectiveWeight(e,o.learnable),c=t.orchestration.getDeviation(e))}for(let e of r){e.provenance.length>0&&(e.provenance[0].effectiveWeight=s,e.provenance[0].deviation=c);let t=a.get(e.cardId)||[];t.push({card:e,weight:s}),a.set(e.cardId,t)}});let o=[];for(let[,e]of a){let t=e.map(e=>e.card),n=this.aggregateScores(e),r=Math.max(0,n),a=t.flatMap(e=>e.provenance),s=t[0].score,c=r>s?`boosted`:r<s?`penalized`:`passed`,l=this.buildAggregationReason(e,r);o.push({...t[0],score:r,provenance:[...a,{strategy:`composite`,strategyName:`Composite Generator`,strategyId:`COMPOSITE_GENERATOR`,action:c,score:r,reason:l}]})}return{cards:o.sort((e,t)=>t.score-e.score).slice(0,e),hints:mergeHints(n.map(e=>e.hints))}}buildAggregationReason(e,t){let n=e.map(e=>e.card),r=n.length,a=n.map(e=>e.score.toFixed(2)).join(`, `);if(r===1){let n=Math.abs(e[0].weight-1)>.001?` (w=${e[0].weight.toFixed(2)})`:``;return`Single generator, score ${t.toFixed(2)}${n}`}let o=n.map(e=>e.provenance[0]?.strategy||`unknown`).join(`, `);switch(this.aggregationMode){case`max`:return`Max of ${r} generators (${o}): scores [${a}] \u2192 ${t.toFixed(2)}`;case`average`:return`Weighted Avg of ${r} generators (${o}): scores [${a}] \u2192 ${t.toFixed(2)}`;case`frequencyBoost`:{let n=e.reduce((e,t)=>e+t.weight,0),a=e.reduce((e,t)=>e+t.card.score*t.weight,0),s=n>0?a/n:0,c=1+FREQUENCY_BOOST_FACTOR*(r-1);return`Frequency boost from ${r} generators (${o}): w-avg ${s.toFixed(2)} \xD7 ${c.toFixed(2)} \u2192 ${t.toFixed(2)}`}default:return`Aggregated from ${r} generators: ${t.toFixed(2)}`}}aggregateScores(e){let t=e.map(e=>e.card.score);switch(this.aggregationMode){case`max`:return Math.max(...t);case`average`:{let t=e.reduce((e,t)=>e+t.weight,0);return t===0?0:e.reduce((e,t)=>e+t.card.score*t.weight,0)/t}case`frequencyBoost`:{let t=e.reduce((e,t)=>e+t.weight,0),n=e.reduce((e,t)=>e+t.card.score*t.weight,0);return(t>0?n/t:0)*(1+FREQUENCY_BOOST_FACTOR*(e.length-1))}default:return t[0]}}}}}),elo_exports={};__export(elo_exports,{default:()=>ELONavigator});var ELONavigator,init_elo=__esm({"src/core/navigators/generators/elo.ts"(){init_navigators(),init_logger(),ELONavigator=class extends ContentNavigator{constructor(e,t,n){super(e,t,n),_defineProperty(this,`name`,void 0),this.name=n?.name||`ELO`}async getWeightedCards(e,t){let n;n=t?.userElo===void 0?toCourseElo((await this.user.getCourseRegDoc(this.course.getCourseID())).elo).global.score:t.userElo;let r=await this.user.getActiveCards(),a=(await this.course.getCardsCenteredAtELO({limit:e,elo:`user`},e=>!r.some(t=>e.cardID===t.cardID))).map(e=>({...e,status:`new`})).map(e=>{let t=e.elo??1e3,r=Math.abs(t-n),a=.5+.5*Math.random();return{cardId:e.cardID,courseId:e.courseID,score:a,cardElo:t,provenance:[{strategy:`elo`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||`NAVIGATION_STRATEGY-ELO-default`,action:`generated`,score:a,reason:`new card near learner ELO: distance ${Math.round(r)} (card: ${Math.round(t)}, user: ${Math.round(n)}), jitter ${a.toFixed(3)}`}]}});a.sort((e,t)=>t.score-e.score);let o=a.slice(0,e);if(o.length>0){let e=o.slice(0,3).map(e=>e.score.toFixed(2)).join(`, `);logger.info(`[ELO] Course ${this.course.getCourseID()}: ${o.length} new cards (top scores: ${e})`)}else logger.info(`[ELO] Course ${this.course.getCourseID()}: No new cards available`);return{cards:o}}}}}),generators_exports={},init_generators=__esm({"src/core/navigators/generators/index.ts"(){}}),prescribed_exports={};__export(prescribed_exports,{default:()=>PrescribedCardsGenerator,unmetIntroExposeTags:()=>unmetIntroExposeTags});function dedupe(e){return[...new Set(e)]}function isoNow(){return new Date().toISOString()}function clamp(e,t,n){return Math.max(t,Math.min(n,e))}function matchesTagPattern(e,t){if(t===`*`)return!0;let n=t.replace(/[.+^${}()|[\]\\]/g,`\\$&`).replace(/\*/g,`.*`);return RegExp(`^${n}$`).test(e)}function extractWordStem(e){for(let t of[`c-ml-`,`c-ws-`,`c-spelling-`])if(e.startsWith(t)){let n=e.slice(t.length),r=n.lastIndexOf(`-`);return r>0?n.slice(0,r):n}return e}function shuffleInPlace(e){for(let t=e.length-1;t>0;t--){let n=Math.floor(Math.random()*(t+1));[e[t],e[n]]=[e[n],e[t]]}}function formatAge(e){let t=Math.max(0,Math.round(e/6e4));if(t<60)return`${t}m`;let n=Math.round(t/60);return n<24?`${n}h`:`${Math.round(n/24)}d`}function unmetIntroExposeTags(e,t,n){if(n<=0)return[];let r=new Set(e.filter(e=>e.startsWith(`gpc:expose:`)));for(let t of e){if(!t.startsWith(`gpc:intro:`))continue;let e=t.slice(10);e&&r.add(`gpc:expose:${e}`)}return[...r].filter(e=>{let r=t[e];return!r||r.count<n})}function pickTopByScore(e,t){return[...e].sort((e,t)=>t.score-e.score||e.cardId.localeCompare(t.cardId)).slice(0,t)}var DEFAULT_FRESHNESS_WINDOW,DEFAULT_MAX_DIRECT_PER_RUN,DEFAULT_MAX_SUPPORT_PER_RUN,DEFAULT_HIERARCHY_DEPTH,DEFAULT_MIN_COUNT,DEFAULT_PRACTICE_MIN_COUNT,DEFAULT_MAX_PRACTICE_PER_RUN,BASE_TARGET_SCORE,BASE_SUPPORT_SCORE,DISCOVERED_SUPPORT_SCORE,BASE_PRACTICE_SCORE,PRACTICE_BASE_MULT,MAX_PRACTICE_MULTIPLIER,PRACTICE_STALENESS_BUMP_PER_DAY,MAX_TARGET_MULTIPLIER,MAX_SUPPORT_MULTIPLIER,PRESCRIBED_DEBUG_VERSION,PrescribedCardsGenerator,init_prescribed=__esm({"src/core/navigators/generators/prescribed.ts"(){init_navigators(),init_StrategyPressureDebugger(),init_logger(),DEFAULT_FRESHNESS_WINDOW=3,DEFAULT_MAX_DIRECT_PER_RUN=3,DEFAULT_MAX_SUPPORT_PER_RUN=3,DEFAULT_HIERARCHY_DEPTH=2,DEFAULT_MIN_COUNT=3,DEFAULT_PRACTICE_MIN_COUNT=3,DEFAULT_MAX_PRACTICE_PER_RUN=4,BASE_TARGET_SCORE=1,BASE_SUPPORT_SCORE=.8,DISCOVERED_SUPPORT_SCORE=12,BASE_PRACTICE_SCORE=1,PRACTICE_BASE_MULT=2,MAX_PRACTICE_MULTIPLIER=4,PRACTICE_STALENESS_BUMP_PER_DAY=.5,MAX_TARGET_MULTIPLIER=8,MAX_SUPPORT_MULTIPLIER=4,PRESCRIBED_DEBUG_VERSION=`testversion-prescribed-v3`,PrescribedCardsGenerator=class extends ContentNavigator{constructor(e,t,n){super(e,t,n),_defineProperty(this,`name`,void 0),_defineProperty(this,`config`,void 0),this.name=n.name||`Prescribed Cards`,this.config=this.parseConfig(n.serializedData),logger.debug(`[Prescribed] Initialized with ${this.config.groups.length} groups and ${this.config.groups.reduce((e,t)=>e+t.targetCardIds.length,0)} targets`)}get strategyKey(){return`PrescribedProgress`}async getWeightedCards(e,t){if(this.config.groups.length===0||e<=0)return{cards:[]};let n=this.course.getCourseID(),r=await this.user.getActiveCards(),a=new Set(r.map(e=>e.cardID)),o=await this.user.getSeenCards(n).catch(()=>[]),s=new Set(o),c=await this.getStrategyState()??{updatedAt:isoNow(),groups:{}},l=await this.loadHierarchyConfigs(),u=await this.user.getCourseRegDoc(n).catch(()=>null),d=typeof u?.elo==`number`?u.elo:u?.elo?.global?.score??t?.userElo??1e3,p=typeof u?.elo==`number`?{}:u?.elo?.tags??{},m=dedupe(this.config.groups.flatMap(e=>e.targetCardIds)),g=dedupe(this.config.groups.flatMap(e=>e.supportCardIds??[])),_=dedupe([...m,...g]),v=_.length>0?await this.course.getAppliedTagsBatch(_):new Map,y=await this.course.getCourseTagStubs().catch(()=>({rows:[],offset:0,total_rows:0})),b=new Map;for(let e of y.rows??[]){let t=e.doc;t?.name&&Array.isArray(t.taggedCards)&&b.set(t.name,[...t.taggedCards])}let x={updatedAt:isoNow(),groups:{}},S=[],C=new Set,w=[],T=c.practiceDebt??{},E={},D=new Map;for(let e of this.config.groups){let t=this.buildGroupRuntimeState({group:e,priorState:c.groups[e.id],activeIds:a,seenIds:s,tagsByCard:v,cardsByTag:b,hierarchyConfigs:l,userTagElo:p,userGlobalElo:d});w.push(t),logger.info(`[Prescribed] Group '${e.id}': ${e.targetCardIds.length} targets total, ${t.encounteredTargets.size} encountered, ${t.pendingTargets.length} pending (${t.surfaceableTargets.length} surfaceable, ${t.blockedTargets.length} blocked), ${t.supportCandidates.length} authored support candidates, ${t.discoveredSupportCandidates.length} discovered support candidates, pressure=${t.pressureMultiplier.toFixed(2)}`),t.blockedTargets.length>0&&(logger.info(`[Prescribed] Group '${e.id}' blocked targets: ${t.blockedTargets.join(`, `)}`),logger.info(`[Prescribed] Group '${e.id}' support tags needed: ${t.supportTags.join(`, `)||`(none)`}`),logger.info(`[Prescribed] Group '${e.id}' escalation mode: `+(t.supportCandidates.length>0?`direct-support`:t.discoveredSupportCandidates.length>0?`inserted-support-candidates`:`boost-only`)),t.discoveredSupportCandidates.length>0&&logger.info(`[Prescribed] Group '${e.id}' discovered support candidates: ${t.discoveredSupportCandidates.join(`, `)}`)),x.groups[e.id]=this.buildNextGroupState(t,c.groups[e.id]);let r=this.buildDirectTargetCards(t,n,C),o=this.buildSupportCards(t,n,C),u=this.buildDiscoveredSupportCards(t,n,C),m=this.buildPracticeCards({group:e,courseId:n,emittedIds:C,cardsByTag:b,hierarchyConfigs:l,userTagElo:p,userGlobalElo:d,activeIds:a,seenIds:s,priorPracticeDebt:T,nextPracticeDebt:E});D.set(e.id,m.debts),S.push(...r,...o,...u,...m.cards)}x.practiceDebt=E;let O=this.buildSupportHintSummary(w),k=Object.keys(O.boostTags).length>0?{boostTags:O.boostTags,_label:`prescribed-support (${O.supportTags.length} tags; blocked=${O.blockedTargetIds.length}; testversion=${PRESCRIBED_DEBUG_VERSION})`}:void 0;if(k){let e=Object.entries(k.boostTags??{});logger.info(`[Prescribed] Emitting ${e.length} boost hint(s): `+e.map(([e,t])=>`${e}\xD7${t.toFixed(1)}`).join(`, `))}else logger.info(`[Prescribed] No hints to emit (no blocked targets or no support tags)`);if(this.capturePressureSnapshot(n,w,D,S,k),S.length===0)return logger.info(`[Prescribed] 0 cards emitted (all targets blocked, authored/discovered support candidates exhausted)`+(k?` — boost hints emitted but may not survive filters`:``)),await this.putStrategyState(x).catch(e=>{logger.debug(`[Prescribed] Failed to persist empty-state update: ${e}`)}),k?{cards:[],hints:k}:{cards:[]};let A=pickTopByScore(S,e),j=new Map;for(let e of A){let t=e.provenance[0];if(t?.reason.includes(`mode=practice`))continue;let n=t?.reason.match(/group=([^;]+)/)?.[1],r=t?.reason.includes(`mode=support`)?`supportIds`:`targetIds`;n&&(j.has(n)||j.set(n,{targetIds:[],supportIds:[]}),j.get(n)[r].push(e.cardId))}for(let e of this.config.groups){let t=x.groups[e.id],n=j.get(e.id);n&&(n.targetIds.length>0||n.supportIds.length>0)&&(t.lastSurfacedAt=isoNow(),t.sessionsSinceSurfaced=0,n.supportIds.length>0&&(t.lastSupportAt=isoNow()))}return await this.putStrategyState(x).catch(e=>{logger.debug(`[Prescribed] Failed to persist prescribed progress: ${e}`)}),logger.info(`[Prescribed] Emitting ${A.length} cards (${A.filter(e=>e.provenance[0]?.reason.includes(`mode=target`)).length} target, ${A.filter(e=>e.provenance[0]?.reason.includes(`mode=support`)).length} support, ${A.filter(e=>e.provenance[0]?.reason.includes(`mode=discovered-support`)).length} discovered support)`),k?{cards:A,hints:k}:{cards:A}}buildSupportHintSummary(e){let t={},n=new Set,r=new Set;for(let a of e)if(!(a.blockedTargets.length===0||a.supportTags.length===0)){a.blockedTargets.forEach(e=>n.add(e));for(let e of a.supportTags)r.add(e),t[e]=(t[e]??1)*a.supportMultiplier}return{boostTags:t,blockedTargetIds:[...n].sort(),supportTags:[...r].sort()}}capturePressureSnapshot(e,t,n,r,a){let o=Date.now(),s=[];for(let e of t){let t=e.group,r=t.freshnessWindowSessions??DEFAULT_FRESHNESS_WINDOW;if(s.push({id:`group:${t.id}:target`,label:`${t.id} targets`,multiplier:e.pressureMultiplier,max:MAX_TARGET_MULTIPLIER,detail:`${e.pendingTargets.length} pending (${e.surfaceableTargets.length} surfaceable, ${e.blockedTargets.length} blocked) \xB7 sinceSurfaced ${e.sessionsSinceSurfaced}/${r}`,items:e.blockedTargets.map(e=>({label:e,value:`blocked`}))}),e.blockedTargets.length>0){let n=e.supportCandidates.length>0?`direct-support`:e.discoveredSupportCandidates.length>0?`inserted-support`:`boost-only`;s.push({id:`group:${t.id}:support`,label:`${t.id} support`,multiplier:e.supportMultiplier,max:MAX_SUPPORT_MULTIPLIER,detail:`mode=${n} \xB7 ${e.supportTags.length} support tag(s)`,items:e.supportTags.map(e=>({label:e}))})}let a=n.get(t.id)??[];a.length>0&&s.push({id:`group:${t.id}:practice-debt`,label:`${t.id} practice debt`,multiplier:Math.max(...a.map(e=>e.multiplier)),max:MAX_PRACTICE_MULTIPLIER,detail:`${a.length} under-practiced skill(s)`,items:a.map(e=>({label:e.tag,value:`\xD7${e.multiplier.toFixed(2)} \xB7 open ${formatAge(o-new Date(e.firstOwedAt).getTime())}`}))})}captureStrategyPressure({source:`prescribed`,courseId:e,gauges:s,topScore:r.length>0?Math.max(...r.map(e=>e.score)):null,hintsLabel:a?._label,timestamp:o})}parseConfig(e){try{let t=JSON.parse(e);return{groups:(Array.isArray(t.groups)?t.groups:[]).map((e,t)=>({id:typeof e.id==`string`&&e.id.trim().length>0?e.id:`group-${t+1}`,targetCardIds:dedupe(Array.isArray(e.targetCardIds)?e.targetCardIds.filter(e=>typeof e==`string`):[]),supportCardIds:dedupe(Array.isArray(e.supportCardIds)?e.supportCardIds.filter(e=>typeof e==`string`):[]),supportTagPatterns:dedupe(Array.isArray(e.supportTagPatterns)?e.supportTagPatterns.filter(e=>typeof e==`string`):[]),freshnessWindowSessions:typeof e.freshnessWindowSessions==`number`?e.freshnessWindowSessions:DEFAULT_FRESHNESS_WINDOW,maxDirectTargetsPerRun:typeof e.maxDirectTargetsPerRun==`number`?e.maxDirectTargetsPerRun:DEFAULT_MAX_DIRECT_PER_RUN,maxSupportCardsPerRun:typeof e.maxSupportCardsPerRun==`number`?e.maxSupportCardsPerRun:DEFAULT_MAX_SUPPORT_PER_RUN,introExposeMinCount:typeof e.introExposeMinCount==`number`?e.introExposeMinCount:DEFAULT_MIN_COUNT,hierarchyWalk:{enabled:e.hierarchyWalk?.enabled!==!1,maxDepth:typeof e.hierarchyWalk?.maxDepth==`number`?e.hierarchyWalk.maxDepth:DEFAULT_HIERARCHY_DEPTH},retireOnEncounter:e.retireOnEncounter!==!1,practiceTagPatterns:dedupe(Array.isArray(e.practiceTagPatterns)?e.practiceTagPatterns.filter(e=>typeof e==`string`):[]),practiceMinCount:typeof e.practiceMinCount==`number`?e.practiceMinCount:DEFAULT_PRACTICE_MIN_COUNT,maxPracticeCardsPerRun:typeof e.maxPracticeCardsPerRun==`number`?e.maxPracticeCardsPerRun:DEFAULT_MAX_PRACTICE_PER_RUN})).filter(e=>e.targetCardIds.length>0)}}catch{return{groups:[]}}}async loadHierarchyConfigs(){try{return(await this.course.getAllNavigationStrategies()).filter(e=>e.implementingClass===`hierarchyDefinition`).map(e=>{try{return{prerequisites:JSON.parse(e.serializedData).prerequisites||{}}}catch{return{prerequisites:{}}}})}catch(e){return logger.debug(`[Prescribed] Failed to load hierarchy configs: ${e}`),[]}}buildGroupRuntimeState(e){let{group:t,priorState:n,activeIds:r,seenIds:a,tagsByCard:o,cardsByTag:s,hierarchyConfigs:c,userTagElo:l,userGlobalElo:u}=e,d=new Set;for(let e of t.targetCardIds)(r.has(e)||a.has(e))&&d.add(e);if(n?.encounteredCardIds?.length)for(let e of n.encounteredCardIds)d.add(e);let p=t.targetCardIds.filter(e=>!d.has(e)),m=new Map;for(let e of p)m.set(e,o.get(e)??[]);let g=[],_=[],v=new Set;for(let e of p){let n=m.get(e)??[],r=this.resolveBlockedSupportTags(n,c,l,u,t.hierarchyWalk?.enabled!==!1,t.hierarchyWalk?.maxDepth??DEFAULT_HIERARCHY_DEPTH),a=unmetIntroExposeTags(n,l,t.introExposeMinCount??DEFAULT_MIN_COUNT);a.length>0&&a.forEach(e=>v.add(e)),r.blocked||a.length>0?(g.push(e),r.supportTags.forEach(e=>v.add(e))):_.push(e)}let y=dedupe([...t.supportCardIds??[],...this.findSupportCardsByTags(t,o,[...v])]).filter(e=>!r.has(e)&&!a.has(e)),b=g.length>0&&v.size>0&&y.length===0?this.findDiscoveredSupportCards({supportTags:[...v],cardsByTag:s,activeIds:r,seenIds:a,excludedIds:new Set([...t.targetCardIds,...t.supportCardIds??[]]),limit:t.maxSupportCardsPerRun??DEFAULT_MAX_SUPPORT_PER_RUN}):[];g.length>0&&v.size>0&&b.length===0&&logger.info(`[Prescribed] Group '${t.id}' discovered 0 broader support candidates (blocked=${g.length}; authoredSupport=${y.length})`);let x=n?.sessionsSinceSurfaced??0,S=t.freshnessWindowSessions??DEFAULT_FRESHNESS_WINDOW,C=Math.max(0,x-S),w=p.length===0?1:clamp(1+C*.75+Math.min(2,p.length*.1),1,MAX_TARGET_MULTIPLIER),T=g.length===0?1:clamp(1+C*.5+Math.min(1.5,g.length*.15),1,MAX_SUPPORT_MULTIPLIER);return{group:t,encounteredTargets:d,pendingTargets:p,blockedTargets:g,surfaceableTargets:_,targetTags:m,supportCandidates:y,discoveredSupportCandidates:b,supportTags:[...v],pressureMultiplier:w,supportMultiplier:T,sessionsSinceSurfaced:x,debugVersion:PRESCRIBED_DEBUG_VERSION}}buildNextGroupState(e,t){let n=t?.sessionsSinceSurfaced??0,r=!1;return{encounteredCardIds:[...e.encounteredTargets].sort(),pendingTargetIds:[...e.pendingTargets].sort(),lastSurfacedAt:t?.lastSurfacedAt??null,sessionsSinceSurfaced:n+1,lastSupportAt:t?.lastSupportAt??null,blockedTargetIds:[...e.blockedTargets].sort(),lastResolvedSupportTags:[...e.supportTags].sort()}}buildDirectTargetCards(e,t,n){let r=e.group.maxDirectTargetsPerRun??DEFAULT_MAX_DIRECT_PER_RUN,a=e.surfaceableTargets.filter(e=>!n.has(e)).slice(0,r),o=[];for(let r of a)n.add(r),o.push({cardId:r,courseId:t,score:BASE_TARGET_SCORE*e.pressureMultiplier,provenance:[{strategy:`prescribed`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||`NAVIGATION_STRATEGY-prescribed`,action:`generated`,score:BASE_TARGET_SCORE*e.pressureMultiplier,reason:`mode=target;group=${e.group.id};pending=${e.pendingTargets.length};surfaceable=${e.surfaceableTargets.length};blocked=${e.blockedTargets.length};blockedTargets=${e.blockedTargets.join(`|`)||`none`};supportTags=${e.supportTags.join(`|`)||`none`};multiplier=${e.pressureMultiplier.toFixed(2)};testversion=${e.debugVersion}`}]});return o}buildSupportCards(e,t,n){if(e.blockedTargets.length===0||e.supportCandidates.length===0)return[];let r=e.group.maxSupportCardsPerRun??DEFAULT_MAX_SUPPORT_PER_RUN,a=e.supportCandidates.filter(e=>!n.has(e)).slice(0,r),o=[];for(let r of a)n.add(r),o.push({cardId:r,courseId:t,score:BASE_SUPPORT_SCORE*e.supportMultiplier,provenance:[{strategy:`prescribed`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||`NAVIGATION_STRATEGY-prescribed`,action:`generated`,score:BASE_SUPPORT_SCORE*e.supportMultiplier,reason:`mode=support;group=${e.group.id};pending=${e.pendingTargets.length};blocked=${e.blockedTargets.length};blockedTargets=${e.blockedTargets.join(`|`)||`none`};supportCard=${r};supportTags=${e.supportTags.join(`|`)||`none`};multiplier=${e.supportMultiplier.toFixed(2)};testversion=${e.debugVersion}`}]});return o}buildDiscoveredSupportCards(e,t,n){if(e.blockedTargets.length===0||e.discoveredSupportCandidates.length===0)return[];let r=e.group.maxSupportCardsPerRun??DEFAULT_MAX_SUPPORT_PER_RUN,a=e.discoveredSupportCandidates.filter(e=>!n.has(e)).slice(0,r),o=[];for(let r of a)n.add(r),o.push({cardId:r,courseId:t,score:DISCOVERED_SUPPORT_SCORE*e.supportMultiplier,provenance:[{strategy:`prescribed`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||`NAVIGATION_STRATEGY-prescribed`,action:`generated`,score:DISCOVERED_SUPPORT_SCORE*e.supportMultiplier,reason:`mode=discovered-support;group=${e.group.id};pending=${e.pendingTargets.length};blocked=${e.blockedTargets.length};blockedTargets=${e.blockedTargets.join(`|`)||`none`};supportCard=${r};supportTags=${e.supportTags.join(`|`)||`none`};multiplier=${e.supportMultiplier.toFixed(2)};testversion=${e.debugVersion}`}]});return o}buildPracticeCards(e){let{group:t,courseId:n,emittedIds:r,cardsByTag:a,hierarchyConfigs:o,userTagElo:s,userGlobalElo:c,activeIds:l,seenIds:u,priorPracticeDebt:d,nextPracticeDebt:p}=e,m=t.practiceTagPatterns??[];if(m.length===0)return{cards:[],debts:[]};let g=t.practiceMinCount??DEFAULT_PRACTICE_MIN_COUNT,_=t.maxPracticeCardsPerRun??DEFAULT_MAX_PRACTICE_PER_RUN,v=[...a.keys()].filter(e=>m.some(t=>matchesTagPattern(e,t))&&this.isUnlockedGatedSkill(e,o,s,c)&&(s[e]?.count??0)<g);if(v.length===0)return{cards:[],debts:[]};let y=Date.now(),b=1440*60*1e3,x=new Map;for(let e of v){let t=d[e]??isoNow();p[e]=t;let n=Math.max(0,(y-new Date(t).getTime())/864e5),r=clamp(PRACTICE_BASE_MULT+n*PRACTICE_STALENESS_BUMP_PER_DAY,PRACTICE_BASE_MULT,MAX_PRACTICE_MULTIPLIER);x.set(e,r)}let S=v.map(e=>({tag:e,multiplier:x.get(e)??PRACTICE_BASE_MULT,firstOwedAt:p[e]})).sort((e,t)=>t.multiplier-e.multiplier||e.tag.localeCompare(t.tag)),C=this.findDiscoveredSupportCards({supportTags:v,cardsByTag:a,activeIds:l,seenIds:u,excludedIds:r,limit:_});if(C.length===0)return{cards:[],debts:S};logger.info(`[Prescribed] Group '${t.id}' practice: ${v.length} unlocked under-practiced skill(s), emitting ${C.length} drill card(s)`);let w=[];for(let e of C){r.add(e);let o=PRACTICE_BASE_MULT;for(let t of v)(a.get(t)?.includes(e)??!1)&&(o=Math.max(o,x.get(t)??PRACTICE_BASE_MULT));let s=BASE_PRACTICE_SCORE*o;w.push({cardId:e,courseId:n,score:s,provenance:[{strategy:`prescribed`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||`NAVIGATION_STRATEGY-prescribed`,action:`generated`,score:s,reason:`mode=practice;group=${t.id};debtMult=\xD7${o.toFixed(2)};underPracticedSkills=${v.length};practiceTags=${v.slice(0,8).join(`|`)}${v.length>8?`|…`:``};testversion=${PRESCRIBED_DEBUG_VERSION}`}]})}return{cards:w,debts:S}}isUnlockedGatedSkill(e,t,n,r){let a=t.map(t=>t.prerequisites[e]).filter(e=>Array.isArray(e)&&e.length>0);return a.length===0?!1:a.every(e=>e.every(e=>this.isPrerequisiteMet(e,n[e.tag],r)))}findSupportCardsByTags(e,t,n){if(n.length===0)return[];let r=e.supportCardIds??[],a=e.supportTagPatterns??[];if(r.length===0&&a.length===0)return[];let o=new Set;for(let e of r){let r=t.get(e)??[],s=n.some(e=>r.includes(e)),c=a.some(e=>r.some(t=>matchesTagPattern(t,e)));(s||c)&&o.add(e)}return[...o]}findDiscoveredSupportCards(e){let{supportTags:t,cardsByTag:n,activeIds:r,seenIds:a,excludedIds:o,limit:s}=e,c=new Map;for(let e of t){let t=n.get(e)??[];for(let e of t){if(r.has(e)||a.has(e)||o.has(e))continue;let t=c.get(e);t?t.matches+=1:c.set(e,{cardId:e,matches:1})}}let l=[...c.values()].sort((e,t)=>t.matches-e.matches||e.cardId.localeCompare(t.cardId)),u=new Set,d=[],p=[];for(let e of l){let t=extractWordStem(e.cardId);u.has(t)?p.push(e):(u.add(t),d.push(e))}return shuffleInPlace(d),shuffleInPlace(p),[...d,...p].slice(0,s).map(e=>e.cardId)}resolveBlockedSupportTags(e,t,n,r,a,o){let s=new Set,c=!1;for(let l of e){let e=t.map(e=>e.prerequisites[l]).filter(e=>Array.isArray(e)&&e.length>0);if(e.length!==0&&e.some(e=>e.some(e=>!this.isPrerequisiteMet(e,n[e.tag],r)))){if(c=!0,!a){for(let t of e)for(let e of t)this.isPrerequisiteMet(e,n[e.tag],r)||s.add(e.tag);continue}for(let a of e)for(let e of a)this.isPrerequisiteMet(e,n[e.tag],r)||this.collectSupportTagsRecursive(e.tag,t,n,r,o,new Set,s)}}return{blocked:c,supportTags:[...s]}}collectSupportTagsRecursive(e,t,n,r,a,o,s){if(a<0||o.has(e))return;o.add(e);let c=!1;for(let l of t){let u=l.prerequisites[e];if(!u||u.length===0)continue;let d=u.filter(e=>!this.isPrerequisiteMet(e,n[e.tag],r));if(d.length>0&&a>0){c=!0;for(let e of d)this.collectSupportTagsRecursive(e.tag,t,n,r,a-1,o,s)}}c||s.add(e)}isPrerequisiteMet(e,t,n){if(!t)return!1;let r=e.masteryThreshold?.minCount??DEFAULT_MIN_COUNT;return t.count<r?!1:e.masteryThreshold?.minElo===void 0?e.masteryThreshold?.minCount===void 0?t.score>=n:!0:t.score>=e.masteryThreshold.minElo}}}}),regulators_exports={};__export(regulators_exports,{DEFAULT_REGULATOR_CONFIG:()=>DEFAULT_REGULATOR_CONFIG,INTAKE_CLOCK_KEY:()=>INTAKE_CLOCK_KEY,applyReading:()=>applyReading,frozenMsAt:()=>frozenMsAt,intakeHours:()=>intakeHours,intakeReading:()=>intakeReading,resetIntakeClock:()=>resetIntakeClock,reviewMassReading:()=>reviewMassReading,reviewWeight:()=>reviewWeight,stepIntakeClock:()=>stepIntakeClock});function clamp01(e){return Math.min(1,Math.max(0,e))}function reviewWeight(e,t,n){return e.reviewUrgency!==void 0&&t!==void 0&&t>0?clamp01(e.reviewUrgency*(e.score/t)):clamp01(e.score/n)}function reviewMassReading(e,t,n=DEFAULT_REGULATOR_CONFIG.reviewMass){let r=e.reduce((e,r)=>e+reviewWeight(r,t.get(r.cardId),n.referenceScore),0);return{name:`review-mass`,class:`review`,urgency:r,multiplier:r>n.healthyMass?n.rate**+((r-n.healthyMass)/n.healthyMass):1,applied:e.length,inputs:{due:e.length,mass:r,healthyMass:n.healthyMass}}}function resetIntakeClock(e){return{lastNewAt:new Date(e).toISOString(),frozenMs:0,unservableSince:null}}function frozenMsAt(e,t){let n=e.unservableSince?Math.max(0,t-Date.parse(e.unservableSince)):0;return e.frozenMs+n}function intakeHours(e,t){let n=t-Date.parse(e.lastNewAt);return Math.max(0,n-frozenMsAt(e,t))/36e5}function stepIntakeClock(e,t,n){return n===0&&!e.unservableSince?{...e,unservableSince:new Date(t).toISOString()}:n>0&&e.unservableSince?{...e,frozenMs:frozenMsAt(e,t),unservableSince:null}:e}function intakeReading(e,t,n,r=DEFAULT_REGULATOR_CONFIG.intake){let a=intakeHours(e,t);return{name:`intake`,class:`new`,urgency:a,multiplier:n.eligibleNew>0?r.rate**+(a/r.scaleHours):1,applied:n.eligibleNew,inputs:{hoursSinceNew:a,frozenHours:frozenMsAt(e,t)/36e5,eligibleNew:n.eligibleNew,newCandidates:n.newCandidates}}}function applyReading(e,t){if(t.multiplier===1)return;let n=t.name===`review-mass`?`review mass ${t.urgency.toFixed(1)} (healthy ${t.inputs.healthyMass}) \u2192 \xD7${t.multiplier.toFixed(2)}`:`${t.urgency.toFixed(1)}h since a new card \u2192 \xD7${t.multiplier.toFixed(2)}`;for(let r of e)r.score*=t.multiplier,r.provenance.push({strategy:`regulator`,strategyName:`Regulator: ${REGULATOR_LABEL[t.name]}`,strategyId:`regulator-${t.name}`,action:t.multiplier>1?`boosted`:`penalized`,score:r.score,reason:n})}var DEFAULT_REGULATOR_CONFIG,INTAKE_CLOCK_KEY,REGULATOR_LABEL,init_regulators=__esm({"src/core/navigators/regulators.ts"(){DEFAULT_REGULATOR_CONFIG={reviewMass:{referenceScore:.95,healthyMass:20,rate:2},intake:{rate:2,scaleHours:24}},INTAKE_CLOCK_KEY=`IntakeClock`,REGULATOR_LABEL={"review-mass":`Review mass`,intake:`Intake`}}}),srs_exports={};__export(srs_exports,{default:()=>SRSNavigator});var SRSNavigator,init_srs=__esm({"src/core/navigators/generators/srs.ts"(){init_navigators(),init_SrsDebugger(),init_regulators(),init_logger(),SRSNavigator=class extends ContentNavigator{constructor(e,t,n){super(e,t,n),_defineProperty(this,`name`,void 0),this.name=n?.name||`SRS`}async getWeightedCards(e,t){if(!this.user||!this.course)throw Error(`SRSNavigator requires user and course to be set`);let n=this.course.getCourseID(),r=await this.user.getPendingReviews(n),a=hooks.utc(),o=r.filter(e=>a.isAfter(hooks.utc(e.reviewTime)));if(o.length>0){let e=[...new Set(o.map(e=>e.cardId))],t=await this.course.getAppliedTagsBatch(e),n=[];if(o=o.filter(e=>(t.get(e.cardId)??[]).includes(`srs:skip`)?(n.push(e._id),!1):!0),n.length>0){logger.info(`[SRS] Removing ${n.length} scheduled reviews for srs:skip cards`);for(let e of n)this.user.removeScheduledCardReview(e)}}let s=r.filter(e=>!a.isAfter(hooks.utc(e.reviewTime))),c=null;if(s.length>0){let e=s.reduce((e,t)=>hooks.utc(e.reviewTime).isBefore(hooks.utc(t.reviewTime))?e:t),t=hooks.duration(hooks.utc(e.reviewTime).diff(a));c=t.asHours()<1?`${Math.round(t.asMinutes())}m`:t.asHours()<24?`${Math.round(t.asHours())}h`:`${Math.round(t.asDays())}d`}if(o.length>0)logger.info(`[SRS] Course ${n}: ${o.length} reviews due now (of ${r.length} scheduled)`);else if(r.length>0){let e=[...r].sort((e,t)=>hooks.utc(e.reviewTime).diff(hooks.utc(t.reviewTime)))[0],t=hooks.utc(e.reviewTime),o=hooks.duration(t.diff(a)),s=o.asHours()<1?`${Math.round(o.asMinutes())}m`:o.asHours()<24?`${Math.round(o.asHours())}h`:`${Math.round(o.asDays())}d`;logger.info(`[SRS] Course ${n}: 0 reviews due now (${r.length} scheduled, next in ${s})`)}else logger.info(`[SRS] Course ${n}: No reviews scheduled`);let l=o.map(e=>{let{score:t,urgency:n,reason:r}=this.computeUrgencyScore(e,a);return{cardId:e.cardId,courseId:e.courseId,score:t,reviewUrgency:n,reviewID:e._id,provenance:[{strategy:`srs`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||`NAVIGATION_STRATEGY-SRS-default`,action:`generated`,score:t,reason:r}]}}).sort((e,t)=>t.score-e.score);return captureSrsBacklog({courseId:n,scheduledTotal:r.length,dueNow:o.length,healthyBacklog:DEFAULT_REGULATOR_CONFIG.reviewMass.healthyMass,reviewMass:null,backlogMultiplier:1,backlogGrowthRate:DEFAULT_REGULATOR_CONFIG.reviewMass.rate,topReviewScore:l.length>0?l[0].score:null,nextDueIn:c,timestamp:Date.now()}),{cards:l.slice(0,e)}}computeUrgencyScore(e,t){let n=hooks.utc(e.scheduledAt),r=hooks.utc(e.reviewTime),a=Math.max(1,r.diff(n,`hours`)),o=t.diff(r,`hours`),s=o/a,c=.3+.7*Math.exp(-a/720),l=Math.min(1,Math.max(0,s))*.5+c*.5,u=.5+l*.45,d=[`${Math.round(o)}h overdue`,`interval: ${Math.round(a)}h`,`relative: ${s.toFixed(2)}`,`recency: ${c.toFixed(2)}`];return d.push(`review`),{score:u,urgency:l,reason:d.join(`, `)}}}}}),types_exports={},init_types=__esm({"src/core/navigators/generators/types.ts"(){}}),globImport_generators,init_=__esm({'import("./generators/**/*") in src/core/navigators/index.ts'(){globImport_generators=__glob({"./generators/CompositeGenerator.ts":()=>Promise.resolve().then(()=>(init_CompositeGenerator(),CompositeGenerator_exports)),"./generators/elo.ts":()=>Promise.resolve().then(()=>(init_elo(),elo_exports)),"./generators/index.ts":()=>Promise.resolve().then(()=>(init_generators(),generators_exports)),"./generators/prescribed.ts":()=>Promise.resolve().then(()=>(init_prescribed(),prescribed_exports)),"./generators/srs.ts":()=>Promise.resolve().then(()=>(init_srs(),srs_exports)),"./generators/types.ts":()=>Promise.resolve().then(()=>(init_types(),types_exports))})}}),DEFAULT_LEARNABLE_WEIGHT,init_contentNavigationStrategy=__esm({"src/core/types/contentNavigationStrategy.ts"(){DEFAULT_LEARNABLE_WEIGHT={weight:1,confidence:.1,sampleSize:0}}}),WeightedFilter_exports={};__export(WeightedFilter_exports,{WeightedFilter:()=>WeightedFilter});var WeightedFilter,init_WeightedFilter=__esm({"src/core/navigators/filters/WeightedFilter.ts"(){init_contentNavigationStrategy(),WeightedFilter=class{constructor(e,t=DEFAULT_LEARNABLE_WEIGHT,n=!1,r){_defineProperty(this,`name`,void 0),_defineProperty(this,`kind`,void 0),_defineProperty(this,`liveOnly`,void 0),_defineProperty(this,`inner`,void 0),_defineProperty(this,`learnable`,void 0),_defineProperty(this,`staticWeight`,void 0),_defineProperty(this,`strategyId`,void 0),this.inner=e,this.name=e.name,this.kind=e.kind,this.liveOnly=e.liveOnly,this.learnable=t,this.staticWeight=n,this.strategyId=r}async transform(e,t){let n=this.learnable.weight,r;if(!this.staticWeight&&t.orchestration){let e=this.strategyId||this.inner.strategyId||this.name;n=t.orchestration.getEffectiveWeight(e,this.learnable),r=t.orchestration.getDeviation(e)}if(Math.abs(n-1)<.001)return this.inner.transform(e,t);let a=new Map;for(let t of e)a.set(t.cardId,t.score);return(await this.inner.transform(e,t)).map(e=>{let t=a.get(e.cardId);if(t===void 0||t===0||e.score===0)return e;let o=e.score/t;if(Math.abs(o-1)<1e-4)return e;let s=t*o**+n,c=e.provenance.length-1,l=e.provenance[c];if(l){let t=[...e.provenance];return t[c]={...l,score:s,effectiveWeight:n,deviation:r},{...e,score:s,provenance:t}}return{...e,score:s}})}}}}),eloDistance_exports={};__export(eloDistance_exports,{DEFAULT_ELO_DISTANCE_CONFIG:()=>DEFAULT_ELO_DISTANCE_CONFIG,createEloDistanceFilter:()=>createEloDistanceFilter,default:()=>EloDistanceFilter,tagAwareGap:()=>tagAwareGap});function computeMultiplier(e,t,n,r){let a=e/t,o=Math.exp(-(a*a));return n+(r-n)*o}function resolveConfig(e){return{...DEFAULT_ELO_DISTANCE_CONFIG,...e}}function tagAwareGap(e,t,n,r){let a=t.global.score-n.global.score,o=[];for(let a of e){let e=n.tags[a];if(!e||e.score===COUNT_ONLY_SENTINEL||e.count<r)continue;let s=t.tags[a]?.score??t.global.score;o.push({tag:a,gap:s-e.score,weight:e.count/(e.count+TAG_CONFIDENCE_HALF)})}if(o.length===0)return{gap:a,globalGap:a,tags:o};let s=o.reduce((e,t)=>e+t.weight,0),c=o.reduce((e,t)=>e+t.weight*t.gap,0)/s,l=s/(s+1);return{gap:l*c+(1-l)*a,globalGap:a,tags:o}}function tagReason(e,t){let n=[...e].sort((e,t)=>Math.abs(t.gap)*t.weight-Math.abs(e.gap)*e.weight).slice(0,3).map(e=>`${e.tag} ${e.gap>=0?`+`:``}${Math.round(e.gap)}`).join(`, `),r=e.length>3?`, +${e.length-3} more`:``;return` on ${e.length} shared tag(s) [${n}${r}] (global ${t>=0?`+`:``}${Math.round(t)})`}async function applyEloDistance(e,t,n,r){let{course:a,userElo:o,userCourseElo:s}=t,{halfLife:c,minMultiplier:l,maxMultiplier:u}=n,d=n.tagAware&&s!==void 0,p=e.filter(e=>d||e.cardElo===void 0).map(e=>e.cardId),m=new Map;if(p.length>0){let e=await a.getCardEloData(p);p.forEach((t,n)=>{e[n]&&m.set(t,e[n])})}return e.map(e=>{let t=m.get(e.cardId),a=t?.global?.score??e.cardElo??DEFAULT_CARD_ELO,p=Math.abs(a-o),g=``;if(d&&t){let r=tagAwareGap(e.tags??[],t,s,n.minTagCount);p=Math.abs(r.gap),r.tags.length>0&&(g=tagReason(r.tags,r.globalGap))}let _=computeMultiplier(p,c,l,u),v=e.score*_,y=_<u-.01?`penalized`:`passed`;return{...e,cardElo:a,score:v,provenance:[...e.provenance,{strategy:`eloDistance`,strategyName:r.name,strategyId:r.strategyId,action:y,score:v,reason:`ELO distance ${Math.round(p)} (card: ${Math.round(a)}, user: ${Math.round(o)})${g} \u2192 ${_.toFixed(2)}x`}]}})}function createEloDistanceFilter(e){let t=resolveConfig(e),n={name:`ELO Distance Filter`,strategyId:`ELO_DISTANCE_FILTER`};return{name:n.name,kind:`signal`,liveOnly:!0,transform:(e,r)=>applyEloDistance(e,r,t,n)}}var DEFAULT_ELO_DISTANCE_CONFIG,TAG_CONFIDENCE_HALF,COUNT_ONLY_SENTINEL,DEFAULT_CARD_ELO,EloDistanceFilter,init_eloDistance=__esm({"src/core/navigators/filters/eloDistance.ts"(){var e;init_navigators(),DEFAULT_ELO_DISTANCE_CONFIG={halfLife:300,minMultiplier:.05,maxMultiplier:1,tagAware:!0,minTagCount:3},TAG_CONFIDENCE_HALF=10,COUNT_ONLY_SENTINEL=-1,DEFAULT_CARD_ELO=1e3,EloDistanceFilter=(e=class _EloDistanceFilter extends ContentNavigator{constructor(e,t,n){super(e,t,n),_defineProperty(this,`name`,void 0),_defineProperty(this,`kind`,_EloDistanceFilter.kind),_defineProperty(this,`liveOnly`,!0),_defineProperty(this,`config`,void 0),this.name=n.name||`ELO Distance`;let r;try{r=n.serializedData?JSON.parse(n.serializedData):void 0}catch{r=void 0}this.config=resolveConfig(r)}transform(e,t){return applyEloDistance(e,t,this.config,{name:this.name,strategyId:this.strategyId||`NAVIGATION_STRATEGY-eloDistance-default`})}async getWeightedCards(e){throw Error(`EloDistanceFilter is a filter and should not be used as a generator. Use Pipeline with a generator and this filter via transform().`)}},_defineProperty(e,`kind`,`signal`),e)}}),hierarchyDefinition_exports={};__export(hierarchyDefinition_exports,{default:()=>HierarchyDefinitionNavigator});var DEFAULT_MIN_COUNT2,HierarchyDefinitionNavigator,init_hierarchyDefinition=__esm({"src/core/navigators/filters/hierarchyDefinition.ts"(){var e;init_navigators(),init_logger(),DEFAULT_MIN_COUNT2=3,HierarchyDefinitionNavigator=(e=class _HierarchyDefinitionNavigator extends ContentNavigator{constructor(e,t,n){super(e,t,n),_defineProperty(this,`config`,void 0),_defineProperty(this,`name`,void 0),_defineProperty(this,`kind`,_HierarchyDefinitionNavigator.kind),this.config=this.parseConfig(n.serializedData),this.name=n.name||`Hierarchy Definition`}parseConfig(e){try{return{prerequisites:JSON.parse(e).prerequisites||{}}}catch{return{prerequisites:{}}}}isPrerequisiteMet(e,t,n){if(!t)return!1;let r=e.masteryThreshold?.minCount??DEFAULT_MIN_COUNT2;return t.count<r?!1:e.masteryThreshold?.minElo===void 0?e.masteryThreshold?.minCount===void 0?t.score>=n:!0:t.score>=e.masteryThreshold.minElo}async getMasteredTags(e){let t=new Set;try{let n=toCourseElo((await e.user.getCourseRegDoc(e.course.getCourseID())).elo);for(let e of Object.values(this.config.prerequisites))for(let r of e){let e=n.tags[r.tag];this.isPrerequisiteMet(r,e,n.global.score)&&t.add(r.tag)}}catch{}return t}getUnlockedTags(e){let t=new Set;for(let[n,r]of Object.entries(this.config.prerequisites))r.every(t=>e.has(t.tag))&&t.add(n);return t}hasPrerequisites(e){return e in this.config.prerequisites}async checkCardUnlock(e,t,n,r){try{let t=e.tags??[],a=t.filter(e=>this.hasPrerequisites(e)&&!n.has(e));return a.length===0?{isUnlocked:!0,reason:`Prerequisites met, tags: ${t.length>0?t.join(`, `):`none`}`}:{isUnlocked:!1,reason:`Blocked: missing prerequisites ${a.flatMap(e=>(this.config.prerequisites[e]||[]).filter(e=>!r.has(e.tag)).map(e=>e.tag)).join(`, `)} for tags ${a.join(`, `)}`}}catch{return{isUnlocked:!0,reason:`Prerequisites check skipped (tag lookup failed)`}}}getPreReqBoosts(e,t){let n=new Map;for(let[r,a]of Object.entries(this.config.prerequisites))if(!e.has(r))for(let e of a){if(!e.preReqBoost||e.preReqBoost<=1||t.has(e.tag))continue;let r=n.get(e.tag)??1;n.set(e.tag,Math.max(r,e.preReqBoost))}return n}getTargetBoosts(e){let t=new Map,n=Object.keys(this.config.prerequisites),r=[...e];logger.info(`[HierarchyDefinition:targetBoost:trace] ${this.name} | configKeys=${n.length}, unlocked=${r.length} (${r.slice(0,5).join(`, `)}${r.length>5?`...`:``})`);for(let[n,r]of Object.entries(this.config.prerequisites))if(e.has(n)){logger.info(`[HierarchyDefinition:targetBoost:trace] UNLOCKED ${n}: ${r.length} prereqs, raw=${JSON.stringify(r.map(e=>({tag:e.tag,tb:e.targetBoost})))}`);for(let e of r){if(!e.targetBoost||e.targetBoost<=1)continue;let r=t.get(n)??1;t.set(n,Math.max(r,e.targetBoost))}}return t.size>0?logger.info(`[HierarchyDefinition] targetBoosts active: ${[...t.entries()].map(([e,t])=>`${e}=\xD7${t}`).join(`, `)}`):logger.info(`[HierarchyDefinition:targetBoost:trace] no targetBoosts found despite ${r.length} unlocked tags`),t}async transform(e,t){let n=await this.getMasteredTags(t),r=this.getUnlockedTags(n),a=this.getPreReqBoosts(r,n),o=this.getTargetBoosts(r),s=[];for(let c of e){let{isUnlocked:e,reason:l}=await this.checkCardUnlock(c,t.course,r,n),u=e?c.score:c.score*.02,d=e?`passed`:`penalized`,p=l;if(e&&a.size>0){let e=c.tags??[],t=1,n=[];for(let r of e){let e=a.get(r);e&&e>t&&(t=e,n.push(r))}t>1&&(u*=t,d=`boosted`,p=`${l} | preReqBoost \xD7${t.toFixed(2)} for ${n.join(`, `)}`,logger.info(`[HierarchyDefinition] preReqBoost \xD7${t.toFixed(2)} applied to card ${c.cardId} via tags [${n.join(`, `)}] (score: ${c.score.toFixed(3)} \u2192 ${u.toFixed(3)})`))}if(e&&o.size>0){let e=c.tags??[],t=1,n=[];for(let r of e){let e=o.get(r);e&&e>t&&(t=e,n.push(r))}t>1&&(u*=t,d=`boosted`,p=`${p} | targetBoost \xD7${t.toFixed(2)} for ${n.join(`, `)}`,logger.info(`[HierarchyDefinition] targetBoost \xD7${t.toFixed(2)} applied to card ${c.cardId} via tags [${n.join(`, `)}] (score: ${c.score.toFixed(3)} \u2192 ${u.toFixed(3)})`))}s.push({...c,score:u,provenance:[...c.provenance,{strategy:`hierarchyDefinition`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||`NAVIGATION_STRATEGY-hierarchy`,action:d,score:u,reason:p}]})}return s}async getWeightedCards(e){throw Error(`HierarchyDefinitionNavigator is a filter and should not be used as a generator. Use Pipeline with a generator and this filter via transform().`)}},_defineProperty(e,`kind`,`gate`),e)}}),userTagPreference_exports={};__export(userTagPreference_exports,{default:()=>UserTagPreferenceFilter});var UserTagPreferenceFilter,init_userTagPreference=__esm({"src/core/navigators/filters/userTagPreference.ts"(){var e;init_navigators(),UserTagPreferenceFilter=(e=class _UserTagPreferenceFilter extends ContentNavigator{constructor(e,t,n){super(e,t,n),_defineProperty(this,`_strategyData`,void 0),_defineProperty(this,`name`,void 0),_defineProperty(this,`kind`,_UserTagPreferenceFilter.kind),this._strategyData=n,this.name=n.name||`User Tag Preferences`}computeMultiplier(e,t){let n=e.map(e=>t[e]).filter(e=>e!==void 0);return n.length===0?1:Math.max(...n)}buildReason(e,t,n){let r=e.filter(e=>t[e]===n);return n===0?`Excluded by user preference: ${r.join(`, `)} (${n}x)`:n<1?`Penalized by user preference: ${r.join(`, `)} (${n.toFixed(2)}x)`:n>1?`Boosted by user preference: ${r.join(`, `)} (${n.toFixed(2)}x)`:`No matching user preferences`}async transform(e,t){let n=await this.getStrategyState();return!n||Object.keys(n.boost).length===0?e.map(e=>({...e,provenance:[...e.provenance,{strategy:`userTagPreference`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||this._strategyData._id,action:`passed`,score:e.score,reason:`No user tag preferences configured`}]})):await Promise.all(e.map(async e=>{let t=e.tags??[],r=this.computeMultiplier(t,n.boost),a=Math.min(1,e.score*r),o;return o=r===0||r<1?`penalized`:r>1?`boosted`:`passed`,{...e,score:a,provenance:[...e.provenance,{strategy:`userTagPreference`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||this._strategyData._id,action:o,score:a,reason:this.buildReason(t,n.boost,r)}]}}))}async getWeightedCards(e){throw Error(`UserTagPreferenceFilter is a filter and should not be used as a generator. Use Pipeline with a generator and this filter via transform().`)}},_defineProperty(e,`kind`,`signal`),e)}}),filters_exports={};__export(filters_exports,{EloDistanceFilter:()=>EloDistanceFilter,UserTagPreferenceFilter:()=>UserTagPreferenceFilter,createEloDistanceFilter:()=>createEloDistanceFilter});var init_filters=__esm({"src/core/navigators/filters/index.ts"(){init_eloDistance(),init_userTagPreference()}}),inferredPreferenceStub_exports={};__export(inferredPreferenceStub_exports,{INFERRED_PREFERENCE_NAVIGATOR_STUB:()=>INFERRED_PREFERENCE_NAVIGATOR_STUB});var INFERRED_PREFERENCE_NAVIGATOR_STUB,init_inferredPreferenceStub=__esm({"src/core/navigators/filters/inferredPreferenceStub.ts"(){INFERRED_PREFERENCE_NAVIGATOR_STUB=!0}}),interferenceMitigator_exports={};__export(interferenceMitigator_exports,{default:()=>InterferenceMitigatorNavigator});var DEFAULT_MIN_COUNT3,DEFAULT_MIN_ELAPSED_DAYS,DEFAULT_INTERFERENCE_DECAY,InterferenceMitigatorNavigator,init_interferenceMitigator=__esm({"src/core/navigators/filters/interferenceMitigator.ts"(){var e;init_navigators(),DEFAULT_MIN_COUNT3=10,DEFAULT_MIN_ELAPSED_DAYS=3,DEFAULT_INTERFERENCE_DECAY=.8,InterferenceMitigatorNavigator=(e=class _InterferenceMitigatorNavigator extends ContentNavigator{constructor(e,t,n){super(e,t,n),_defineProperty(this,`config`,void 0),_defineProperty(this,`name`,void 0),_defineProperty(this,`kind`,_InterferenceMitigatorNavigator.kind),_defineProperty(this,`interferenceMap`,void 0),this.config=this.parseConfig(n.serializedData),this.interferenceMap=this.buildInterferenceMap(),this.name=n.name||`Interference Mitigator`}parseConfig(e){try{let t=JSON.parse(e),n=t.interferenceSets||[];return n.length>0&&Array.isArray(n[0])&&(n=n.map(e=>({tags:e}))),{interferenceSets:n,maturityThreshold:{minCount:t.maturityThreshold?.minCount??DEFAULT_MIN_COUNT3,minElo:t.maturityThreshold?.minElo,minElapsedDays:t.maturityThreshold?.minElapsedDays??DEFAULT_MIN_ELAPSED_DAYS},defaultDecay:t.defaultDecay??DEFAULT_INTERFERENCE_DECAY}}catch{return{interferenceSets:[],maturityThreshold:{minCount:DEFAULT_MIN_COUNT3,minElapsedDays:DEFAULT_MIN_ELAPSED_DAYS},defaultDecay:DEFAULT_INTERFERENCE_DECAY}}}buildInterferenceMap(){let e=new Map;for(let t of this.config.interferenceSets){let n=t.decay??this.config.defaultDecay??DEFAULT_INTERFERENCE_DECAY;for(let r of t.tags){e.has(r)||e.set(r,[]);let a=e.get(r);for(let e of t.tags)if(e!==r){let t=a.find(t=>t.partner===e);t?t.decay=Math.max(t.decay,n):a.push({partner:e,decay:n})}}}return e}async getImmatureTags(e){let t=new Set;try{let n=toCourseElo((await e.user.getCourseRegDoc(e.course.getCourseID())).elo),r=this.config.maturityThreshold?.minCount??DEFAULT_MIN_COUNT3,a=this.config.maturityThreshold?.minElo,o=(this.config.maturityThreshold?.minElapsedDays??DEFAULT_MIN_ELAPSED_DAYS)*2;for(let[e,s]of Object.entries(n.tags)){if(s.count===0)continue;let n=s.count<r,c=a!==void 0&&s.score<a,l=s.count<o;(n||c||l)&&t.add(e)}}catch{}return t}getTagsToAvoid(e){let t=new Map;for(let n of e){let r=this.interferenceMap.get(n);if(r){for(let{partner:n,decay:a}of r)if(!e.has(n)){let e=t.get(n)??0;t.set(n,Math.max(e,a))}}}return t}computeInterferenceEffect(e,t,n){if(t.size===0)return{multiplier:1,interferingTags:[],reason:`No interference detected`};let r=1,a=[];for(let n of e){let e=t.get(n);e!==void 0&&(a.push(n),r*=1-e)}if(a.length===0)return{multiplier:1,interferingTags:[],reason:`No interference detected`};let o=new Set;for(let e of a)for(let t of n)this.interferenceMap.get(t)?.some(t=>t.partner===e)&&o.add(t);let s=`Interferes with immature tags ${Array.from(o).join(`, `)} (tags: ${a.join(`, `)}, multiplier: ${r.toFixed(2)})`;return{multiplier:r,interferingTags:a,reason:s}}async transform(e,t){let n=await this.getImmatureTags(t),r=this.getTagsToAvoid(n),a=[];for(let t of e){let e=t.tags??[],{multiplier:o,reason:s}=this.computeInterferenceEffect(e,r,n),c=t.score*o,l=o<1?`penalized`:o>1?`boosted`:`passed`;a.push({...t,score:c,provenance:[...t.provenance,{strategy:`interferenceMitigator`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||`NAVIGATION_STRATEGY-interference`,action:l,score:c,reason:s}]})}return a}async getWeightedCards(e){throw Error(`InterferenceMitigatorNavigator is a filter and should not be used as a generator. Use Pipeline with a generator and this filter via transform().`)}},_defineProperty(e,`kind`,`signal`),e)}}),relativePriority_exports={};__export(relativePriority_exports,{default:()=>RelativePriorityNavigator});var DEFAULT_PRIORITY,DEFAULT_PRIORITY_INFLUENCE,DEFAULT_COMBINE_MODE,RelativePriorityNavigator,init_relativePriority=__esm({"src/core/navigators/filters/relativePriority.ts"(){var e;init_navigators(),DEFAULT_PRIORITY=.5,DEFAULT_PRIORITY_INFLUENCE=.5,DEFAULT_COMBINE_MODE=`max`,RelativePriorityNavigator=(e=class _RelativePriorityNavigator extends ContentNavigator{constructor(e,t,n){super(e,t,n),_defineProperty(this,`config`,void 0),_defineProperty(this,`name`,void 0),_defineProperty(this,`kind`,_RelativePriorityNavigator.kind),this.config=this.parseConfig(n.serializedData),this.name=n.name||`Relative Priority`}parseConfig(e){try{let t=JSON.parse(e);return{tagPriorities:t.tagPriorities||{},defaultPriority:t.defaultPriority??DEFAULT_PRIORITY,combineMode:t.combineMode??DEFAULT_COMBINE_MODE,priorityInfluence:t.priorityInfluence??DEFAULT_PRIORITY_INFLUENCE}}catch{return{tagPriorities:{},defaultPriority:DEFAULT_PRIORITY,combineMode:DEFAULT_COMBINE_MODE,priorityInfluence:DEFAULT_PRIORITY_INFLUENCE}}}getTagPriority(e){return this.config.tagPriorities[e]??this.config.defaultPriority??DEFAULT_PRIORITY}computeCardPriority(e){if(e.length===0)return this.config.defaultPriority??DEFAULT_PRIORITY;let t=e.map(e=>this.getTagPriority(e));switch(this.config.combineMode){case`max`:return Math.max(...t);case`min`:return Math.min(...t);case`average`:return t.reduce((e,t)=>e+t,0)/t.length;default:return Math.max(...t)}}computeBoostFactor(e){let t=this.config.priorityInfluence??DEFAULT_PRIORITY_INFLUENCE;return 1+(e-.5)*t}buildPriorityReason(e,t,n,r){if(e.length===0)return`No tags, neutral priority (${t.toFixed(2)})`;let a=e.slice(0,3).join(`, `),o=e.length>3?` (+${e.length-3} more)`:``;return n===1?`Neutral priority (${t.toFixed(2)}) for tags: ${a}${o}`:n>1?`High-priority tags: ${a}${o} (priority ${t.toFixed(2)} \u2192 boost ${n.toFixed(2)}x \u2192 ${r.toFixed(2)})`:`Low-priority tags: ${a}${o} (priority ${t.toFixed(2)} \u2192 reduce ${n.toFixed(2)}x \u2192 ${r.toFixed(2)})`}async transform(e,t){return await Promise.all(e.map(async e=>{let t=e.tags??[],n=this.computeCardPriority(t),r=this.computeBoostFactor(n),a=Math.max(0,e.score*r),o=r>1?`boosted`:r<1?`penalized`:`passed`,s=this.buildPriorityReason(t,n,r,a);return{...e,score:a,provenance:[...e.provenance,{strategy:`relativePriority`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||`NAVIGATION_STRATEGY-priority`,action:o,score:a,reason:s}]}}))}async getWeightedCards(e){throw Error(`RelativePriorityNavigator is a filter and should not be used as a generator. Use Pipeline with a generator and this filter via transform().`)}},_defineProperty(e,`kind`,`signal`),e)}}),types_exports2={},init_types2=__esm({"src/core/navigators/filters/types.ts"(){}}),userGoalStub_exports={};__export(userGoalStub_exports,{USER_GOAL_NAVIGATOR_STUB:()=>USER_GOAL_NAVIGATOR_STUB});var USER_GOAL_NAVIGATOR_STUB,init_userGoalStub=__esm({"src/core/navigators/filters/userGoalStub.ts"(){USER_GOAL_NAVIGATOR_STUB=!0}}),globImport_filters,init_2=__esm({'import("./filters/**/*") in src/core/navigators/index.ts'(){globImport_filters=__glob({"./filters/WeightedFilter.ts":()=>Promise.resolve().then(()=>(init_WeightedFilter(),WeightedFilter_exports)),"./filters/eloDistance.ts":()=>Promise.resolve().then(()=>(init_eloDistance(),eloDistance_exports)),"./filters/hierarchyDefinition.ts":()=>Promise.resolve().then(()=>(init_hierarchyDefinition(),hierarchyDefinition_exports)),"./filters/index.ts":()=>Promise.resolve().then(()=>(init_filters(),filters_exports)),"./filters/inferredPreferenceStub.ts":()=>Promise.resolve().then(()=>(init_inferredPreferenceStub(),inferredPreferenceStub_exports)),"./filters/interferenceMitigator.ts":()=>Promise.resolve().then(()=>(init_interferenceMitigator(),interferenceMitigator_exports)),"./filters/relativePriority.ts":()=>Promise.resolve().then(()=>(init_relativePriority(),relativePriority_exports)),"./filters/types.ts":()=>Promise.resolve().then(()=>(init_types2(),types_exports2)),"./filters/userGoalStub.ts":()=>Promise.resolve().then(()=>(init_userGoalStub(),userGoalStub_exports)),"./filters/userTagPreference.ts":()=>Promise.resolve().then(()=>(init_userTagPreference(),userTagPreference_exports))})}});function aggregateOutcomesForGradient(e,t){let n=[];for(let r of e){let e=r.deviations[t];e!==void 0&&n.push({deviation:e,outcomeValue:r.outcomeValue,weight:1})}return logger.debug(`[Orchestration] Aggregated ${n.length} observations for strategy ${t}`),n}function computeStrategyGradient(e){let t=e.length;if(t<3)return logger.debug(`[Orchestration] Insufficient observations for gradient (${t} < 3)`),null;let n=0,r=0,a=0;for(let t of e){let e=t.weight??1;n+=t.deviation*e,r+=t.outcomeValue*e,a+=e}let o=n/a,s=r/a,c=0,l=0,u=0;for(let t of e){let e=t.weight??1,n=t.deviation-o,r=t.outcomeValue-s;c+=e*n*r,l+=e*n*n,u+=e*r*r}if(l<1e-10)return logger.debug(`[Orchestration] No variance in deviations, cannot compute gradient`),{gradient:0,intercept:s,rSquared:0,sampleSize:t};let d=c/l,p=s-d*o,m=0;for(let t of e){let e=t.weight??1,n=d*t.deviation+p,r=t.outcomeValue-n;m+=e*r*r}let g=u>1e-10?1-m/u:0;return logger.debug(`[Orchestration] Computed gradient: ${d.toFixed(4)}, intercept: ${p.toFixed(4)}, R\xB2: ${g.toFixed(4)}, n=${t}`),{gradient:d,intercept:p,rSquared:Math.max(0,Math.min(1,g)),sampleSize:t}}var init_gradient=__esm({"src/core/orchestration/gradient.ts"(){init_logger()}});function updateStrategyWeight(e,t){if(t.sampleSize<MIN_OBSERVATIONS_FOR_UPDATE)return logger.debug(`[Orchestration] Insufficient samples (${t.sampleSize} < ${MIN_OBSERVATIONS_FOR_UPDATE}), keeping current weight`),{...e,sampleSize:e.sampleSize+t.sampleSize};let n=t.rSquared>=MIN_R_SQUARED_FOR_GRADIENT,r=Math.abs(t.gradient)<FLAT_GRADIENT_THRESHOLD,a=e.weight,o=e.confidence;if(!n||r){let n=.05*(1-e.confidence);o=Math.min(1,e.confidence+n),logger.debug(`[Orchestration] Flat/unreliable gradient (|g|=${Math.abs(t.gradient).toFixed(4)}, R\xB2=${t.rSquared.toFixed(4)}). Increasing confidence: ${e.confidence.toFixed(3)} \u2192 ${o.toFixed(3)}`)}else{let n=t.gradient*LEARNING_RATE;n=Math.max(-MAX_WEIGHT_DELTA,Math.min(MAX_WEIGHT_DELTA,n)),a=e.weight+n,a=Math.max(.1,Math.min(3,a));let r=.02*(1-e.confidence);o=Math.min(1,e.confidence+r),logger.debug(`[Orchestration] Adjusting weight: ${e.weight.toFixed(3)} \u2192 ${a.toFixed(3)} (gradient=${t.gradient.toFixed(4)}, delta=${n.toFixed(4)})`)}return{weight:a,confidence:o,sampleSize:e.sampleSize+t.sampleSize}}function updateLearningState(e,t,n,r,a){let o=new Date().toISOString(),s=`STRATEGY_LEARNING_STATE::${e}::${t}`,c={timestamp:o,weight:n.weight,confidence:n.confidence,gradient:r.gradient},l=a?.history??[];return l=[...l,c],l.length>MAX_HISTORY_LENGTH&&(l=l.slice(l.length-MAX_HISTORY_LENGTH)),{_id:s,_rev:a?._rev,docType:`STRATEGY_LEARNING_STATE`,courseId:e,strategyId:t,currentWeight:n,regression:{gradient:r.gradient,intercept:r.intercept,rSquared:r.rSquared,sampleSize:r.sampleSize,computedAt:o},history:l,updatedAt:o}}function runPeriodUpdate(e){let{courseId:t,strategyId:n,currentWeight:r,gradient:a,existingState:o}=e;logger.info(`[Orchestration] Running period update for strategy ${n} (${a.sampleSize} observations)`);let s=updateStrategyWeight(r,a),c=s.weight!==r.weight,l=updateLearningState(t,n,s,a,o);return logger.info(`[Orchestration] Period update complete for ${n}: weight ${r.weight.toFixed(3)} \u2192 ${s.weight.toFixed(3)}, confidence ${r.confidence.toFixed(3)} \u2192 ${s.confidence.toFixed(3)}`),{strategyId:n,previousWeight:r,newWeight:s,gradient:a,learningState:l,updated:c}}function getDefaultLearnableWeight(){return{...DEFAULT_LEARNABLE_WEIGHT}}var MIN_OBSERVATIONS_FOR_UPDATE,LEARNING_RATE,MAX_WEIGHT_DELTA,MIN_R_SQUARED_FOR_GRADIENT,FLAT_GRADIENT_THRESHOLD,MAX_HISTORY_LENGTH,init_learning=__esm({"src/core/orchestration/learning.ts"(){init_contentNavigationStrategy(),init_types_legacy(),init_logger(),MIN_OBSERVATIONS_FOR_UPDATE=10,LEARNING_RATE=.1,MAX_WEIGHT_DELTA=.3,MIN_R_SQUARED_FOR_GRADIENT=.05,FLAT_GRADIENT_THRESHOLD=.02,MAX_HISTORY_LENGTH=100}});function computeOutcomeSignal(e,t={}){if(!e||e.length===0)return null;let n=t.targetAccuracy??.85,r=t.tolerance??.05,a=0;for(let t of e)t.isCorrect&&a++;return scoreAccuracyInZone(a/e.length,n,r)}function scoreAccuracyInZone(e,t,n){let r=Math.abs(e-t);if(r<=n)return 1;let a=r-n;return Math.max(0,1-a*2.5)}var init_signal=__esm({"src/core/orchestration/signal.ts"(){}});async function recordUserOutcome(e,t,n,r,a,o=0,s=0,c){let{user:l,course:u,userId:d}=e,p=u.getCourseID(),m=computeOutcomeSignal(r,c);if(m===null){logger.debug(`[Orchestration] No outcome signal computed for ${d} (insufficient data). Skipping record.`);return}let g={};for(let t of a)g[t]=e.getDeviation(t);let _=`USER_OUTCOME::${p}::${d}::${n}`,v={_id:_,docType:`USER_OUTCOME`,courseId:p,userId:d,periodStart:t,periodEnd:n,outcomeValue:m,deviations:g,metadata:{sessionsCount:1,cardsSeen:r.length,eloStart:o,eloEnd:s,signalType:`accuracy_in_zone`}};try{await l.putUserOutcome(v),logger.debug(`[Orchestration] Recorded outcome ${m.toFixed(3)} for ${d} (doc: ${_})`)}catch(e){logger.error(`[Orchestration] Failed to record outcome: ${e}`)}}var init_recording=__esm({"src/core/orchestration/recording.ts"(){init_signal(),init_types_legacy(),init_logger()}});function fnv1a(e){let t=2166136261;for(let n=0;n<e.length;n++)t^=e.charCodeAt(n),t=Math.imul(t,16777619);return t>>>0}function computeDeviation(e,t,n){return fnv1a(`${e}:${t}:${n}`)/4294967296*2-1}function computeSpread(e){return MAX_SPREAD-Math.max(0,Math.min(1,e))*(MAX_SPREAD-MIN_SPREAD)}function computeEffectiveWeight(e,t,n,r){let a=computeDeviation(t,n,r)*computeSpread(e.confidence)*e.weight,o=e.weight+a;return Math.max(MIN_WEIGHT,Math.min(MAX_WEIGHT,o))}async function createOrchestrationContext(e,t){let n;try{n=await t.getCourseConfig()}catch(e){logger.error(`[Orchestration] Failed to load course config: ${e}`),n={name:`Unknown`,description:``,public:!1,deleted:!1,creator:``,admins:[],moderators:[],dataShapes:[],questionTypes:[],orchestration:{salt:`default`}}}let r=e.getUsername(),a=n.orchestration?.salt||`default_salt`;return{user:e,course:t,userId:r,courseConfig:n,getEffectiveWeight(e,t){return computeEffectiveWeight(t,r,e,a)},getDeviation(e){return computeDeviation(r,e,a)}}}var MIN_SPREAD,MAX_SPREAD,MIN_WEIGHT,MAX_WEIGHT,init_orchestration=__esm({"src/core/orchestration/index.ts"(){init_logger(),init_gradient(),init_learning(),init_signal(),init_recording(),MIN_SPREAD=.1,MAX_SPREAD=.5,MIN_WEIGHT=.1,MAX_WEIGHT=3}}),Pipeline_exports={};__export(Pipeline_exports,{Pipeline:()=>Pipeline,mergeHints:()=>mergeHints2});function globToRegex(e){let t=e.replace(/[.+?^${}()|[\]\\]/g,`\\$&`).replace(/\*/g,`.*`);return RegExp(`^${t}$`)}function globMatch(e,t){return t.includes(`*`)?globToRegex(t).test(e):e===t}function cardMatchesTagPattern(e,t){return(e.tags??[]).some(e=>globMatch(e,t))}function mergeHints2(e){let t=e.filter(e=>e!=null);if(t.length===0)return;let n={},r={};for(let e of t)for(let[t,n]of Object.entries(e.boostTags??{}))r[t]=(r[t]??1)*n;Object.keys(r).length>0&&(n.boostTags=r);let a={};for(let e of t)for(let[t,n]of Object.entries(e.boostCards??{}))a[t]=(a[t]??1)*n;Object.keys(a).length>0&&(n.boostCards=a);let concatUnique=e=>{let r=t.flatMap(t=>t[e]??[]);r.length>0&&(n[e]=[...new Set(r)])};concatUnique(`requireTags`),concatUnique(`requireCards`),concatUnique(`excludeTags`),concatUnique(`excludeCards`);let o=t.map(e=>e._label).filter(Boolean);return o.length>0&&(n._label=o.join(`; `)),Object.keys(n).length>0?n:void 0}function logPipelineConfig(e,t){let n=t.length>0?`
|
|
248
|
+
`)}},mountPipelineDebugger()}}),SrsDebugger_exports={};__export(SrsDebugger_exports,{captureSrsBacklog:()=>captureSrsBacklog,clearSrsBacklogDebug:()=>clearSrsBacklogDebug,getSrsBacklogDebug:()=>getSrsBacklogDebug,noteReviewPressure:()=>noteReviewPressure});function captureSrsBacklog(e){snapshots.set(e.courseId,e)}function noteReviewPressure(e,t){let n=snapshots.get(e);n&&snapshots.set(e,{...n,reviewMass:t.mass,healthyBacklog:t.healthyMass,backlogGrowthRate:t.rate,backlogMultiplier:t.multiplier})}function getSrsBacklogDebug(){return[...snapshots.values()].sort((e,t)=>t.timestamp-e.timestamp)}function clearSrsBacklogDebug(){snapshots.clear()}var snapshots,init_SrsDebugger=__esm({"src/core/navigators/SrsDebugger.ts"(){snapshots=new Map}}),StrategyPressureDebugger_exports={};__export(StrategyPressureDebugger_exports,{captureStrategyPressure:()=>captureStrategyPressure,clearStrategyPressureDebug:()=>clearStrategyPressureDebug,getStrategyPressureDebug:()=>getStrategyPressureDebug});function captureStrategyPressure(e){snapshots2.set(`${e.source}:${e.courseId}`,e)}function getStrategyPressureDebug(){return[...snapshots2.values()].sort((e,t)=>t.timestamp-e.timestamp)}function clearStrategyPressureDebug(){snapshots2.clear()}var snapshots2,init_StrategyPressureDebugger=__esm({"src/core/navigators/StrategyPressureDebugger.ts"(){snapshots2=new Map}}),CompositeGenerator_exports={};__export(CompositeGenerator_exports,{AggregationMode:()=>AggregationMode,default:()=>CompositeGenerator});function mergeHints(e){let t=e.filter(e=>e!==void 0);if(t.length===0)return;let n={},r={};for(let e of t)for(let[t,n]of Object.entries(e.boostTags??{}))r[t]=(r[t]??1)*n;Object.keys(r).length>0&&(n.boostTags=r);let a={};for(let e of t)for(let[t,n]of Object.entries(e.boostCards??{}))a[t]=(a[t]??1)*n;Object.keys(a).length>0&&(n.boostCards=a);let concatUnique=e=>{let r=t.flatMap(t=>t[e]??[]);r.length>0&&(n[e]=[...new Set(r)])};concatUnique(`requireTags`),concatUnique(`requireCards`),concatUnique(`excludeTags`),concatUnique(`excludeCards`);let o=t.map(e=>e._label).filter(Boolean);return o.length>0&&(n._label=o.join(`; `)),Object.keys(n).length>0?n:void 0}var AggregationMode,DEFAULT_AGGREGATION_MODE,FREQUENCY_BOOST_FACTOR,CompositeGenerator,init_CompositeGenerator=__esm({"src/core/navigators/generators/CompositeGenerator.ts"(){init_navigators(),init_logger(),AggregationMode=(e=>(e.MAX=`max`,e.AVERAGE=`average`,e.FREQUENCY_BOOST=`frequencyBoost`,e))(AggregationMode||{}),DEFAULT_AGGREGATION_MODE=`frequencyBoost`,FREQUENCY_BOOST_FACTOR=.1,CompositeGenerator=class _CompositeGenerator extends ContentNavigator{constructor(e,t=DEFAULT_AGGREGATION_MODE){if(super(),_defineProperty(this,`name`,`Composite Generator`),_defineProperty(this,`generators`,void 0),_defineProperty(this,`aggregationMode`,void 0),this.generators=e,this.aggregationMode=t,e.length===0)throw Error(`CompositeGenerator requires at least one generator`);logger.debug(`[CompositeGenerator] Created with ${e.length} generators, mode: ${t}`)}static async fromStrategies(e,t,n,r=DEFAULT_AGGREGATION_MODE){return new _CompositeGenerator(await Promise.all(n.map(n=>ContentNavigator.create(e,t,n))),r)}async getWeightedCards(e,t){if(!t)throw Error(`CompositeGenerator.getWeightedCards requires a GeneratorContext. It should be called via Pipeline, not directly.`);let n=await Promise.all(this.generators.map(n=>n.getWeightedCards(e,t))),r=[];n.forEach((e,t)=>{let n=e.cards,a=this.generators[t].name||`Generator ${t}`,o=n.filter(e=>e.provenance[0]?.reason?.includes(`new card`)),s=n.filter(e=>e.provenance[0]?.reason?.includes(`review`));if(n.length>0){let e=Math.max(...n.map(e=>e.score)).toFixed(2),t=[];o.length>0&&t.push(`${o.length} new`),s.length>0&&t.push(`${s.length} reviews`);let c=t.length>0?t.join(`, `):`${n.length} cards`;r.push(`${a}: ${c} (top: ${e})`)}else r.push(`${a}: 0 cards`)}),logger.info(`[Composite] Generator breakdown: ${r.join(` | `)}`);let a=new Map;n.forEach((e,n)=>{let r=e.cards,o=this.generators[n],s=o.learnable?.weight??1,c;if(o.learnable&&!o.staticWeight&&t.orchestration){let e=o.strategyId;e&&(s=t.orchestration.getEffectiveWeight(e,o.learnable),c=t.orchestration.getDeviation(e))}for(let e of r){e.provenance.length>0&&(e.provenance[0].effectiveWeight=s,e.provenance[0].deviation=c);let t=a.get(e.cardId)||[];t.push({card:e,weight:s}),a.set(e.cardId,t)}});let o=[];for(let[,e]of a){let t=e.map(e=>e.card),n=this.aggregateScores(e),r=Math.max(0,n),a=t.flatMap(e=>e.provenance),s=t[0].score,c=r>s?`boosted`:r<s?`penalized`:`passed`,l=this.buildAggregationReason(e,r);o.push({...t[0],score:r,provenance:[...a,{strategy:`composite`,strategyName:`Composite Generator`,strategyId:`COMPOSITE_GENERATOR`,action:c,score:r,reason:l}]})}return{cards:o.sort((e,t)=>t.score-e.score).slice(0,e),hints:mergeHints(n.map(e=>e.hints))}}buildAggregationReason(e,t){let n=e.map(e=>e.card),r=n.length,a=n.map(e=>e.score.toFixed(2)).join(`, `);if(r===1){let n=Math.abs(e[0].weight-1)>.001?` (w=${e[0].weight.toFixed(2)})`:``;return`Single generator, score ${t.toFixed(2)}${n}`}let o=n.map(e=>e.provenance[0]?.strategy||`unknown`).join(`, `);switch(this.aggregationMode){case`max`:return`Max of ${r} generators (${o}): scores [${a}] \u2192 ${t.toFixed(2)}`;case`average`:return`Weighted Avg of ${r} generators (${o}): scores [${a}] \u2192 ${t.toFixed(2)}`;case`frequencyBoost`:{let n=e.reduce((e,t)=>e+t.weight,0),a=e.reduce((e,t)=>e+t.card.score*t.weight,0),s=n>0?a/n:0,c=1+FREQUENCY_BOOST_FACTOR*(r-1);return`Frequency boost from ${r} generators (${o}): w-avg ${s.toFixed(2)} \xD7 ${c.toFixed(2)} \u2192 ${t.toFixed(2)}`}default:return`Aggregated from ${r} generators: ${t.toFixed(2)}`}}aggregateScores(e){let t=e.map(e=>e.card.score);switch(this.aggregationMode){case`max`:return Math.max(...t);case`average`:{let t=e.reduce((e,t)=>e+t.weight,0);return t===0?0:e.reduce((e,t)=>e+t.card.score*t.weight,0)/t}case`frequencyBoost`:{let t=e.reduce((e,t)=>e+t.weight,0),n=e.reduce((e,t)=>e+t.card.score*t.weight,0);return(t>0?n/t:0)*(1+FREQUENCY_BOOST_FACTOR*(e.length-1))}default:return t[0]}}}}}),elo_exports={};__export(elo_exports,{default:()=>ELONavigator});var ELONavigator,init_elo=__esm({"src/core/navigators/generators/elo.ts"(){init_navigators(),init_logger(),ELONavigator=class extends ContentNavigator{constructor(e,t,n){super(e,t,n),_defineProperty(this,`name`,void 0),this.name=n?.name||`ELO`}async getWeightedCards(e,t){let n;n=t?.userElo===void 0?toCourseElo((await this.user.getCourseRegDoc(this.course.getCourseID())).elo).global.score:t.userElo;let r=await this.user.getActiveCards(),a=(await this.course.getCardsCenteredAtELO({limit:e,elo:`user`},e=>!r.some(t=>e.cardID===t.cardID))).map(e=>({...e,status:`new`})).map(e=>{let t=e.elo??1e3,r=Math.abs(t-n),a=.5+.5*Math.random();return{cardId:e.cardID,courseId:e.courseID,score:a,cardElo:t,provenance:[{strategy:`elo`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||`NAVIGATION_STRATEGY-ELO-default`,action:`generated`,score:a,reason:`new card near learner ELO: distance ${Math.round(r)} (card: ${Math.round(t)}, user: ${Math.round(n)}), jitter ${a.toFixed(3)}`}]}});a.sort((e,t)=>t.score-e.score);let o=a.slice(0,e);if(o.length>0){let e=o.slice(0,3).map(e=>e.score.toFixed(2)).join(`, `);logger.info(`[ELO] Course ${this.course.getCourseID()}: ${o.length} new cards (top scores: ${e})`)}else logger.info(`[ELO] Course ${this.course.getCourseID()}: No new cards available`);return{cards:o}}}}}),generators_exports={},init_generators=__esm({"src/core/navigators/generators/index.ts"(){}}),prescribed_exports={};__export(prescribed_exports,{default:()=>PrescribedCardsGenerator,unmetIntroExposeTags:()=>unmetIntroExposeTags});function dedupe(e){return[...new Set(e)]}function isoNow(){return new Date().toISOString()}function clamp(e,t,n){return Math.max(t,Math.min(n,e))}function matchesTagPattern(e,t){if(t===`*`)return!0;let n=t.replace(/[.+^${}()|[\]\\]/g,`\\$&`).replace(/\*/g,`.*`);return RegExp(`^${n}$`).test(e)}function extractWordStem(e){for(let t of[`c-ml-`,`c-ws-`,`c-spelling-`])if(e.startsWith(t)){let n=e.slice(t.length),r=n.lastIndexOf(`-`);return r>0?n.slice(0,r):n}return e}function shuffleInPlace(e){for(let t=e.length-1;t>0;t--){let n=Math.floor(Math.random()*(t+1));[e[t],e[n]]=[e[n],e[t]]}}function formatAge(e){let t=Math.max(0,Math.round(e/6e4));if(t<60)return`${t}m`;let n=Math.round(t/60);return n<24?`${n}h`:`${Math.round(n/24)}d`}function unmetIntroExposeTags(e,t,n){if(n<=0)return[];let r=new Set(e.filter(e=>e.startsWith(`gpc:expose:`)));for(let t of e){if(!t.startsWith(`gpc:intro:`))continue;let e=t.slice(10);e&&r.add(`gpc:expose:${e}`)}return[...r].filter(e=>{let r=t[e];return!r||r.count<n})}function pickTopByScore(e,t){return[...e].sort((e,t)=>t.score-e.score||e.cardId.localeCompare(t.cardId)).slice(0,t)}var DEFAULT_FRESHNESS_WINDOW,DEFAULT_MAX_DIRECT_PER_RUN,DEFAULT_MAX_SUPPORT_PER_RUN,DEFAULT_HIERARCHY_DEPTH,DEFAULT_MIN_COUNT,DEFAULT_PRACTICE_MIN_COUNT,DEFAULT_MAX_PRACTICE_PER_RUN,BASE_TARGET_SCORE,BASE_SUPPORT_SCORE,DISCOVERED_SUPPORT_SCORE,BASE_PRACTICE_SCORE,PRACTICE_BASE_MULT,MAX_PRACTICE_MULTIPLIER,PRACTICE_STALENESS_BUMP_PER_DAY,MAX_TARGET_MULTIPLIER,MAX_SUPPORT_MULTIPLIER,PRESCRIBED_DEBUG_VERSION,PrescribedCardsGenerator,init_prescribed=__esm({"src/core/navigators/generators/prescribed.ts"(){init_navigators(),init_StrategyPressureDebugger(),init_logger(),DEFAULT_FRESHNESS_WINDOW=3,DEFAULT_MAX_DIRECT_PER_RUN=3,DEFAULT_MAX_SUPPORT_PER_RUN=3,DEFAULT_HIERARCHY_DEPTH=2,DEFAULT_MIN_COUNT=3,DEFAULT_PRACTICE_MIN_COUNT=3,DEFAULT_MAX_PRACTICE_PER_RUN=4,BASE_TARGET_SCORE=1,BASE_SUPPORT_SCORE=.8,DISCOVERED_SUPPORT_SCORE=12,BASE_PRACTICE_SCORE=1,PRACTICE_BASE_MULT=2,MAX_PRACTICE_MULTIPLIER=4,PRACTICE_STALENESS_BUMP_PER_DAY=.5,MAX_TARGET_MULTIPLIER=8,MAX_SUPPORT_MULTIPLIER=4,PRESCRIBED_DEBUG_VERSION=`testversion-prescribed-v3`,PrescribedCardsGenerator=class extends ContentNavigator{constructor(e,t,n){super(e,t,n),_defineProperty(this,`name`,void 0),_defineProperty(this,`config`,void 0),this.name=n.name||`Prescribed Cards`,this.config=this.parseConfig(n.serializedData),logger.debug(`[Prescribed] Initialized with ${this.config.groups.length} groups and ${this.config.groups.reduce((e,t)=>e+t.targetCardIds.length,0)} targets`)}get strategyKey(){return`PrescribedProgress`}async getWeightedCards(e,t){if(this.config.groups.length===0||e<=0)return{cards:[]};let n=this.course.getCourseID(),r=await this.user.getActiveCards(),a=new Set(r.map(e=>e.cardID)),o=await this.user.getSeenCards(n).catch(()=>[]),s=new Set(o),c=await this.getStrategyState()??{updatedAt:isoNow(),groups:{}},l=await this.loadHierarchyConfigs(),u=await this.user.getCourseRegDoc(n).catch(()=>null),d=typeof u?.elo==`number`?u.elo:u?.elo?.global?.score??t?.userElo??1e3,p=typeof u?.elo==`number`?{}:u?.elo?.tags??{},m=dedupe(this.config.groups.flatMap(e=>e.targetCardIds)),g=dedupe(this.config.groups.flatMap(e=>e.supportCardIds??[])),_=dedupe([...m,...g]),v=_.length>0?await this.course.getAppliedTagsBatch(_):new Map,y=await this.course.getCourseTagStubs().catch(()=>({rows:[],offset:0,total_rows:0})),b=new Map;for(let e of y.rows??[]){let t=e.doc;t?.name&&Array.isArray(t.taggedCards)&&b.set(t.name,[...t.taggedCards])}let x={updatedAt:isoNow(),groups:{}},S=[],C=new Set,w=[],T=c.practiceDebt??{},E={},D=new Map;for(let e of this.config.groups){let t=this.buildGroupRuntimeState({group:e,priorState:c.groups[e.id],activeIds:a,seenIds:s,tagsByCard:v,cardsByTag:b,hierarchyConfigs:l,userTagElo:p,userGlobalElo:d});w.push(t),logger.info(`[Prescribed] Group '${e.id}': ${e.targetCardIds.length} targets total, ${t.encounteredTargets.size} encountered, ${t.pendingTargets.length} pending (${t.surfaceableTargets.length} surfaceable, ${t.blockedTargets.length} blocked), ${t.supportCandidates.length} authored support candidates, ${t.discoveredSupportCandidates.length} discovered support candidates, pressure=${t.pressureMultiplier.toFixed(2)}`),t.blockedTargets.length>0&&(logger.info(`[Prescribed] Group '${e.id}' blocked targets: ${t.blockedTargets.join(`, `)}`),logger.info(`[Prescribed] Group '${e.id}' support tags needed: ${t.supportTags.join(`, `)||`(none)`}`),logger.info(`[Prescribed] Group '${e.id}' escalation mode: `+(t.supportCandidates.length>0?`direct-support`:t.discoveredSupportCandidates.length>0?`inserted-support-candidates`:`boost-only`)),t.discoveredSupportCandidates.length>0&&logger.info(`[Prescribed] Group '${e.id}' discovered support candidates: ${t.discoveredSupportCandidates.join(`, `)}`)),x.groups[e.id]=this.buildNextGroupState(t,c.groups[e.id]);let r=this.buildDirectTargetCards(t,n,C),o=this.buildSupportCards(t,n,C),u=this.buildDiscoveredSupportCards(t,n,C),m=this.buildPracticeCards({group:e,courseId:n,emittedIds:C,cardsByTag:b,hierarchyConfigs:l,userTagElo:p,userGlobalElo:d,activeIds:a,seenIds:s,priorPracticeDebt:T,nextPracticeDebt:E});D.set(e.id,m.debts),S.push(...r,...o,...u,...m.cards)}x.practiceDebt=E;let O=this.buildSupportHintSummary(w),k=Object.keys(O.boostTags).length>0?{boostTags:O.boostTags,_label:`prescribed-support (${O.supportTags.length} tags; blocked=${O.blockedTargetIds.length}; testversion=${PRESCRIBED_DEBUG_VERSION})`}:void 0;if(k){let e=Object.entries(k.boostTags??{});logger.info(`[Prescribed] Emitting ${e.length} boost hint(s): `+e.map(([e,t])=>`${e}\xD7${t.toFixed(1)}`).join(`, `))}else logger.info(`[Prescribed] No hints to emit (no blocked targets or no support tags)`);if(this.capturePressureSnapshot(n,w,D,S,k),S.length===0)return logger.info(`[Prescribed] 0 cards emitted (all targets blocked, authored/discovered support candidates exhausted)`+(k?` — boost hints emitted but may not survive filters`:``)),await this.putStrategyState(x).catch(e=>{logger.debug(`[Prescribed] Failed to persist empty-state update: ${e}`)}),k?{cards:[],hints:k}:{cards:[]};let A=pickTopByScore(S,e),j=new Map;for(let e of A){let t=e.provenance[0];if(t?.reason.includes(`mode=practice`))continue;let n=t?.reason.match(/group=([^;]+)/)?.[1],r=t?.reason.includes(`mode=support`)?`supportIds`:`targetIds`;n&&(j.has(n)||j.set(n,{targetIds:[],supportIds:[]}),j.get(n)[r].push(e.cardId))}for(let e of this.config.groups){let t=x.groups[e.id],n=j.get(e.id);n&&(n.targetIds.length>0||n.supportIds.length>0)&&(t.lastSurfacedAt=isoNow(),t.sessionsSinceSurfaced=0,n.supportIds.length>0&&(t.lastSupportAt=isoNow()))}return await this.putStrategyState(x).catch(e=>{logger.debug(`[Prescribed] Failed to persist prescribed progress: ${e}`)}),logger.info(`[Prescribed] Emitting ${A.length} cards (${A.filter(e=>e.provenance[0]?.reason.includes(`mode=target`)).length} target, ${A.filter(e=>e.provenance[0]?.reason.includes(`mode=support`)).length} support, ${A.filter(e=>e.provenance[0]?.reason.includes(`mode=discovered-support`)).length} discovered support)`),k?{cards:A,hints:k}:{cards:A}}buildSupportHintSummary(e){let t={},n=new Set,r=new Set;for(let a of e)if(!(a.blockedTargets.length===0||a.supportTags.length===0)){a.blockedTargets.forEach(e=>n.add(e));for(let e of a.supportTags)r.add(e),t[e]=(t[e]??1)*a.supportMultiplier}return{boostTags:t,blockedTargetIds:[...n].sort(),supportTags:[...r].sort()}}capturePressureSnapshot(e,t,n,r,a){let o=Date.now(),s=[];for(let e of t){let t=e.group,r=t.freshnessWindowSessions??DEFAULT_FRESHNESS_WINDOW;if(s.push({id:`group:${t.id}:target`,label:`${t.id} targets`,multiplier:e.pressureMultiplier,max:MAX_TARGET_MULTIPLIER,detail:`${e.pendingTargets.length} pending (${e.surfaceableTargets.length} surfaceable, ${e.blockedTargets.length} blocked) \xB7 sinceSurfaced ${e.sessionsSinceSurfaced}/${r}`,items:e.blockedTargets.map(e=>({label:e,value:`blocked`}))}),e.blockedTargets.length>0){let n=e.supportCandidates.length>0?`direct-support`:e.discoveredSupportCandidates.length>0?`inserted-support`:`boost-only`;s.push({id:`group:${t.id}:support`,label:`${t.id} support`,multiplier:e.supportMultiplier,max:MAX_SUPPORT_MULTIPLIER,detail:`mode=${n} \xB7 ${e.supportTags.length} support tag(s)`,items:e.supportTags.map(e=>({label:e}))})}let a=n.get(t.id)??[];a.length>0&&s.push({id:`group:${t.id}:practice-debt`,label:`${t.id} practice debt`,multiplier:Math.max(...a.map(e=>e.multiplier)),max:MAX_PRACTICE_MULTIPLIER,detail:`${a.length} under-practiced skill(s)`,items:a.map(e=>({label:e.tag,value:`\xD7${e.multiplier.toFixed(2)} \xB7 open ${formatAge(o-new Date(e.firstOwedAt).getTime())}`}))})}captureStrategyPressure({source:`prescribed`,courseId:e,gauges:s,topScore:r.length>0?Math.max(...r.map(e=>e.score)):null,hintsLabel:a?._label,timestamp:o})}parseConfig(e){try{let t=JSON.parse(e);return{groups:(Array.isArray(t.groups)?t.groups:[]).map((e,t)=>({id:typeof e.id==`string`&&e.id.trim().length>0?e.id:`group-${t+1}`,targetCardIds:dedupe(Array.isArray(e.targetCardIds)?e.targetCardIds.filter(e=>typeof e==`string`):[]),supportCardIds:dedupe(Array.isArray(e.supportCardIds)?e.supportCardIds.filter(e=>typeof e==`string`):[]),supportTagPatterns:dedupe(Array.isArray(e.supportTagPatterns)?e.supportTagPatterns.filter(e=>typeof e==`string`):[]),freshnessWindowSessions:typeof e.freshnessWindowSessions==`number`?e.freshnessWindowSessions:DEFAULT_FRESHNESS_WINDOW,maxDirectTargetsPerRun:typeof e.maxDirectTargetsPerRun==`number`?e.maxDirectTargetsPerRun:DEFAULT_MAX_DIRECT_PER_RUN,maxSupportCardsPerRun:typeof e.maxSupportCardsPerRun==`number`?e.maxSupportCardsPerRun:DEFAULT_MAX_SUPPORT_PER_RUN,introExposeMinCount:typeof e.introExposeMinCount==`number`?e.introExposeMinCount:DEFAULT_MIN_COUNT,hierarchyWalk:{enabled:e.hierarchyWalk?.enabled!==!1,maxDepth:typeof e.hierarchyWalk?.maxDepth==`number`?e.hierarchyWalk.maxDepth:DEFAULT_HIERARCHY_DEPTH},retireOnEncounter:e.retireOnEncounter!==!1,practiceTagPatterns:dedupe(Array.isArray(e.practiceTagPatterns)?e.practiceTagPatterns.filter(e=>typeof e==`string`):[]),practiceMinCount:typeof e.practiceMinCount==`number`?e.practiceMinCount:DEFAULT_PRACTICE_MIN_COUNT,maxPracticeCardsPerRun:typeof e.maxPracticeCardsPerRun==`number`?e.maxPracticeCardsPerRun:DEFAULT_MAX_PRACTICE_PER_RUN})).filter(e=>e.targetCardIds.length>0)}}catch{return{groups:[]}}}async loadHierarchyConfigs(){try{return(await this.course.getAllNavigationStrategies()).filter(e=>e.implementingClass===`hierarchyDefinition`).map(e=>{try{return{prerequisites:JSON.parse(e.serializedData).prerequisites||{}}}catch{return{prerequisites:{}}}})}catch(e){return logger.debug(`[Prescribed] Failed to load hierarchy configs: ${e}`),[]}}buildGroupRuntimeState(e){let{group:t,priorState:n,activeIds:r,seenIds:a,tagsByCard:o,cardsByTag:s,hierarchyConfigs:c,userTagElo:l,userGlobalElo:u}=e,d=new Set;for(let e of t.targetCardIds)(r.has(e)||a.has(e))&&d.add(e);if(n?.encounteredCardIds?.length)for(let e of n.encounteredCardIds)d.add(e);let p=t.targetCardIds.filter(e=>!d.has(e)),m=new Map;for(let e of p)m.set(e,o.get(e)??[]);let g=[],_=[],v=new Set;for(let e of p){let n=m.get(e)??[],r=this.resolveBlockedSupportTags(n,c,l,u,t.hierarchyWalk?.enabled!==!1,t.hierarchyWalk?.maxDepth??DEFAULT_HIERARCHY_DEPTH),a=unmetIntroExposeTags(n,l,t.introExposeMinCount??DEFAULT_MIN_COUNT);a.length>0&&a.forEach(e=>v.add(e)),r.blocked||a.length>0?(g.push(e),r.supportTags.forEach(e=>v.add(e))):_.push(e)}let y=dedupe([...t.supportCardIds??[],...this.findSupportCardsByTags(t,o,[...v])]).filter(e=>!r.has(e)&&!a.has(e)),b=g.length>0&&v.size>0&&y.length===0?this.findDiscoveredSupportCards({supportTags:[...v],cardsByTag:s,activeIds:r,seenIds:a,excludedIds:new Set([...t.targetCardIds,...t.supportCardIds??[]]),limit:t.maxSupportCardsPerRun??DEFAULT_MAX_SUPPORT_PER_RUN}):[];g.length>0&&v.size>0&&b.length===0&&logger.info(`[Prescribed] Group '${t.id}' discovered 0 broader support candidates (blocked=${g.length}; authoredSupport=${y.length})`);let x=n?.sessionsSinceSurfaced??0,S=t.freshnessWindowSessions??DEFAULT_FRESHNESS_WINDOW,C=Math.max(0,x-S),w=p.length===0?1:clamp(1+C*.75+Math.min(2,p.length*.1),1,MAX_TARGET_MULTIPLIER),T=g.length===0?1:clamp(1+C*.5+Math.min(1.5,g.length*.15),1,MAX_SUPPORT_MULTIPLIER);return{group:t,encounteredTargets:d,pendingTargets:p,blockedTargets:g,surfaceableTargets:_,targetTags:m,supportCandidates:y,discoveredSupportCandidates:b,supportTags:[...v],pressureMultiplier:w,supportMultiplier:T,sessionsSinceSurfaced:x,debugVersion:PRESCRIBED_DEBUG_VERSION}}buildNextGroupState(e,t){let n=t?.sessionsSinceSurfaced??0,r=!1;return{encounteredCardIds:[...e.encounteredTargets].sort(),pendingTargetIds:[...e.pendingTargets].sort(),lastSurfacedAt:t?.lastSurfacedAt??null,sessionsSinceSurfaced:n+1,lastSupportAt:t?.lastSupportAt??null,blockedTargetIds:[...e.blockedTargets].sort(),lastResolvedSupportTags:[...e.supportTags].sort()}}buildDirectTargetCards(e,t,n){let r=e.group.maxDirectTargetsPerRun??DEFAULT_MAX_DIRECT_PER_RUN,a=e.surfaceableTargets.filter(e=>!n.has(e)).slice(0,r),o=[];for(let r of a)n.add(r),o.push({cardId:r,courseId:t,score:BASE_TARGET_SCORE*e.pressureMultiplier,provenance:[{strategy:`prescribed`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||`NAVIGATION_STRATEGY-prescribed`,action:`generated`,score:BASE_TARGET_SCORE*e.pressureMultiplier,reason:`mode=target;group=${e.group.id};pending=${e.pendingTargets.length};surfaceable=${e.surfaceableTargets.length};blocked=${e.blockedTargets.length};blockedTargets=${e.blockedTargets.join(`|`)||`none`};supportTags=${e.supportTags.join(`|`)||`none`};multiplier=${e.pressureMultiplier.toFixed(2)};testversion=${e.debugVersion}`}]});return o}buildSupportCards(e,t,n){if(e.blockedTargets.length===0||e.supportCandidates.length===0)return[];let r=e.group.maxSupportCardsPerRun??DEFAULT_MAX_SUPPORT_PER_RUN,a=e.supportCandidates.filter(e=>!n.has(e)).slice(0,r),o=[];for(let r of a)n.add(r),o.push({cardId:r,courseId:t,score:BASE_SUPPORT_SCORE*e.supportMultiplier,provenance:[{strategy:`prescribed`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||`NAVIGATION_STRATEGY-prescribed`,action:`generated`,score:BASE_SUPPORT_SCORE*e.supportMultiplier,reason:`mode=support;group=${e.group.id};pending=${e.pendingTargets.length};blocked=${e.blockedTargets.length};blockedTargets=${e.blockedTargets.join(`|`)||`none`};supportCard=${r};supportTags=${e.supportTags.join(`|`)||`none`};multiplier=${e.supportMultiplier.toFixed(2)};testversion=${e.debugVersion}`}]});return o}buildDiscoveredSupportCards(e,t,n){if(e.blockedTargets.length===0||e.discoveredSupportCandidates.length===0)return[];let r=e.group.maxSupportCardsPerRun??DEFAULT_MAX_SUPPORT_PER_RUN,a=e.discoveredSupportCandidates.filter(e=>!n.has(e)).slice(0,r),o=[];for(let r of a)n.add(r),o.push({cardId:r,courseId:t,score:DISCOVERED_SUPPORT_SCORE*e.supportMultiplier,provenance:[{strategy:`prescribed`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||`NAVIGATION_STRATEGY-prescribed`,action:`generated`,score:DISCOVERED_SUPPORT_SCORE*e.supportMultiplier,reason:`mode=discovered-support;group=${e.group.id};pending=${e.pendingTargets.length};blocked=${e.blockedTargets.length};blockedTargets=${e.blockedTargets.join(`|`)||`none`};supportCard=${r};supportTags=${e.supportTags.join(`|`)||`none`};multiplier=${e.supportMultiplier.toFixed(2)};testversion=${e.debugVersion}`}]});return o}buildPracticeCards(e){let{group:t,courseId:n,emittedIds:r,cardsByTag:a,hierarchyConfigs:o,userTagElo:s,userGlobalElo:c,activeIds:l,seenIds:u,priorPracticeDebt:d,nextPracticeDebt:p}=e,m=t.practiceTagPatterns??[];if(m.length===0)return{cards:[],debts:[]};let g=t.practiceMinCount??DEFAULT_PRACTICE_MIN_COUNT,_=t.maxPracticeCardsPerRun??DEFAULT_MAX_PRACTICE_PER_RUN,v=[...a.keys()].filter(e=>m.some(t=>matchesTagPattern(e,t))&&this.isUnlockedGatedSkill(e,o,s,c)&&(s[e]?.count??0)<g);if(v.length===0)return{cards:[],debts:[]};let y=Date.now(),b=1440*60*1e3,x=new Map;for(let e of v){let t=d[e]??isoNow();p[e]=t;let n=Math.max(0,(y-new Date(t).getTime())/864e5),r=clamp(PRACTICE_BASE_MULT+n*PRACTICE_STALENESS_BUMP_PER_DAY,PRACTICE_BASE_MULT,MAX_PRACTICE_MULTIPLIER);x.set(e,r)}let S=v.map(e=>({tag:e,multiplier:x.get(e)??PRACTICE_BASE_MULT,firstOwedAt:p[e]})).sort((e,t)=>t.multiplier-e.multiplier||e.tag.localeCompare(t.tag)),C=this.findDiscoveredSupportCards({supportTags:v,cardsByTag:a,activeIds:l,seenIds:u,excludedIds:r,limit:_});if(C.length===0)return{cards:[],debts:S};logger.info(`[Prescribed] Group '${t.id}' practice: ${v.length} unlocked under-practiced skill(s), emitting ${C.length} drill card(s)`);let w=[];for(let e of C){r.add(e);let o=PRACTICE_BASE_MULT;for(let t of v)(a.get(t)?.includes(e)??!1)&&(o=Math.max(o,x.get(t)??PRACTICE_BASE_MULT));let s=BASE_PRACTICE_SCORE*o;w.push({cardId:e,courseId:n,score:s,provenance:[{strategy:`prescribed`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||`NAVIGATION_STRATEGY-prescribed`,action:`generated`,score:s,reason:`mode=practice;group=${t.id};debtMult=\xD7${o.toFixed(2)};underPracticedSkills=${v.length};practiceTags=${v.slice(0,8).join(`|`)}${v.length>8?`|…`:``};testversion=${PRESCRIBED_DEBUG_VERSION}`}]})}return{cards:w,debts:S}}isUnlockedGatedSkill(e,t,n,r){let a=t.map(t=>t.prerequisites[e]).filter(e=>Array.isArray(e)&&e.length>0);return a.length===0?!1:a.every(e=>e.every(e=>this.isPrerequisiteMet(e,n[e.tag],r)))}findSupportCardsByTags(e,t,n){if(n.length===0)return[];let r=e.supportCardIds??[],a=e.supportTagPatterns??[];if(r.length===0&&a.length===0)return[];let o=new Set;for(let e of r){let r=t.get(e)??[],s=n.some(e=>r.includes(e)),c=a.some(e=>r.some(t=>matchesTagPattern(t,e)));(s||c)&&o.add(e)}return[...o]}findDiscoveredSupportCards(e){let{supportTags:t,cardsByTag:n,activeIds:r,seenIds:a,excludedIds:o,limit:s}=e,c=new Map;for(let e of t){let t=n.get(e)??[];for(let e of t){if(r.has(e)||a.has(e)||o.has(e))continue;let t=c.get(e);t?t.matches+=1:c.set(e,{cardId:e,matches:1})}}let l=[...c.values()].sort((e,t)=>t.matches-e.matches||e.cardId.localeCompare(t.cardId)),u=new Set,d=[],p=[];for(let e of l){let t=extractWordStem(e.cardId);u.has(t)?p.push(e):(u.add(t),d.push(e))}return shuffleInPlace(d),shuffleInPlace(p),[...d,...p].slice(0,s).map(e=>e.cardId)}resolveBlockedSupportTags(e,t,n,r,a,o){let s=new Set,c=!1;for(let l of e){let e=t.map(e=>e.prerequisites[l]).filter(e=>Array.isArray(e)&&e.length>0);if(e.length!==0&&e.some(e=>e.some(e=>!this.isPrerequisiteMet(e,n[e.tag],r)))){if(c=!0,!a){for(let t of e)for(let e of t)this.isPrerequisiteMet(e,n[e.tag],r)||s.add(e.tag);continue}for(let a of e)for(let e of a)this.isPrerequisiteMet(e,n[e.tag],r)||this.collectSupportTagsRecursive(e.tag,t,n,r,o,new Set,s)}}return{blocked:c,supportTags:[...s]}}collectSupportTagsRecursive(e,t,n,r,a,o,s){if(a<0||o.has(e))return;o.add(e);let c=!1;for(let l of t){let u=l.prerequisites[e];if(!u||u.length===0)continue;let d=u.filter(e=>!this.isPrerequisiteMet(e,n[e.tag],r));if(d.length>0&&a>0){c=!0;for(let e of d)this.collectSupportTagsRecursive(e.tag,t,n,r,a-1,o,s)}}c||s.add(e)}isPrerequisiteMet(e,t,n){if(!t)return!1;let r=e.masteryThreshold?.minCount??DEFAULT_MIN_COUNT;return t.count<r?!1:e.masteryThreshold?.minElo===void 0?e.masteryThreshold?.minCount===void 0?t.score>=n:!0:t.score>=e.masteryThreshold.minElo}}}}),regulators_exports={};__export(regulators_exports,{DEFAULT_REGULATOR_CONFIG:()=>DEFAULT_REGULATOR_CONFIG,INTAKE_CLOCK_KEY:()=>INTAKE_CLOCK_KEY,applyReading:()=>applyReading,frozenMsAt:()=>frozenMsAt,intakeHours:()=>intakeHours,intakeReading:()=>intakeReading,resetIntakeClock:()=>resetIntakeClock,reviewMassReading:()=>reviewMassReading,reviewWeight:()=>reviewWeight,stepIntakeClock:()=>stepIntakeClock});function clamp01(e){return Math.min(1,Math.max(0,e))}function reviewWeight(e,t,n){return e.reviewUrgency!==void 0&&t!==void 0&&t>0?clamp01(e.reviewUrgency*(e.score/t)):clamp01(e.score/n)}function reviewMassReading(e,t,n=DEFAULT_REGULATOR_CONFIG.reviewMass){let r=e.reduce((e,r)=>e+reviewWeight(r,t.get(r.cardId),n.referenceScore),0);return{name:`review-mass`,class:`review`,urgency:r,multiplier:r>n.healthyMass?n.rate**+((r-n.healthyMass)/n.healthyMass):1,applied:e.length,inputs:{due:e.length,mass:r,healthyMass:n.healthyMass}}}function resetIntakeClock(e){return{lastNewAt:new Date(e).toISOString(),frozenMs:0,unservableSince:null}}function frozenMsAt(e,t){let n=e.unservableSince?Math.max(0,t-Date.parse(e.unservableSince)):0;return e.frozenMs+n}function intakeHours(e,t){let n=t-Date.parse(e.lastNewAt);return Math.max(0,n-frozenMsAt(e,t))/36e5}function stepIntakeClock(e,t,n){return n===0&&!e.unservableSince?{...e,unservableSince:new Date(t).toISOString()}:n>0&&e.unservableSince?{...e,frozenMs:frozenMsAt(e,t),unservableSince:null}:e}function intakeReading(e,t,n,r=DEFAULT_REGULATOR_CONFIG.intake){let a=intakeHours(e,t);return{name:`intake`,class:`new`,urgency:a,multiplier:n.eligibleNew>0?r.rate**+(a/r.scaleHours):1,applied:n.eligibleNew,inputs:{hoursSinceNew:a,frozenHours:frozenMsAt(e,t)/36e5,eligibleNew:n.eligibleNew,newCandidates:n.newCandidates}}}function applyReading(e,t){if(t.multiplier===1)return;let n=t.name===`review-mass`?`review mass ${t.urgency.toFixed(1)} (healthy ${t.inputs.healthyMass}) \u2192 \xD7${t.multiplier.toFixed(2)}`:`${t.urgency.toFixed(1)}h since a new card \u2192 \xD7${t.multiplier.toFixed(2)}`;for(let r of e)r.score*=t.multiplier,r.provenance.push({strategy:`regulator`,strategyName:`Regulator: ${REGULATOR_LABEL[t.name]}`,strategyId:`regulator-${t.name}`,action:t.multiplier>1?`boosted`:`penalized`,score:r.score,reason:n})}var DEFAULT_REGULATOR_CONFIG,INTAKE_CLOCK_KEY,REGULATOR_LABEL,init_regulators=__esm({"src/core/navigators/regulators.ts"(){DEFAULT_REGULATOR_CONFIG={reviewMass:{referenceScore:.95,healthyMass:20,rate:2},intake:{rate:2,scaleHours:24}},INTAKE_CLOCK_KEY=`IntakeClock`,REGULATOR_LABEL={"review-mass":`Review mass`,intake:`Intake`}}}),srs_exports={};__export(srs_exports,{default:()=>SRSNavigator});var SRSNavigator,init_srs=__esm({"src/core/navigators/generators/srs.ts"(){init_navigators(),init_SrsDebugger(),init_regulators(),init_logger(),SRSNavigator=class extends ContentNavigator{constructor(e,t,n){super(e,t,n),_defineProperty(this,`name`,void 0),this.name=n?.name||`SRS`}async getWeightedCards(e,t){if(!this.user||!this.course)throw Error(`SRSNavigator requires user and course to be set`);let n=this.course.getCourseID(),r=await this.user.getPendingReviews(n),a=hooks.utc(),o=r.filter(e=>a.isAfter(hooks.utc(e.reviewTime)));if(o.length>0){let e=[...new Set(o.map(e=>e.cardId))],t=await this.course.getAppliedTagsBatch(e),n=[];if(o=o.filter(e=>(t.get(e.cardId)??[]).includes(`srs:skip`)?(n.push(e._id),!1):!0),n.length>0){logger.info(`[SRS] Removing ${n.length} scheduled reviews for srs:skip cards`);for(let e of n)this.user.removeScheduledCardReview(e)}}let s=r.filter(e=>!a.isAfter(hooks.utc(e.reviewTime))),c=null;if(s.length>0){let e=s.reduce((e,t)=>hooks.utc(e.reviewTime).isBefore(hooks.utc(t.reviewTime))?e:t),t=hooks.duration(hooks.utc(e.reviewTime).diff(a));c=t.asHours()<1?`${Math.round(t.asMinutes())}m`:t.asHours()<24?`${Math.round(t.asHours())}h`:`${Math.round(t.asDays())}d`}if(o.length>0)logger.info(`[SRS] Course ${n}: ${o.length} reviews due now (of ${r.length} scheduled)`);else if(r.length>0){let e=[...r].sort((e,t)=>hooks.utc(e.reviewTime).diff(hooks.utc(t.reviewTime)))[0],t=hooks.utc(e.reviewTime),o=hooks.duration(t.diff(a)),s=o.asHours()<1?`${Math.round(o.asMinutes())}m`:o.asHours()<24?`${Math.round(o.asHours())}h`:`${Math.round(o.asDays())}d`;logger.info(`[SRS] Course ${n}: 0 reviews due now (${r.length} scheduled, next in ${s})`)}else logger.info(`[SRS] Course ${n}: No reviews scheduled`);let l=o.map(e=>{let{score:t,urgency:n,reason:r}=this.computeUrgencyScore(e,a);return{cardId:e.cardId,courseId:e.courseId,score:t,reviewUrgency:n,reviewID:e._id,provenance:[{strategy:`srs`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||`NAVIGATION_STRATEGY-SRS-default`,action:`generated`,score:t,reason:r}]}}).sort((e,t)=>t.score-e.score);return captureSrsBacklog({courseId:n,scheduledTotal:r.length,dueNow:o.length,healthyBacklog:DEFAULT_REGULATOR_CONFIG.reviewMass.healthyMass,reviewMass:null,backlogMultiplier:1,backlogGrowthRate:DEFAULT_REGULATOR_CONFIG.reviewMass.rate,topReviewScore:l.length>0?l[0].score:null,nextDueIn:c,timestamp:Date.now()}),{cards:l.slice(0,e)}}computeUrgencyScore(e,t){let n=hooks.utc(e.scheduledAt),r=hooks.utc(e.reviewTime),a=Math.max(1,r.diff(n,`hours`)),o=t.diff(r,`hours`),s=o/a,c=.3+.7*Math.exp(-a/720),l=Math.min(1,Math.max(0,s))*.5+c*.5,u=.5+l*.45,d=[`${Math.round(o)}h overdue`,`interval: ${Math.round(a)}h`,`relative: ${s.toFixed(2)}`,`recency: ${c.toFixed(2)}`];return d.push(`review`),{score:u,urgency:l,reason:d.join(`, `)}}}}}),types_exports={},init_types=__esm({"src/core/navigators/generators/types.ts"(){}}),globImport_generators,init_=__esm({'import("./generators/**/*") in src/core/navigators/index.ts'(){globImport_generators=__glob({"./generators/CompositeGenerator.ts":()=>Promise.resolve().then(()=>(init_CompositeGenerator(),CompositeGenerator_exports)),"./generators/elo.ts":()=>Promise.resolve().then(()=>(init_elo(),elo_exports)),"./generators/index.ts":()=>Promise.resolve().then(()=>(init_generators(),generators_exports)),"./generators/prescribed.ts":()=>Promise.resolve().then(()=>(init_prescribed(),prescribed_exports)),"./generators/srs.ts":()=>Promise.resolve().then(()=>(init_srs(),srs_exports)),"./generators/types.ts":()=>Promise.resolve().then(()=>(init_types(),types_exports))})}}),DEFAULT_LEARNABLE_WEIGHT,init_contentNavigationStrategy=__esm({"src/core/types/contentNavigationStrategy.ts"(){DEFAULT_LEARNABLE_WEIGHT={weight:1,confidence:.1,sampleSize:0}}}),WeightedFilter_exports={};__export(WeightedFilter_exports,{WeightedFilter:()=>WeightedFilter});var WeightedFilter,init_WeightedFilter=__esm({"src/core/navigators/filters/WeightedFilter.ts"(){init_contentNavigationStrategy(),WeightedFilter=class{constructor(e,t=DEFAULT_LEARNABLE_WEIGHT,n=!1,r){_defineProperty(this,`name`,void 0),_defineProperty(this,`kind`,void 0),_defineProperty(this,`liveOnly`,void 0),_defineProperty(this,`inner`,void 0),_defineProperty(this,`learnable`,void 0),_defineProperty(this,`staticWeight`,void 0),_defineProperty(this,`strategyId`,void 0),this.inner=e,this.name=e.name,this.kind=e.kind,this.liveOnly=e.liveOnly,this.learnable=t,this.staticWeight=n,this.strategyId=r}async transform(e,t){let n=this.learnable.weight,r;if(!this.staticWeight&&t.orchestration){let e=this.strategyId||this.inner.strategyId||this.name;n=t.orchestration.getEffectiveWeight(e,this.learnable),r=t.orchestration.getDeviation(e)}if(Math.abs(n-1)<.001)return this.inner.transform(e,t);let a=new Map;for(let t of e)a.set(t.cardId,t.score);return(await this.inner.transform(e,t)).map(e=>{let t=a.get(e.cardId);if(t===void 0||t===0||e.score===0)return e;let o=e.score/t;if(Math.abs(o-1)<1e-4)return e;let s=t*o**+n,c=e.provenance.length-1,l=e.provenance[c];if(l){let t=[...e.provenance];return t[c]={...l,score:s,effectiveWeight:n,deviation:r},{...e,score:s,provenance:t}}return{...e,score:s}})}}}}),eloDistance_exports={};__export(eloDistance_exports,{DEFAULT_ELO_DISTANCE_CONFIG:()=>DEFAULT_ELO_DISTANCE_CONFIG,createEloDistanceFilter:()=>createEloDistanceFilter,default:()=>EloDistanceFilter,mountMultiDimEloToggle:()=>mountMultiDimEloToggle,tagAwareGap:()=>tagAwareGap});function computeMultiplier(e,t,n,r){let a=e/t,o=Math.exp(-(a*a));return n+(r-n)*o}function resolveConfig(e){return{...DEFAULT_ELO_DISTANCE_CONFIG,...e}}function readMultiDimElo(){try{return typeof localStorage<`u`&&localStorage.getItem(MULTI_DIM_ELO_KEY)===`true`}catch{return!1}}function mountMultiDimEloToggle(){if(typeof window>`u`)return;let e=window;e.skuilder=e.skuilder||{},Object.defineProperty(e.skuilder,`multiDimElo`,{configurable:!0,enumerable:!0,get:()=>multiDimElo,set:e=>{multiDimElo=e===!0;try{multiDimElo?localStorage.setItem(MULTI_DIM_ELO_KEY,`true`):localStorage.removeItem(MULTI_DIM_ELO_KEY)}catch(e){logger.warn(`[EloDistance] multiDimElo not persisted (localStorage unavailable): ${e}`)}logger.info(`[EloDistance] multiDimElo ${multiDimElo?`on: tag-aware`:`off: per strategy config`} ELO distance from the next replan`)}})}function tagAwareGap(e,t,n,r){let a=t.global.score-n.global.score,o=[];for(let a of e){let e=n.tags[a];if(!e||e.score===COUNT_ONLY_SENTINEL||e.count<r)continue;let s=t.tags[a]?.score??t.global.score;o.push({tag:a,gap:s-e.score,weight:e.count/(e.count+TAG_CONFIDENCE_HALF)})}if(o.length===0)return{gap:a,globalGap:a,tags:o};let s=o.reduce((e,t)=>e+t.weight,0),c=o.reduce((e,t)=>e+t.weight*t.gap,0)/s,l=s/(s+1);return{gap:l*c+(1-l)*a,globalGap:a,tags:o}}function tagReason(e,t){let n=[...e].sort((e,t)=>Math.abs(t.gap)*t.weight-Math.abs(e.gap)*e.weight).slice(0,3).map(e=>`${e.tag} ${e.gap>=0?`+`:``}${Math.round(e.gap)}`).join(`, `),r=e.length>3?`, +${e.length-3} more`:``;return` on ${e.length} shared tag(s) [${n}${r}] (global ${t>=0?`+`:``}${Math.round(t)})`}async function applyEloDistance(e,t,n,r){let{course:a,userElo:o,userCourseElo:s}=t,{halfLife:c,minMultiplier:l,maxMultiplier:u}=n,d=(n.tagAware||multiDimElo)&&s!==void 0,p=e.filter(e=>d||e.cardElo===void 0).map(e=>e.cardId),m=new Map;if(p.length>0){let e=await a.getCardEloData(p);p.forEach((t,n)=>{e[n]&&m.set(t,e[n])})}return e.map(e=>{let t=m.get(e.cardId),a=t?.global?.score??e.cardElo??DEFAULT_CARD_ELO,p=Math.abs(a-o),g=``;if(d){let r=t?tagAwareGap(e.tags??[],t,s,n.minTagCount):void 0;r&&(p=Math.abs(r.gap)),g=r?.tags.length?`, tag-aware${tagReason(r.tags,r.globalGap)}`:`, tag-aware: global (no shared tags)`}let _=computeMultiplier(p,c,l,u),v=e.score*_,y=_<u-.01?`penalized`:`passed`;return{...e,cardElo:a,score:v,provenance:[...e.provenance,{strategy:`eloDistance`,strategyName:r.name,strategyId:r.strategyId,action:y,score:v,reason:`ELO distance ${Math.round(p)} (card: ${Math.round(a)}, user: ${Math.round(o)})${g} \u2192 ${_.toFixed(2)}x`}]}})}function createEloDistanceFilter(e){let t=resolveConfig(e),n={name:`ELO Distance Filter`,strategyId:`ELO_DISTANCE_FILTER`};return{name:n.name,kind:`signal`,liveOnly:!0,transform:(e,r)=>applyEloDistance(e,r,t,n)}}var DEFAULT_ELO_DISTANCE_CONFIG,TAG_CONFIDENCE_HALF,COUNT_ONLY_SENTINEL,DEFAULT_CARD_ELO,MULTI_DIM_ELO_KEY,multiDimElo,EloDistanceFilter,init_eloDistance=__esm({"src/core/navigators/filters/eloDistance.ts"(){var e;init_navigators(),init_logger(),DEFAULT_ELO_DISTANCE_CONFIG={halfLife:300,minMultiplier:.05,maxMultiplier:1,tagAware:!1,minTagCount:3},TAG_CONFIDENCE_HALF=10,COUNT_ONLY_SENTINEL=-1,DEFAULT_CARD_ELO=1e3,MULTI_DIM_ELO_KEY=`skuilder:multiDimElo`,multiDimElo=readMultiDimElo(),mountMultiDimEloToggle(),EloDistanceFilter=(e=class _EloDistanceFilter extends ContentNavigator{constructor(e,t,n){super(e,t,n),_defineProperty(this,`name`,void 0),_defineProperty(this,`kind`,_EloDistanceFilter.kind),_defineProperty(this,`liveOnly`,!0),_defineProperty(this,`config`,void 0),this.name=n.name||`ELO Distance`;let r;try{r=n.serializedData?JSON.parse(n.serializedData):void 0}catch{r=void 0}this.config=resolveConfig(r)}transform(e,t){return applyEloDistance(e,t,this.config,{name:this.name,strategyId:this.strategyId||`NAVIGATION_STRATEGY-eloDistance-default`})}async getWeightedCards(e){throw Error(`EloDistanceFilter is a filter and should not be used as a generator. Use Pipeline with a generator and this filter via transform().`)}},_defineProperty(e,`kind`,`signal`),e)}}),hierarchyDefinition_exports={};__export(hierarchyDefinition_exports,{default:()=>HierarchyDefinitionNavigator});var DEFAULT_MIN_COUNT2,HierarchyDefinitionNavigator,init_hierarchyDefinition=__esm({"src/core/navigators/filters/hierarchyDefinition.ts"(){var e;init_navigators(),init_logger(),DEFAULT_MIN_COUNT2=3,HierarchyDefinitionNavigator=(e=class _HierarchyDefinitionNavigator extends ContentNavigator{constructor(e,t,n){super(e,t,n),_defineProperty(this,`config`,void 0),_defineProperty(this,`name`,void 0),_defineProperty(this,`kind`,_HierarchyDefinitionNavigator.kind),this.config=this.parseConfig(n.serializedData),this.name=n.name||`Hierarchy Definition`}parseConfig(e){try{return{prerequisites:JSON.parse(e).prerequisites||{}}}catch{return{prerequisites:{}}}}isPrerequisiteMet(e,t,n){if(!t)return!1;let r=e.masteryThreshold?.minCount??DEFAULT_MIN_COUNT2;return t.count<r?!1:e.masteryThreshold?.minElo===void 0?e.masteryThreshold?.minCount===void 0?t.score>=n:!0:t.score>=e.masteryThreshold.minElo}async getMasteredTags(e){let t=new Set;try{let n=toCourseElo((await e.user.getCourseRegDoc(e.course.getCourseID())).elo);for(let e of Object.values(this.config.prerequisites))for(let r of e){let e=n.tags[r.tag];this.isPrerequisiteMet(r,e,n.global.score)&&t.add(r.tag)}}catch{}return t}getUnlockedTags(e){let t=new Set;for(let[n,r]of Object.entries(this.config.prerequisites))r.every(t=>e.has(t.tag))&&t.add(n);return t}hasPrerequisites(e){return e in this.config.prerequisites}async checkCardUnlock(e,t,n,r){try{let t=e.tags??[],a=t.filter(e=>this.hasPrerequisites(e)&&!n.has(e));return a.length===0?{isUnlocked:!0,reason:`Prerequisites met, tags: ${t.length>0?t.join(`, `):`none`}`}:{isUnlocked:!1,reason:`Blocked: missing prerequisites ${a.flatMap(e=>(this.config.prerequisites[e]||[]).filter(e=>!r.has(e.tag)).map(e=>e.tag)).join(`, `)} for tags ${a.join(`, `)}`}}catch{return{isUnlocked:!0,reason:`Prerequisites check skipped (tag lookup failed)`}}}getPreReqBoosts(e,t){let n=new Map;for(let[r,a]of Object.entries(this.config.prerequisites))if(!e.has(r))for(let e of a){if(!e.preReqBoost||e.preReqBoost<=1||t.has(e.tag))continue;let r=n.get(e.tag)??1;n.set(e.tag,Math.max(r,e.preReqBoost))}return n}getTargetBoosts(e){let t=new Map,n=Object.keys(this.config.prerequisites),r=[...e];logger.info(`[HierarchyDefinition:targetBoost:trace] ${this.name} | configKeys=${n.length}, unlocked=${r.length} (${r.slice(0,5).join(`, `)}${r.length>5?`...`:``})`);for(let[n,r]of Object.entries(this.config.prerequisites))if(e.has(n)){logger.info(`[HierarchyDefinition:targetBoost:trace] UNLOCKED ${n}: ${r.length} prereqs, raw=${JSON.stringify(r.map(e=>({tag:e.tag,tb:e.targetBoost})))}`);for(let e of r){if(!e.targetBoost||e.targetBoost<=1)continue;let r=t.get(n)??1;t.set(n,Math.max(r,e.targetBoost))}}return t.size>0?logger.info(`[HierarchyDefinition] targetBoosts active: ${[...t.entries()].map(([e,t])=>`${e}=\xD7${t}`).join(`, `)}`):logger.info(`[HierarchyDefinition:targetBoost:trace] no targetBoosts found despite ${r.length} unlocked tags`),t}async transform(e,t){let n=await this.getMasteredTags(t),r=this.getUnlockedTags(n),a=this.getPreReqBoosts(r,n),o=this.getTargetBoosts(r),s=[];for(let c of e){let{isUnlocked:e,reason:l}=await this.checkCardUnlock(c,t.course,r,n),u=e?c.score:c.score*.02,d=e?`passed`:`penalized`,p=l;if(e&&a.size>0){let e=c.tags??[],t=1,n=[];for(let r of e){let e=a.get(r);e&&e>t&&(t=e,n.push(r))}t>1&&(u*=t,d=`boosted`,p=`${l} | preReqBoost \xD7${t.toFixed(2)} for ${n.join(`, `)}`,logger.info(`[HierarchyDefinition] preReqBoost \xD7${t.toFixed(2)} applied to card ${c.cardId} via tags [${n.join(`, `)}] (score: ${c.score.toFixed(3)} \u2192 ${u.toFixed(3)})`))}if(e&&o.size>0){let e=c.tags??[],t=1,n=[];for(let r of e){let e=o.get(r);e&&e>t&&(t=e,n.push(r))}t>1&&(u*=t,d=`boosted`,p=`${p} | targetBoost \xD7${t.toFixed(2)} for ${n.join(`, `)}`,logger.info(`[HierarchyDefinition] targetBoost \xD7${t.toFixed(2)} applied to card ${c.cardId} via tags [${n.join(`, `)}] (score: ${c.score.toFixed(3)} \u2192 ${u.toFixed(3)})`))}s.push({...c,score:u,provenance:[...c.provenance,{strategy:`hierarchyDefinition`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||`NAVIGATION_STRATEGY-hierarchy`,action:d,score:u,reason:p}]})}return s}async getWeightedCards(e){throw Error(`HierarchyDefinitionNavigator is a filter and should not be used as a generator. Use Pipeline with a generator and this filter via transform().`)}},_defineProperty(e,`kind`,`gate`),e)}}),userTagPreference_exports={};__export(userTagPreference_exports,{default:()=>UserTagPreferenceFilter});var UserTagPreferenceFilter,init_userTagPreference=__esm({"src/core/navigators/filters/userTagPreference.ts"(){var e;init_navigators(),UserTagPreferenceFilter=(e=class _UserTagPreferenceFilter extends ContentNavigator{constructor(e,t,n){super(e,t,n),_defineProperty(this,`_strategyData`,void 0),_defineProperty(this,`name`,void 0),_defineProperty(this,`kind`,_UserTagPreferenceFilter.kind),this._strategyData=n,this.name=n.name||`User Tag Preferences`}computeMultiplier(e,t){let n=e.map(e=>t[e]).filter(e=>e!==void 0);return n.length===0?1:Math.max(...n)}buildReason(e,t,n){let r=e.filter(e=>t[e]===n);return n===0?`Excluded by user preference: ${r.join(`, `)} (${n}x)`:n<1?`Penalized by user preference: ${r.join(`, `)} (${n.toFixed(2)}x)`:n>1?`Boosted by user preference: ${r.join(`, `)} (${n.toFixed(2)}x)`:`No matching user preferences`}async transform(e,t){let n=await this.getStrategyState();return!n||Object.keys(n.boost).length===0?e.map(e=>({...e,provenance:[...e.provenance,{strategy:`userTagPreference`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||this._strategyData._id,action:`passed`,score:e.score,reason:`No user tag preferences configured`}]})):await Promise.all(e.map(async e=>{let t=e.tags??[],r=this.computeMultiplier(t,n.boost),a=Math.min(1,e.score*r),o;return o=r===0||r<1?`penalized`:r>1?`boosted`:`passed`,{...e,score:a,provenance:[...e.provenance,{strategy:`userTagPreference`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||this._strategyData._id,action:o,score:a,reason:this.buildReason(t,n.boost,r)}]}}))}async getWeightedCards(e){throw Error(`UserTagPreferenceFilter is a filter and should not be used as a generator. Use Pipeline with a generator and this filter via transform().`)}},_defineProperty(e,`kind`,`signal`),e)}}),filters_exports={};__export(filters_exports,{EloDistanceFilter:()=>EloDistanceFilter,UserTagPreferenceFilter:()=>UserTagPreferenceFilter,createEloDistanceFilter:()=>createEloDistanceFilter});var init_filters=__esm({"src/core/navigators/filters/index.ts"(){init_eloDistance(),init_userTagPreference()}}),inferredPreferenceStub_exports={};__export(inferredPreferenceStub_exports,{INFERRED_PREFERENCE_NAVIGATOR_STUB:()=>INFERRED_PREFERENCE_NAVIGATOR_STUB});var INFERRED_PREFERENCE_NAVIGATOR_STUB,init_inferredPreferenceStub=__esm({"src/core/navigators/filters/inferredPreferenceStub.ts"(){INFERRED_PREFERENCE_NAVIGATOR_STUB=!0}}),interferenceMitigator_exports={};__export(interferenceMitigator_exports,{default:()=>InterferenceMitigatorNavigator});var DEFAULT_MIN_COUNT3,DEFAULT_MIN_ELAPSED_DAYS,DEFAULT_INTERFERENCE_DECAY,InterferenceMitigatorNavigator,init_interferenceMitigator=__esm({"src/core/navigators/filters/interferenceMitigator.ts"(){var e;init_navigators(),DEFAULT_MIN_COUNT3=10,DEFAULT_MIN_ELAPSED_DAYS=3,DEFAULT_INTERFERENCE_DECAY=.8,InterferenceMitigatorNavigator=(e=class _InterferenceMitigatorNavigator extends ContentNavigator{constructor(e,t,n){super(e,t,n),_defineProperty(this,`config`,void 0),_defineProperty(this,`name`,void 0),_defineProperty(this,`kind`,_InterferenceMitigatorNavigator.kind),_defineProperty(this,`interferenceMap`,void 0),this.config=this.parseConfig(n.serializedData),this.interferenceMap=this.buildInterferenceMap(),this.name=n.name||`Interference Mitigator`}parseConfig(e){try{let t=JSON.parse(e),n=t.interferenceSets||[];return n.length>0&&Array.isArray(n[0])&&(n=n.map(e=>({tags:e}))),{interferenceSets:n,maturityThreshold:{minCount:t.maturityThreshold?.minCount??DEFAULT_MIN_COUNT3,minElo:t.maturityThreshold?.minElo,minElapsedDays:t.maturityThreshold?.minElapsedDays??DEFAULT_MIN_ELAPSED_DAYS},defaultDecay:t.defaultDecay??DEFAULT_INTERFERENCE_DECAY}}catch{return{interferenceSets:[],maturityThreshold:{minCount:DEFAULT_MIN_COUNT3,minElapsedDays:DEFAULT_MIN_ELAPSED_DAYS},defaultDecay:DEFAULT_INTERFERENCE_DECAY}}}buildInterferenceMap(){let e=new Map;for(let t of this.config.interferenceSets){let n=t.decay??this.config.defaultDecay??DEFAULT_INTERFERENCE_DECAY;for(let r of t.tags){e.has(r)||e.set(r,[]);let a=e.get(r);for(let e of t.tags)if(e!==r){let t=a.find(t=>t.partner===e);t?t.decay=Math.max(t.decay,n):a.push({partner:e,decay:n})}}}return e}async getImmatureTags(e){let t=new Set;try{let n=toCourseElo((await e.user.getCourseRegDoc(e.course.getCourseID())).elo),r=this.config.maturityThreshold?.minCount??DEFAULT_MIN_COUNT3,a=this.config.maturityThreshold?.minElo,o=(this.config.maturityThreshold?.minElapsedDays??DEFAULT_MIN_ELAPSED_DAYS)*2;for(let[e,s]of Object.entries(n.tags)){if(s.count===0)continue;let n=s.count<r,c=a!==void 0&&s.score<a,l=s.count<o;(n||c||l)&&t.add(e)}}catch{}return t}getTagsToAvoid(e){let t=new Map;for(let n of e){let r=this.interferenceMap.get(n);if(r){for(let{partner:n,decay:a}of r)if(!e.has(n)){let e=t.get(n)??0;t.set(n,Math.max(e,a))}}}return t}computeInterferenceEffect(e,t,n){if(t.size===0)return{multiplier:1,interferingTags:[],reason:`No interference detected`};let r=1,a=[];for(let n of e){let e=t.get(n);e!==void 0&&(a.push(n),r*=1-e)}if(a.length===0)return{multiplier:1,interferingTags:[],reason:`No interference detected`};let o=new Set;for(let e of a)for(let t of n)this.interferenceMap.get(t)?.some(t=>t.partner===e)&&o.add(t);let s=`Interferes with immature tags ${Array.from(o).join(`, `)} (tags: ${a.join(`, `)}, multiplier: ${r.toFixed(2)})`;return{multiplier:r,interferingTags:a,reason:s}}async transform(e,t){let n=await this.getImmatureTags(t),r=this.getTagsToAvoid(n),a=[];for(let t of e){let e=t.tags??[],{multiplier:o,reason:s}=this.computeInterferenceEffect(e,r,n),c=t.score*o,l=o<1?`penalized`:o>1?`boosted`:`passed`;a.push({...t,score:c,provenance:[...t.provenance,{strategy:`interferenceMitigator`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||`NAVIGATION_STRATEGY-interference`,action:l,score:c,reason:s}]})}return a}async getWeightedCards(e){throw Error(`InterferenceMitigatorNavigator is a filter and should not be used as a generator. Use Pipeline with a generator and this filter via transform().`)}},_defineProperty(e,`kind`,`signal`),e)}}),relativePriority_exports={};__export(relativePriority_exports,{default:()=>RelativePriorityNavigator});var DEFAULT_PRIORITY,DEFAULT_PRIORITY_INFLUENCE,DEFAULT_COMBINE_MODE,RelativePriorityNavigator,init_relativePriority=__esm({"src/core/navigators/filters/relativePriority.ts"(){var e;init_navigators(),DEFAULT_PRIORITY=.5,DEFAULT_PRIORITY_INFLUENCE=.5,DEFAULT_COMBINE_MODE=`max`,RelativePriorityNavigator=(e=class _RelativePriorityNavigator extends ContentNavigator{constructor(e,t,n){super(e,t,n),_defineProperty(this,`config`,void 0),_defineProperty(this,`name`,void 0),_defineProperty(this,`kind`,_RelativePriorityNavigator.kind),this.config=this.parseConfig(n.serializedData),this.name=n.name||`Relative Priority`}parseConfig(e){try{let t=JSON.parse(e);return{tagPriorities:t.tagPriorities||{},defaultPriority:t.defaultPriority??DEFAULT_PRIORITY,combineMode:t.combineMode??DEFAULT_COMBINE_MODE,priorityInfluence:t.priorityInfluence??DEFAULT_PRIORITY_INFLUENCE}}catch{return{tagPriorities:{},defaultPriority:DEFAULT_PRIORITY,combineMode:DEFAULT_COMBINE_MODE,priorityInfluence:DEFAULT_PRIORITY_INFLUENCE}}}getTagPriority(e){return this.config.tagPriorities[e]??this.config.defaultPriority??DEFAULT_PRIORITY}computeCardPriority(e){if(e.length===0)return this.config.defaultPriority??DEFAULT_PRIORITY;let t=e.map(e=>this.getTagPriority(e));switch(this.config.combineMode){case`max`:return Math.max(...t);case`min`:return Math.min(...t);case`average`:return t.reduce((e,t)=>e+t,0)/t.length;default:return Math.max(...t)}}computeBoostFactor(e){let t=this.config.priorityInfluence??DEFAULT_PRIORITY_INFLUENCE;return 1+(e-.5)*t}buildPriorityReason(e,t,n,r){if(e.length===0)return`No tags, neutral priority (${t.toFixed(2)})`;let a=e.slice(0,3).join(`, `),o=e.length>3?` (+${e.length-3} more)`:``;return n===1?`Neutral priority (${t.toFixed(2)}) for tags: ${a}${o}`:n>1?`High-priority tags: ${a}${o} (priority ${t.toFixed(2)} \u2192 boost ${n.toFixed(2)}x \u2192 ${r.toFixed(2)})`:`Low-priority tags: ${a}${o} (priority ${t.toFixed(2)} \u2192 reduce ${n.toFixed(2)}x \u2192 ${r.toFixed(2)})`}async transform(e,t){return await Promise.all(e.map(async e=>{let t=e.tags??[],n=this.computeCardPriority(t),r=this.computeBoostFactor(n),a=Math.max(0,e.score*r),o=r>1?`boosted`:r<1?`penalized`:`passed`,s=this.buildPriorityReason(t,n,r,a);return{...e,score:a,provenance:[...e.provenance,{strategy:`relativePriority`,strategyName:this.strategyName||this.name,strategyId:this.strategyId||`NAVIGATION_STRATEGY-priority`,action:o,score:a,reason:s}]}}))}async getWeightedCards(e){throw Error(`RelativePriorityNavigator is a filter and should not be used as a generator. Use Pipeline with a generator and this filter via transform().`)}},_defineProperty(e,`kind`,`signal`),e)}}),types_exports2={},init_types2=__esm({"src/core/navigators/filters/types.ts"(){}}),userGoalStub_exports={};__export(userGoalStub_exports,{USER_GOAL_NAVIGATOR_STUB:()=>USER_GOAL_NAVIGATOR_STUB});var USER_GOAL_NAVIGATOR_STUB,init_userGoalStub=__esm({"src/core/navigators/filters/userGoalStub.ts"(){USER_GOAL_NAVIGATOR_STUB=!0}}),globImport_filters,init_2=__esm({'import("./filters/**/*") in src/core/navigators/index.ts'(){globImport_filters=__glob({"./filters/WeightedFilter.ts":()=>Promise.resolve().then(()=>(init_WeightedFilter(),WeightedFilter_exports)),"./filters/eloDistance.ts":()=>Promise.resolve().then(()=>(init_eloDistance(),eloDistance_exports)),"./filters/hierarchyDefinition.ts":()=>Promise.resolve().then(()=>(init_hierarchyDefinition(),hierarchyDefinition_exports)),"./filters/index.ts":()=>Promise.resolve().then(()=>(init_filters(),filters_exports)),"./filters/inferredPreferenceStub.ts":()=>Promise.resolve().then(()=>(init_inferredPreferenceStub(),inferredPreferenceStub_exports)),"./filters/interferenceMitigator.ts":()=>Promise.resolve().then(()=>(init_interferenceMitigator(),interferenceMitigator_exports)),"./filters/relativePriority.ts":()=>Promise.resolve().then(()=>(init_relativePriority(),relativePriority_exports)),"./filters/types.ts":()=>Promise.resolve().then(()=>(init_types2(),types_exports2)),"./filters/userGoalStub.ts":()=>Promise.resolve().then(()=>(init_userGoalStub(),userGoalStub_exports)),"./filters/userTagPreference.ts":()=>Promise.resolve().then(()=>(init_userTagPreference(),userTagPreference_exports))})}});function aggregateOutcomesForGradient(e,t){let n=[];for(let r of e){let e=r.deviations[t];e!==void 0&&n.push({deviation:e,outcomeValue:r.outcomeValue,weight:1})}return logger.debug(`[Orchestration] Aggregated ${n.length} observations for strategy ${t}`),n}function computeStrategyGradient(e){let t=e.length;if(t<3)return logger.debug(`[Orchestration] Insufficient observations for gradient (${t} < 3)`),null;let n=0,r=0,a=0;for(let t of e){let e=t.weight??1;n+=t.deviation*e,r+=t.outcomeValue*e,a+=e}let o=n/a,s=r/a,c=0,l=0,u=0;for(let t of e){let e=t.weight??1,n=t.deviation-o,r=t.outcomeValue-s;c+=e*n*r,l+=e*n*n,u+=e*r*r}if(l<1e-10)return logger.debug(`[Orchestration] No variance in deviations, cannot compute gradient`),{gradient:0,intercept:s,rSquared:0,sampleSize:t};let d=c/l,p=s-d*o,m=0;for(let t of e){let e=t.weight??1,n=d*t.deviation+p,r=t.outcomeValue-n;m+=e*r*r}let g=u>1e-10?1-m/u:0;return logger.debug(`[Orchestration] Computed gradient: ${d.toFixed(4)}, intercept: ${p.toFixed(4)}, R\xB2: ${g.toFixed(4)}, n=${t}`),{gradient:d,intercept:p,rSquared:Math.max(0,Math.min(1,g)),sampleSize:t}}var init_gradient=__esm({"src/core/orchestration/gradient.ts"(){init_logger()}});function updateStrategyWeight(e,t){if(t.sampleSize<MIN_OBSERVATIONS_FOR_UPDATE)return logger.debug(`[Orchestration] Insufficient samples (${t.sampleSize} < ${MIN_OBSERVATIONS_FOR_UPDATE}), keeping current weight`),{...e,sampleSize:e.sampleSize+t.sampleSize};let n=t.rSquared>=MIN_R_SQUARED_FOR_GRADIENT,r=Math.abs(t.gradient)<FLAT_GRADIENT_THRESHOLD,a=e.weight,o=e.confidence;if(!n||r){let n=.05*(1-e.confidence);o=Math.min(1,e.confidence+n),logger.debug(`[Orchestration] Flat/unreliable gradient (|g|=${Math.abs(t.gradient).toFixed(4)}, R\xB2=${t.rSquared.toFixed(4)}). Increasing confidence: ${e.confidence.toFixed(3)} \u2192 ${o.toFixed(3)}`)}else{let n=t.gradient*LEARNING_RATE;n=Math.max(-MAX_WEIGHT_DELTA,Math.min(MAX_WEIGHT_DELTA,n)),a=e.weight+n,a=Math.max(.1,Math.min(3,a));let r=.02*(1-e.confidence);o=Math.min(1,e.confidence+r),logger.debug(`[Orchestration] Adjusting weight: ${e.weight.toFixed(3)} \u2192 ${a.toFixed(3)} (gradient=${t.gradient.toFixed(4)}, delta=${n.toFixed(4)})`)}return{weight:a,confidence:o,sampleSize:e.sampleSize+t.sampleSize}}function updateLearningState(e,t,n,r,a){let o=new Date().toISOString(),s=`STRATEGY_LEARNING_STATE::${e}::${t}`,c={timestamp:o,weight:n.weight,confidence:n.confidence,gradient:r.gradient},l=a?.history??[];return l=[...l,c],l.length>MAX_HISTORY_LENGTH&&(l=l.slice(l.length-MAX_HISTORY_LENGTH)),{_id:s,_rev:a?._rev,docType:`STRATEGY_LEARNING_STATE`,courseId:e,strategyId:t,currentWeight:n,regression:{gradient:r.gradient,intercept:r.intercept,rSquared:r.rSquared,sampleSize:r.sampleSize,computedAt:o},history:l,updatedAt:o}}function runPeriodUpdate(e){let{courseId:t,strategyId:n,currentWeight:r,gradient:a,existingState:o}=e;logger.info(`[Orchestration] Running period update for strategy ${n} (${a.sampleSize} observations)`);let s=updateStrategyWeight(r,a),c=s.weight!==r.weight,l=updateLearningState(t,n,s,a,o);return logger.info(`[Orchestration] Period update complete for ${n}: weight ${r.weight.toFixed(3)} \u2192 ${s.weight.toFixed(3)}, confidence ${r.confidence.toFixed(3)} \u2192 ${s.confidence.toFixed(3)}`),{strategyId:n,previousWeight:r,newWeight:s,gradient:a,learningState:l,updated:c}}function getDefaultLearnableWeight(){return{...DEFAULT_LEARNABLE_WEIGHT}}var MIN_OBSERVATIONS_FOR_UPDATE,LEARNING_RATE,MAX_WEIGHT_DELTA,MIN_R_SQUARED_FOR_GRADIENT,FLAT_GRADIENT_THRESHOLD,MAX_HISTORY_LENGTH,init_learning=__esm({"src/core/orchestration/learning.ts"(){init_contentNavigationStrategy(),init_types_legacy(),init_logger(),MIN_OBSERVATIONS_FOR_UPDATE=10,LEARNING_RATE=.1,MAX_WEIGHT_DELTA=.3,MIN_R_SQUARED_FOR_GRADIENT=.05,FLAT_GRADIENT_THRESHOLD=.02,MAX_HISTORY_LENGTH=100}});function computeOutcomeSignal(e,t={}){if(!e||e.length===0)return null;let n=t.targetAccuracy??.85,r=t.tolerance??.05,a=0;for(let t of e)t.isCorrect&&a++;return scoreAccuracyInZone(a/e.length,n,r)}function scoreAccuracyInZone(e,t,n){let r=Math.abs(e-t);if(r<=n)return 1;let a=r-n;return Math.max(0,1-a*2.5)}var init_signal=__esm({"src/core/orchestration/signal.ts"(){}});async function recordUserOutcome(e,t,n,r,a,o=0,s=0,c){let{user:l,course:u,userId:d}=e,p=u.getCourseID(),m=computeOutcomeSignal(r,c);if(m===null){logger.debug(`[Orchestration] No outcome signal computed for ${d} (insufficient data). Skipping record.`);return}let g={};for(let t of a)g[t]=e.getDeviation(t);let _=`USER_OUTCOME::${p}::${d}::${n}`,v={_id:_,docType:`USER_OUTCOME`,courseId:p,userId:d,periodStart:t,periodEnd:n,outcomeValue:m,deviations:g,metadata:{sessionsCount:1,cardsSeen:r.length,eloStart:o,eloEnd:s,signalType:`accuracy_in_zone`}};try{await l.putUserOutcome(v),logger.debug(`[Orchestration] Recorded outcome ${m.toFixed(3)} for ${d} (doc: ${_})`)}catch(e){logger.error(`[Orchestration] Failed to record outcome: ${e}`)}}var init_recording=__esm({"src/core/orchestration/recording.ts"(){init_signal(),init_types_legacy(),init_logger()}});function fnv1a(e){let t=2166136261;for(let n=0;n<e.length;n++)t^=e.charCodeAt(n),t=Math.imul(t,16777619);return t>>>0}function computeDeviation(e,t,n){return fnv1a(`${e}:${t}:${n}`)/4294967296*2-1}function computeSpread(e){return MAX_SPREAD-Math.max(0,Math.min(1,e))*(MAX_SPREAD-MIN_SPREAD)}function computeEffectiveWeight(e,t,n,r){let a=computeDeviation(t,n,r)*computeSpread(e.confidence)*e.weight,o=e.weight+a;return Math.max(MIN_WEIGHT,Math.min(MAX_WEIGHT,o))}async function createOrchestrationContext(e,t){let n;try{n=await t.getCourseConfig()}catch(e){logger.error(`[Orchestration] Failed to load course config: ${e}`),n={name:`Unknown`,description:``,public:!1,deleted:!1,creator:``,admins:[],moderators:[],dataShapes:[],questionTypes:[],orchestration:{salt:`default`}}}let r=e.getUsername(),a=n.orchestration?.salt||`default_salt`;return{user:e,course:t,userId:r,courseConfig:n,getEffectiveWeight(e,t){return computeEffectiveWeight(t,r,e,a)},getDeviation(e){return computeDeviation(r,e,a)}}}var MIN_SPREAD,MAX_SPREAD,MIN_WEIGHT,MAX_WEIGHT,init_orchestration=__esm({"src/core/orchestration/index.ts"(){init_logger(),init_gradient(),init_learning(),init_signal(),init_recording(),MIN_SPREAD=.1,MAX_SPREAD=.5,MIN_WEIGHT=.1,MAX_WEIGHT=3}}),Pipeline_exports={};__export(Pipeline_exports,{Pipeline:()=>Pipeline,mergeHints:()=>mergeHints2});function globToRegex(e){let t=e.replace(/[.+?^${}()|[\]\\]/g,`\\$&`).replace(/\*/g,`.*`);return RegExp(`^${t}$`)}function globMatch(e,t){return t.includes(`*`)?globToRegex(t).test(e):e===t}function cardMatchesTagPattern(e,t){return(e.tags??[]).some(e=>globMatch(e,t))}function mergeHints2(e){let t=e.filter(e=>e!=null);if(t.length===0)return;let n={},r={};for(let e of t)for(let[t,n]of Object.entries(e.boostTags??{}))r[t]=(r[t]??1)*n;Object.keys(r).length>0&&(n.boostTags=r);let a={};for(let e of t)for(let[t,n]of Object.entries(e.boostCards??{}))a[t]=(a[t]??1)*n;Object.keys(a).length>0&&(n.boostCards=a);let concatUnique=e=>{let r=t.flatMap(t=>t[e]??[]);r.length>0&&(n[e]=[...new Set(r)])};concatUnique(`requireTags`),concatUnique(`requireCards`),concatUnique(`excludeTags`),concatUnique(`excludeCards`);let o=t.map(e=>e._label).filter(Boolean);return o.length>0&&(n._label=o.join(`; `)),Object.keys(n).length>0?n:void 0}function logPipelineConfig(e,t){let n=t.length>0?`
|
|
249
249
|
- `+t.map(e=>e.name).join(`
|
|
250
250
|
- `):` none`;logger.info(`[Pipeline] Configuration:
|
|
251
251
|
Generator: ${e.name}
|
|
@@ -376,4 +376,4 @@ Examples:
|
|
|
376
376
|
window.skuilder.userdb.queryByType('SCHEDULED_CARD', 10)
|
|
377
377
|
window.skuilder.userdb.raw(db => db.allDocs({ limit: 5 }))
|
|
378
378
|
`)}},mountUserDBDebugger()}}),init_core=__esm({"src/core/index.ts"(){init_interfaces(),init_types_legacy(),init_user(),init_strategyState(),init_userOutcome(),init_studySession(),init_hydration(),init_Loggable(),init_util(),init_navigators(),init_bulkImport(),init_orchestration(),init_UserDBDebugger()}});init_core(),init_pipelinePlan(),init_regulators(),init_courseLookupDB(),init_logger();function isDataShapeRegistered(e,t){let n=NameSpacer.getDataShapeString({dataShape:e.name,course:e.course});return t.dataShapes.find(e=>e.name===n)!==void 0}function isDataShapeSchemaAvailable(e,t){let n=NameSpacer.getDataShapeString({dataShape:e.name,course:e.course}),r=t.dataShapes.find(e=>e.name===n);return r!==void 0&&r.serializedZodSchema!==void 0}function isQuestionTypeRegistered(e,t){let n=NameSpacer.getQuestionString({course:e.course,questionType:e.name});return t.questionTypes.some(e=>e.name===n)}function processCustomQuestionsData(e){let t=[],n=[],r=e.courses.map(e=>e.name);return e.questionClasses.forEach(a=>{let o=r.length>0?r[0]:e.meta.packageName;a.dataShapes&&Array.isArray(a.dataShapes)&&a.dataShapes.forEach(e=>{t.push({name:e.name,course:o,dataShape:e})}),n.push({name:a.name,course:o,questionClass:a,dataShapes:a.dataShapes||[],views:a.views||[]})}),{dataShapes:t,questions:n}}function registerDataShape(e,t){let n=NameSpacer.getDataShapeString({dataShape:e.name,course:e.course}),r;try{r=toZodJSON(e.dataShape)}catch(e){logger.warn(`Failed to generate schema for ${n}:`,e),r=void 0}let a=t.dataShapes.findIndex(e=>e.name===n);if(a!==-1){let o=t.dataShapes[a];return o.serializedZodSchema===r?(logger.info(`DataShape '${e.name}' from '${e.course}' already registered with identical schema`),!1):(o.serializedZodSchema?logger.info(`DataShape '${e.name}' from '${e.course}' schema has changed, updating...`):logger.info(`DataShape '${e.name}' from '${e.course}' already registered, but with no schema. Adding schema to existing entry`),t.dataShapes[a]={name:n,questionTypes:o.questionTypes,serializedZodSchema:r},logger.info(`Updated DataShape: ${n}`),!0)}return t.dataShapes.push({name:n,questionTypes:[],serializedZodSchema:r}),logger.info(`Registered DataShape: ${n}`),!0}function registerQuestionType(e,t){if(isQuestionTypeRegistered(e,t))return logger.info(`QuestionType '${e.name}' from '${e.course}' already registered`),!1;let n=NameSpacer.getQuestionString({course:e.course,questionType:e.name}),r=e.views.map(e=>e.name?e.name:`unnamedComponent`),a=e.dataShapes.map(t=>NameSpacer.getDataShapeString({course:e.course,dataShape:t.name}));return t.questionTypes.push({name:n,viewList:r,dataShapeList:a}),e.dataShapes.forEach(r=>{let a=NameSpacer.getDataShapeString({course:e.course,dataShape:r.name});for(let e of t.dataShapes)e.name===a&&e.questionTypes.push(n)}),logger.info(`Registered QuestionType: ${n}`),!0}function removeDataShape(e,t){let n=t.dataShapes.findIndex(t=>t.name===e);return n===-1?(logger.info(`DataShape '${e}' not found in course config`),!1):(t.dataShapes.splice(n,1),t.questionTypes.forEach(t=>{let n=t.dataShapeList.indexOf(e);n!==-1&&t.dataShapeList.splice(n,1)}),logger.info(`Removed DataShape: ${e}`),!0)}function removeQuestionType(e,t){let n=t.questionTypes.findIndex(t=>t.name===e);return n===-1?(logger.info(`QuestionType '${e}' not found in course config`),!1):(t.questionTypes.splice(n,1),t.dataShapes.forEach(t=>{let n=t.questionTypes.indexOf(e);n!==-1&&t.questionTypes.splice(n,1)}),logger.info(`Removed QuestionType: ${e}`),!0)}async function removeCustomQuestionTypes(e,t,n,r){try{logger.info(`Beginning custom question removal`),logger.info(`Removing ${e.length} data shapes and ${t.length} question types`);let a=0;for(let e of t)removeQuestionType(e,n)&&a++;for(let t of e)removeDataShape(t,n)&&a++;logger.info(`Updating course configuration...`);let o=await r.updateCourseConfig(n);if(!o.ok)throw Error(`Failed to update course config: ${JSON.stringify(o)}`);return logger.info(`Custom question removal complete: ${a} items removed`),{success:!0,removedCount:a}}catch(e){let t=e instanceof Error?e.message:String(e);return logger.error(`Custom question removal failed: ${t}`),{success:!1,removedCount:0,errorMessage:t}}}async function registerSeedData(e,t,n){if(e.questionClass.seedData&&Array.isArray(e.questionClass.seedData)){logger.info(`Registering seed data for question: ${e.name}`);try{let r=e.questionClass.seedData.filter(()=>e.dataShapes.length>0).map(r=>t.addNote(e.course,e.dataShapes[0],r,n,[]));await Promise.all(r),logger.info(`Seed data registered for question: ${e.name}`)}catch(t){logger.warn(`Failed to register seed data for question '${e.name}': ${t instanceof Error?t.message:String(t)}`)}}}async function registerBlanksCard(e,t,n,r,a){try{logger.info(`Registering BlanksCard data shapes and question type...`);let o=0,s=`default`;for(let e of t)registerDataShape({name:e.name,course:s,dataShape:e},n)&&o++;let c={name:e.name,course:s,questionClass:{name:e.name,dataShapes:t,views:e.views||[]},dataShapes:t,views:e.views||[]};registerQuestionType(c,n)&&o++,logger.info(`Updating course configuration with BlanksCard...`);let l=await r.updateCourseConfig(n);if(!l.ok)throw Error(`Failed to update course config: ${JSON.stringify(l)}`);return a&&await registerSeedData(c,r,a),logger.info(`BlanksCard registration complete: ${o} items registered`),{success:!0}}catch(e){let t=e instanceof Error?e.message:String(e);return logger.error(`BlanksCard registration failed: ${t}`),{success:!1,errorMessage:t}}}async function registerCustomQuestionTypes(e,t,n,r){try{logger.info(`Beginning custom question registration`),logger.info(`Processing ${e.questionClasses.length} question classes`);let{dataShapes:a,questions:o}=processCustomQuestionsData(e);logger.info(`Found ${a.length} data shapes and ${o.length} questions`);let s=0;logger.info(`Registering data shapes...`);for(let e of a)registerDataShape(e,t)&&s++;logger.info(`Registering question types...`);for(let e of o)registerQuestionType(e,t)&&s++;logger.info(`Updating course configuration...`);let c=await n.updateCourseConfig(t);if(!c.ok)throw Error(`Failed to update course config: ${JSON.stringify(c)}`);if(r){logger.info(`Registering seed data...`);for(let e of o)await registerSeedData(e,n,r)}return logger.info(`Custom question registration complete: ${s} items registered`),{success:!0,registeredCount:s}}catch(e){let t=e instanceof Error?e.message:String(e);return logger.error(`Custom question registration failed: ${t}`),{success:!1,registeredCount:0,errorMessage:t}}}init_study(),init_util2(),init_factory(),init_userDBHelpers();export{getRegisteredNavigatorNames as $,validateMigration as $t,clearSrsBacklogDebug as A,processCustomQuestionsData as At,endSessionTracking as B,resetIntakeClock as Bt,StaticToCouchDBMigrator as C,mountPipelineDebugger as Ct,buildStrategyStateId as D,newSessionId as Dt,areQuestionRecords as E,newInterval as Et,computeOutcomeSignal as F,registerNavigator as Ft,getAppDataDirectory as G,setDebugSessionId as Gt,frozenMsAt as H,reviewWeight as Ht,computeSpread as I,registerQuestionType as It,getDataLayer as J,stepIntakeClock as Jt,getCardHistoryID as K,snapshotQueues as Kt,createOrchestrationContext as L,registerSeedData as Lt,clearStrategyPressureDebug as M,recordUserOutcome as Mt,computeDeviation as N,registerActiveController as Nt,captureMixerRun as O,pipelineDebugAPI as Ot,computeEffectiveWeight as P,registerDataShape as Pt,getRegisteredNavigator as Q,userDBDebugAPI as Qt,diversityRerank as R,removeDataShape as Rt,SessionController as S,mountMixerDebugger as St,applyReading as T,mountUserDBDebugger as Tt,getActiveController as U,scoreAccuracyInZone as Ut,ensureAppDataDirectory as V,reviewMassReading as Vt,getActivePipeline as W,sessionDebugAPI as Wt,getLapses as X,updateLearningState as Xt,getDbPath as Y,toggleSessionOverlay as Yt,getRegisteredFilterKind as Z,updateStrategyWeight as Zt,NavigatorRoles as _,isReview as _t,DIVERSITY_FLOOR as a,getStudySource as at,QuotaRoundRobinMixer as b,makeStudySessionId as bt,DocTypePrefixes as c,initializeDataDirectory as ct,GuestUsername as d,intakeHours as dt,validateProcessorConfig as en,getRegisteredNavigatorRole as et,HYDRATION_MARKER_ID as f,intakeReading as ft,NavigatorRole as g,isQuestionTypeRegistered as gt,NOT_SET as h,isQuestionRecord as ht,DEFAULT_REGULATOR_CONFIG as i,getStreak as it,clearStaleSessionDebugState as j,recordCardPresentation as jt,captureStrategyPressure as k,planPipeline as kt,ENV as l,initializeDataLayer as lt,Loggable as m,isGenerator as mt,CouchDBToStaticPacker as n,getSrsBacklogDebug as nt,DIVERSITY_STRENGTH as o,hasRegisteredNavigator as ot,INTAKE_CLOCK_KEY as p,isFilter as pt,getCardOrigin as q,startSessionTracking as qt,CourseLookup as r,getStrategyPressureDebug as rt,DocType as s,importParsedCards as st,ContentNavigator as t,validateStaticCourse as tn,getRunsForSession as tt,FileSystemError as u,initializeNavigatorRegistry as ut,Navigators as v,log as vt,TagFilteredContentSource as w,mountSessionDebugger as wt,REVIEW_TIME_FORMAT2 as x,mixerDebugAPI as xt,PIPELINE_STAGES as y,makeScheduledCardId as yt,drainCapturedRuns as z,removeQuestionType as zt};
|
|
379
|
-
//# sourceMappingURL=dist-
|
|
379
|
+
//# sourceMappingURL=dist-D8fjT9uC.js.map
|