import { CourseConfig, ClassroomConfig, Status, SkuilderCourseData as SkuilderCourseData$1, CourseElo, DataShape, TagFilter } from '@vue-skuilder/common'; import { Moment } from 'moment'; import { S as SkuilderCourseData, b as DocTypePrefixes, D as DocType, Q as QualifiedCardID, T as TagStub, a as Tag, C as CardHistory, c as CardRecord } from './types-legacy-4tlwHnXo.cjs'; /** * Admin functionality */ interface AdminDBInterface { /** * Get all users */ getUsers(): Promise[]>; /** * Get all courses */ getCourses(): Promise; /** * Remove a course */ removeCourse(id: string): Promise; /** * Get all classrooms */ getClassrooms(): Promise<(ClassroomConfig & { _id: string; })[]>; } /** * Classroom management */ interface ClassroomDBInterface { /** * Get classroom config */ getConfig(): ClassroomConfig; /** * Get assigned content */ getAssignedContent(): Promise; } interface TeacherClassroomDBInterface extends ClassroomDBInterface { /** * For teacher interfaces: assign content */ assignContent?(content: AssignedContent): Promise; /** * For teacher interfaces: remove content */ removeContent?(content: AssignedContent): Promise; } /** * Student-facing classroom interface. * Content is accessed via StudyContentSource.getWeightedCards(). */ type StudentClassroomDBInterface = ClassroomDBInterface; type AssignedContent = AssignedCourse | AssignedTag | AssignedCard; interface AssignedTag extends ContentBase { type: 'tag'; tagID: string; } interface AssignedCourse extends ContentBase { type: 'course'; } interface AssignedCard extends ContentBase { type: 'card'; cardID: string; } interface ContentBase { type: 'course' | 'tag' | 'card'; /** * Username of the assigning teacher. */ assignedBy: string; /** * Date the content was assigned. */ assignedOn: moment.Moment; /** * A 'due' date for this assigned content, for scheduling content * in advance. Content will not be actively pushed to students until * this date. */ activeOn: moment.Moment; courseID: string; } interface DataLayerResult { status: Status; message: string; id?: string; } /** * Configuration for an evolutionarily-weighted strategy. * * This structure tracks the "learned" weight of a strategy, representing the * system's confidence in its utility. * * - weight: The best-known multiplier (peak of the bell curve) * - confidence: How certain we are (inverse of variance / spread) * - sampleSize: How many data points informed this weight */ interface LearnableWeight { /** The current best estimate of optimal weight (multiplier) */ weight: number; /** Confidence in this weight (0-1). Higher = narrower exploration spread. */ confidence: number; /** Number of outcome observations that contributed to this weight */ sampleSize: number; } /** * */ interface ContentNavigationStrategyData extends SkuilderCourseData { _id: `${typeof DocTypePrefixes[DocType.NAVIGATION_STRATEGY]}-${string}`; docType: DocType.NAVIGATION_STRATEGY; name: string; description: string; /** The name of the class that implements the navigation strategy at runtime. */ implementingClass: string; /** A representation of the strategy's parameterization - to be deserialized by the implementing class's constructor at runtime. */ serializedData: string; /** * Evolutionary weighting configuration. * If present, the strategy's influence is scaled by this weight. * If omitted, weight defaults to 1.0. */ learnable?: LearnableWeight; /** * If true, the weight is applied exactly as configured, without * per-user deviation. Used for manual tuning or A/B testing. */ staticWeight?: boolean; } /** * A NavigationStrategyManager is an entity which may contain multiple strategies. * * This interface defines strategy CRUD. */ interface NavigationStrategyManager { /** * Get the navigation strategy for a given course * @returns The navigation strategy for the course */ getNavigationStrategy(id: string): Promise; /** * Get all available navigation strategies * @returns An array of all available navigation strategies */ getAllNavigationStrategies(): Promise; /** * Add a new navigation strategy * @param data The data for the new navigation strategy * @returns A promise that resolves when the strategy has been added */ addNavigationStrategy(data: ContentNavigationStrategyData): Promise; /** * Update an existing navigation strategy * @param id The ID of the navigation strategy to update * @param data The new data for the navigation strategy * @returns A promise that resolves when the update is complete */ updateNavigationStrategy(id: string, data: ContentNavigationStrategyData): Promise; } /** * Course content and management */ interface CoursesDBInterface { /** * Get course config */ getCourseConfig(courseId: string): Promise; /** * Get a list of all courses */ getCourseList(): Promise; disambiguateCourse(courseId: string, disambiguator: string): Promise; } interface CourseInfo { cardCount: number; registeredUsers: number; } interface CourseDBInterface extends NavigationStrategyManager, StudyContentSource { /** * Get course config */ getCourseConfig(): Promise; getCourseID(): string; /** * Set course config */ updateCourseConfig(cfg: CourseConfig): Promise; getCourseInfo(): Promise; getCourseDoc(id: string, options?: PouchDB.Core.GetOptions): Promise; getCourseDocs(ids: string[], options?: PouchDB.Core.AllDocsOptions): Promise>; /** * Get cards sorted by ELO rating */ getCardsByELO(elo: number, limit?: number): Promise<{ courseID: string; cardID: string; elo?: number; }[]>; /** * Get ELO data for specific cards */ getCardEloData(cardIds: string[]): Promise; /** * Update card ELO rating */ updateCardElo(cardId: string, elo: CourseElo): Promise; /** * Get cards centered at a particular ELO rating */ getCardsCenteredAtELO(options: { limit: number; elo: 'user' | 'random' | number; }, filter?: (card: QualifiedCardID) => boolean): Promise; /** * Get tags for a card */ getAppliedTags(cardId: string): Promise>; /** * Get tags for multiple cards in a single batch query. * More efficient than calling getAppliedTags() for each card. * * This method reduces redundant database operations when multiple filters * need tag data for the same cards. The Pipeline uses this to pre-hydrate * tags on WeightedCard objects before filters run. * * @param cardIds - Array of card IDs to fetch tags for * @returns Map from cardId to array of tag names */ getAppliedTagsBatch(cardIds: string[]): Promise>; /** * Get all card IDs in the course. * Used by diagnostics to scan the full card space. */ getAllCardIds(): Promise; /** * Add a tag to a card */ addTagToCard(cardId: string, tagId: string, updateELO?: boolean): Promise; /** * Remove a tag from a card */ removeTagFromCard(cardId: string, tagId: string): Promise; /** * Create a new tag */ createTag(tagName: string, author: string): Promise; /** * Get a tag by name */ getTag(tagName: string): Promise; /** * Update a tag */ updateTag(tag: Tag): Promise; /** * Get all tag stubs for a course */ getCourseTagStubs(): Promise>; /** * Add a note to the course */ addNote(codeCourse: string, shape: DataShape, data: unknown, author: string, tags: string[], uploads?: { [key: string]: PouchDB.Core.FullAttachment; }, elo?: CourseElo): Promise; removeCard(cardId: string): Promise; getInexperiencedCards(): Promise<{ courseId: string; cardId: string; count: number; elo: CourseElo; }[]>; /** * Search for cards by text content * @param query Text to search for * @returns Array of matching card data */ searchCards(query: string): Promise; /** * Find documents using PouchDB query syntax * @param request PouchDB find request * @returns Query response */ find(request: PouchDB.Find.FindRequest): Promise>; } interface SessionTrackingData { peekSessionCount: number; studySessionCount: number; sessionCount: number; firstSessionDate: string; lastSessionDate: string; signupPrompted: boolean; promptDismissalCount: number; studyModeAcknowledged: boolean; } interface UserConfig { darkMode: boolean; likesConfetti: boolean; sessionTimeLimit: number; email?: string; sessionTracking?: Record; } interface ActivityRecord { timeStamp: number | string; [key: string]: any; } interface CourseRegistration { status?: 'active' | 'dropped' | 'maintenance-mode' | 'preview'; courseID: string; admin: boolean; moderator: boolean; user: boolean; settings?: { [setting: string]: string | number | boolean; }; elo: number | CourseElo; } interface StudyWeights { [courseID: string]: number; } interface CourseRegistrationDoc { courses: CourseRegistration[]; studyWeight: StudyWeights; } interface ScheduledCard { _id: PouchDB.Core.DocumentId; /** * The docID of the card to be reviewed */ cardId: PouchDB.Core.DocumentId; /** * The ID of the course */ courseId: string; /** * The time at which the card becomes eligible for review. * * (Should probably be UTC adjusted so that performance is * not wonky across time zones) * * Note: Stored as ISO string for PouchDB serialization compatibility, * but can be consumed as Moment objects via moment.utc(reviewTime) */ reviewTime: string | Moment; /** * The time at which this scheduled event was created. * * Note: Stored as ISO string for PouchDB serialization compatibility, * but can be consumed as Moment objects via moment.utc(scheduledAt) */ scheduledAt: string | Moment; /** * Classifying whether this card is scheduled on behalf of a * user-registered course or by as assigned content from a * user-registered classroom */ scheduledFor: 'course' | 'classroom'; /** * The ID of the course or classroom that requested this card */ schedulingAgentId: string; } /** * Record of a user's learning outcome over a specific period. * * Used by the evolutionary orchestration system to correlate strategy * deviations with learning success. * * Stored in the UserDB. */ interface UserOutcomeRecord { /** * Unique ID: "USER_OUTCOME::{courseId}::{userId}::{timestamp}" * Timestamp corresponds to periodEnd. */ _id: string; docType: DocType.USER_OUTCOME; courseId: string; userId: string; /** Start of the measurement period (ISO timestamp) */ periodStart: string; /** End of the measurement period (ISO timestamp) */ periodEnd: string; /** * The computed signal value (e.g., 0.85 for 85% accuracy). * This is the 'Y' in the regression analysis. * * Higher values indicate better learning outcomes. */ outcomeValue: number; /** * Snapshot of active deviations during this period. * Maps strategyId -> deviation value used [-1.0, 1.0]. * This provides the 'X' values for regression analysis. */ deviations: Record; metadata: { sessionsCount: number; cardsSeen: number; eloStart: number; eloEnd: number; /** The algorithm used to compute outcomeValue (e.g. "accuracy_in_zone") */ signalType: string; }; } type Update = Partial | ((x: T) => T); interface DocumentUpdater { update>(id: string, update: Update): Promise; } /** * Returns the minimum number of seconds that should pass before a * card is redisplayed for review / practice. * * @param cardHistory The user's history working with the given card */ declare function newInterval(user: DocumentUpdater, cardHistory: CardHistory): number; /** * Read-only user data operations */ interface UserDBReader { get(id: string): Promise; getUsername(): string; isLoggedIn(): boolean; /** * Get user configuration */ getConfig(): Promise; /** * Get cards that the user has seen */ getSeenCards(courseId?: string): Promise; /** * Get cards that are actively scheduled for review */ getActiveCards(): Promise; /** * Get user's course registrations */ getCourseRegistrationsDoc(): Promise; /** * Get the registration doc for a specific course. * @param courseId */ getCourseRegDoc(courseId: string): Promise; /** * Get user's active courses */ getActiveCourses(): Promise; /** * Get user's pending reviews */ getPendingReviews(courseId?: string): Promise; getActivityRecords(): Promise; /** * Get strategy-specific state for a course. * * Strategies use this to persist preferences, learned patterns, or temporal * tracking data across sessions. Each strategy owns its own namespace. * * @deprecated Use `getCourseInterface(courseId).getStrategyState(strategyKey)` instead. * Direct use bypasses course-scoping safety — the courseId parameter is unguarded, * allowing accidental cross-course data access. The course-scoped interface binds * courseId once at construction. * * @param courseId - The course this state applies to * @param strategyKey - Unique key identifying the strategy (typically class name) * @returns The strategy's data payload, or null if no state exists */ getStrategyState(courseId: string, strategyKey: string): Promise; /** * Get user's classroom registrations */ getUserClassrooms(): Promise; /** * Get user's active classes */ getActiveClasses(): Promise; getCourseInterface(courseId: string): Promise; } /** * User data mutation operations */ interface UserDBWriter extends DocumentUpdater { /** * Update user configuration */ setConfig(config: Partial): Promise; /** * Record a user's interaction with a card */ putCardRecord(record: T): Promise>; /** * Register user for a course */ registerForCourse(courseId: string, previewMode?: boolean): Promise; /** * Drop a course registration */ dropCourse(courseId: string, dropStatus?: string): Promise; /** * Schedule a card for review */ scheduleCardReview(review: { user: string; course_id: string; card_id: string; time: Moment; scheduledFor: 'course' | 'classroom'; schedulingAgentId: string; }): Promise; /** * Remove a scheduled card review */ removeScheduledCardReview(reviewId: string): Promise; /** * Register user for a classroom */ registerForClassroom(classId: string, registerAs: 'student' | 'teacher' | 'aide' | 'admin'): Promise; /** * Drop user from classroom */ dropFromClassroom(classId: string): Promise; /** * Update user's ELO rating for a course */ updateUserElo(courseId: string, elo: CourseElo): Promise; /** * Reset all user data (progress, registrations, etc.) while preserving authentication */ resetUserData(): Promise<{ status: Status; error?: string; }>; /** * Store strategy-specific state for a course. * * Strategies use this to persist preferences, learned patterns, or temporal * tracking data across sessions. Each strategy owns its own namespace. * * @deprecated Use `getCourseInterface(courseId).putStrategyState(strategyKey, data)` instead. * Direct use bypasses course-scoping safety — the courseId parameter is unguarded, * allowing accidental cross-course data writes. The course-scoped interface binds * courseId once at construction. * * @param courseId - The course this state applies to * @param strategyKey - Unique key identifying the strategy (typically class name) * @param data - The strategy's data payload to store */ putStrategyState(courseId: string, strategyKey: string, data: T): Promise; /** * Delete strategy-specific state for a course. * * @deprecated Use `getCourseInterface(courseId).deleteStrategyState(strategyKey)` instead. * Direct use bypasses course-scoping safety. * * @param courseId - The course this state applies to * @param strategyKey - Unique key identifying the strategy (typically class name) */ deleteStrategyState(courseId: string, strategyKey: string): Promise; /** * Record a user learning outcome for evolutionary orchestration. */ putUserOutcome(record: UserOutcomeRecord): Promise; } /** * Authentication and account management operations */ interface UserDBAuthenticator { /** * Create a new user account */ createAccount(username: string, password: string): Promise<{ status: Status; error: string; }>; /** * Log in as a user */ login(username: string, password: string): Promise<{ ok: boolean; name?: string; roles?: string[]; }>; /** * Log out the current user */ logout(): Promise<{ ok: boolean; }>; } /** * Complete user database interface - combines all user operations * This maintains backward compatibility with existing code */ interface UserDBInterface extends UserDBReader, UserDBWriter, UserDBAuthenticator { } interface UserCourseSettings { [setting: string]: string | number | boolean; } interface UserCourseSetting { key: string; value: string | number | boolean; } interface UsrCrsDataInterface { getScheduledReviewCount(): Promise; getCourseSettings(): Promise; updateCourseSettings(updates: UserCourseSetting[]): void; /** * Get strategy-specific state for this course. * * Course-scoped alternative to `UserDBInterface.getStrategyState()`. * The courseId is bound at construction via `getCourseInterface(courseId)`, * so callers cannot accidentally access another course's state. * * @param strategyKey - Unique key identifying the state document * @returns The state payload, or null if no state exists */ getStrategyState(strategyKey: string): Promise; /** * Store strategy-specific state for this course. * * Course-scoped alternative to `UserDBInterface.putStrategyState()`. * * @param strategyKey - Unique key identifying the state document * @param data - The state payload to store */ putStrategyState(strategyKey: string, data: T): Promise; /** * Delete strategy-specific state for this course. * * Course-scoped alternative to `UserDBInterface.deleteStrategyState()`. * * @param strategyKey - Unique key identifying the state document */ deleteStrategyState(strategyKey: string): Promise; } type ClassroomRegistrationDesignation = 'student' | 'teacher' | 'aide' | 'admin'; interface ClassroomRegistration { classID: string; registeredAs: ClassroomRegistrationDesignation; } interface ClassroomRegistrationDoc { registrations: ClassroomRegistration[]; } /** * Context for orchestration decisions during a session. * * Provides access to user/course data and helper methods for determining * effective strategy weights based on the user's cohort assignment. */ interface OrchestrationContext { user: UserDBInterface; course: CourseDBInterface; userId: string; courseConfig: CourseConfig; /** * Calculate the effective weight for a strategy for this user. * * Applies deviation based on the user's cohort assignment (derived from * userId, strategyId, and course salt). * * @param strategyId - Unique ID of the strategy * @param learnable - The strategy's learning configuration * @returns Effective weight multiplier (typically 0.1 - 3.0) */ getEffectiveWeight(strategyId: string, learnable: LearnableWeight): number; /** * Get the deviation factor for this user/strategy. * Range [-1.0, 1.0]. */ getDeviation(strategyId: string): number; } /** * Compute a user's deviation for a specific strategy. * * Returns a value in [-1, 1] that is: * 1. Deterministic for the same (user, strategy, salt) tuple * 2. Uniformly distributed across users * 3. Uncorrelated between different strategies (due to strategyId in hash) * 4. Rotatable by changing the salt * * @param userId - ID of the user * @param strategyId - ID of the strategy * @param salt - Random seed from course config * @returns Deviation factor between -1.0 and 1.0 */ declare function computeDeviation(userId: string, strategyId: string, salt: string): number; /** * Compute the exploration spread based on confidence. * * - Low confidence (0.0) -> Max spread (Explore broadly) * - High confidence (1.0) -> Min spread (Exploit known good weight) * * @param confidence - Confidence level 0-1 * @returns Spread magnitude (half-width of the distribution) */ declare function computeSpread(confidence: number): number; /** * Calculate the effective weight for a strategy instance. * * Combines the learnable weight (peak) with the user's deviation and the * allowed spread (based on confidence). * * @param learnable - Strategy learning config * @param userId - User ID * @param strategyId - Strategy ID * @param salt - Course salt * @returns Effective weight multiplier */ declare function computeEffectiveWeight(learnable: LearnableWeight, userId: string, strategyId: string, salt: string): number; /** * Create an orchestration context for a study session. * * Fetches necessary configuration to enable deterministic weight calculation. * * @param user - User DB interface * @param course - Course DB interface * @returns Initialized orchestration context */ declare function createOrchestrationContext(user: UserDBInterface, course: CourseDBInterface): Promise; /** * Type for navigator constructor functions. */ type NavigatorConstructor = new (user: UserDBInterface, course: CourseDBInterface, strategyData: ContentNavigationStrategyData) => ContentNavigator; /** * Register a navigator implementation. * * Call this to make a navigator available for instantiation by * ContentNavigator.create() without relying on dynamic imports. * * Passing a `role` is optional for built-in navigators (whose roles are in * the hardcoded `NavigatorRoles` record), but **required** for consumer- * defined navigators that need to participate in pipeline assembly. * * @param implementingClass - The class name (e.g., 'elo', 'letterGatingFilter') * @param constructor - The navigator class constructor * @param role - Optional pipeline role (GENERATOR or FILTER) */ declare function registerNavigator(implementingClass: string, constructor: NavigatorConstructor, role?: NavigatorRole): void; /** * Get a navigator constructor from the registry. * * @param implementingClass - The class name to look up * @returns The constructor, or undefined if not registered */ declare function getRegisteredNavigator(implementingClass: string): NavigatorConstructor | undefined; /** * Check if a navigator is registered. * * @param implementingClass - The class name to check * @returns true if registered, false otherwise */ declare function hasRegisteredNavigator(implementingClass: string): boolean; /** * Get the registered role for a navigator, if one was provided at registration. * * @param implementingClass - The class name to look up * @returns The role, or undefined if not registered or no role was specified */ declare function getRegisteredNavigatorRole(implementingClass: string): NavigatorRole | undefined; /** * Get all registered navigator names. * Useful for debugging and testing. */ declare function getRegisteredNavigatorNames(): string[]; /** * Initialize the navigator registry with all built-in navigators. * * This function dynamically imports all standard navigator implementations * and registers them. Call this once at application startup to ensure * all navigators are available. * * In test environments, this may need to be called explicitly before * using ContentNavigator.create(). */ declare function initializeNavigatorRegistry(): Promise; /** * Tracks a single strategy's contribution to a card's final score. * * Each strategy in the pipeline adds a StrategyContribution entry to the * card's provenance array, creating an audit trail of scoring decisions. */ interface StrategyContribution { /** * Strategy type (implementing class name). * Examples: 'elo', 'hierarchyDefinition', 'interferenceMitigator' */ strategy: string; /** * Human-readable name identifying this specific strategy instance. * Extracted from ContentNavigationStrategyData.name. * Courses may have multiple instances of the same strategy type with * different configurations. * * Examples: * - "ELO (default)" * - "Interference: b/d/p confusion" * - "Interference: phonetic confusables" * - "Priority: Common letters first" */ strategyName: string; /** * Unique database document ID for this strategy instance. * Extracted from ContentNavigationStrategyData._id. * Use this to fetch the full strategy configuration document. * * Examples: * - "NAVIGATION_STRATEGY-ELO-default" * - "NAVIGATION_STRATEGY-interference-bdp" * - "NAVIGATION_STRATEGY-priority-common-letters" */ strategyId: string; /** * What the strategy did: * - 'generated': Strategy produced this card (generators only) * - 'passed': Strategy evaluated but didn't change score (transparent pass-through) * - 'boosted': Strategy increased the score * - 'penalized': Strategy decreased the score */ action: 'generated' | 'passed' | 'boosted' | 'penalized'; /** Score after this strategy's processing */ score: number; /** * The effective weight applied for this strategy instance. * If using evolutionary orchestration, this includes deviation. * If omitted, implies weight 1.0 (legacy behavior). */ effectiveWeight?: number; /** * The deviation factor applied to this user's cohort for this strategy. * Range [-1.0, 1.0]. */ deviation?: number; /** * Human-readable explanation of the strategy's decision. * * Examples: * - "ELO distance 75, new card" * - "Prerequisites met: letter-sounds" * - "Interferes with immature tag 'd' (decay 0.8)" * - "High-priority tag 's' (0.95) → boost 1.15x" * * Required for transparency - silent adjusters are anti-patterns. */ reason: string; } /** * A card with a suitability score and provenance trail. * * Scores range from 0-1: * - 1.0 = fully suitable * - 0.0 = hard filter (e.g., prerequisite not met) * - 0.5 = neutral * - Intermediate values = soft preference * * Provenance tracks the scoring pipeline: * - First entry: Generator that produced the card * - Subsequent entries: Filters that transformed the score * - Each entry includes action and human-readable reason */ interface WeightedCard { cardId: string; courseId: string; /** Suitability score from 0-1 */ score: number; /** * Audit trail of strategy contributions. * First entry is from the generator, subsequent entries from filters. */ provenance: StrategyContribution[]; /** * Pre-fetched tags. Populated by Pipeline before filters run. * Filters should use this instead of querying getAppliedTags() individually. */ tags?: string[]; /** * Review document ID (_id from ScheduledCard). * Present when this card originated from SRS review scheduling. * Used by SessionController to track review outcomes and maintain review state. */ reviewID?: string; } /** * Extract card origin from provenance trail. * * The first provenance entry (from the generator) indicates whether * this is a new card, review, or failed card. We parse the reason * string to extract this information. * * @param card - Card with provenance trail * @returns Card origin ('new', 'review', or 'failed') */ declare function getCardOrigin(card: WeightedCard): 'new' | 'review' | 'failed'; declare enum Navigators { ELO = "elo", SRS = "srs", PRESCRIBED = "prescribed", HIERARCHY = "hierarchyDefinition", INTERFERENCE = "interferenceMitigator", RELATIVE_PRIORITY = "relativePriority", USER_TAG_PREFERENCE = "userTagPreference" } /** * Role classification for navigation strategies. * * - GENERATOR: Produces candidate cards with initial scores * - FILTER: Transforms cards with score multipliers */ declare enum NavigatorRole { GENERATOR = "generator", FILTER = "filter" } /** * Registry mapping navigator implementations to their roles. */ declare const NavigatorRoles: Record; /** * Check if a navigator implementation is a generator. * * @param impl - Navigator implementation name (e.g., 'elo', 'hierarchyDefinition') * @returns true if the navigator is a generator, false otherwise */ declare function isGenerator(impl: string): boolean; /** * Check if a navigator implementation is a filter. * * Checks the built-in NavigatorRoles enum first, then falls back to the * navigator registry for consumer-registered navigators. * * @param impl - Navigator implementation name (e.g., 'elo', 'letterGatingFilter') * @returns true if the navigator is a filter, false otherwise */ declare function isFilter(impl: string): boolean; /** * Abstract base class for navigation strategies. * * This class exists primarily for backward compatibility with legacy code. * New code should use CardGenerator or CardFilter interfaces directly. * * The class implements StudyContentSource for compatibility with SessionController. * Once SessionController migrates to use getWeightedCards() exclusively, * the legacy methods can be removed. */ declare abstract class ContentNavigator implements StudyContentSource { /** User interface for this navigation session */ protected user: UserDBInterface; /** Course interface for this navigation session */ protected course: CourseDBInterface; /** Human-readable name for this strategy instance (from ContentNavigationStrategyData.name) */ protected strategyName?: string; /** Unique document ID for this strategy instance (from ContentNavigationStrategyData._id) */ protected strategyId?: string; /** Evolutionary weighting configuration */ learnable?: LearnableWeight; /** Whether to bypass deviation (manual/static weighting) */ staticWeight?: boolean; /** * Constructor for standard navigators. * Call this from subclass constructors to initialize common fields. * * Note: CompositeGenerator and Pipeline call super() without args, then set * user/course fields directly if needed. */ constructor(user?: UserDBInterface, course?: CourseDBInterface, strategyData?: ContentNavigationStrategyData); /** * Unique key identifying this strategy for state storage. * * Defaults to the constructor name (e.g., "UserTagPreferenceFilter"). * Override in subclasses if multiple instances of the same strategy type * need separate state storage. */ protected get strategyKey(): string; /** * Get this strategy's persisted state for the current course. * * @returns The strategy's data payload, or null if no state exists * @throws Error if user or course is not initialized */ protected getStrategyState(): Promise; /** * Persist this strategy's state for the current course. * * @param data - The strategy's data payload to store * @throws Error if user or course is not initialized */ protected putStrategyState(data: T): Promise; /** * Factory method to create navigator instances. * * First checks the navigator registry for a pre-registered constructor. * If not found, falls back to dynamic import (for custom navigators). * * For reliable operation in test environments, call initializeNavigatorRegistry() * before using this method. * * @param user - User interface * @param course - Course interface * @param strategyData - Strategy configuration document * @returns the runtime object used to steer a study session. */ static create(user: UserDBInterface, course: CourseDBInterface, strategyData: ContentNavigationStrategyData): Promise; /** * Get cards with suitability scores and provenance trails. * * **This is the PRIMARY API for navigation strategies.** * * Returns cards ranked by suitability score (0-1). Higher scores indicate * better candidates for presentation. Each card includes a provenance trail * documenting how strategies contributed to the final score. * * ## Implementation Required * All navigation strategies MUST override this method. The base class does * not provide a default implementation. * * ## For Generators * Override this method to generate candidates and compute scores based on * your strategy's logic (e.g., ELO proximity, review urgency). Create the * initial provenance entry with action='generated'. * * ## For Filters * Filters should implement the CardFilter interface instead and be composed * via Pipeline. Filters do not directly implement getWeightedCards(). * * @param limit - Maximum cards to return * @returns Cards sorted by score descending, with provenance trails */ getWeightedCards(_limit: number): Promise; /** * Set ephemeral hints for the next pipeline run. * No-op for non-Pipeline navigators. Pipeline overrides this. */ setEphemeralHints(_hints: ReplanHints): void; } /** * Typed ephemeral pipeline hints for a single run. * All fields are optional. Tag/card patterns support `*` wildcards. */ interface ReplanHints { /** Multiply scores for cards matching these tag patterns. */ boostTags?: Record; /** Multiply scores for these specific card IDs (glob patterns). */ boostCards?: Record; /** Cards matching these tag patterns MUST appear in results. */ requireTags?: string[]; /** These specific card IDs MUST appear in results. */ requireCards?: string[]; /** Remove cards matching these tag patterns from results. */ excludeTags?: string[]; /** Remove these specific card IDs from results. */ excludeCards?: string[]; /** * Debugging label threaded from the replan requester. * Prefixed with `_` to signal it's metadata, not a scoring hint. */ _label?: string; } /** * Context available to generators when producing candidates. * * Built once per getWeightedCards() call by the Pipeline. */ interface GeneratorContext { /** User database interface */ user: UserDBInterface; /** Course database interface */ course: CourseDBInterface; /** User's global ELO score for this course */ userElo: number; /** Orchestration context for evolutionary weighting */ orchestration?: OrchestrationContext; } /** * Structured generator result. * * Generators may optionally emit one-shot replan hints alongside their * candidate cards. This allows a generator to shape the broader pipeline * without having to enumerate every affected support card directly. */ interface GeneratorResult { /** Candidate cards produced by the generator */ cards: WeightedCard[]; /** Optional one-shot hints to apply after the filter chain */ hints?: ReplanHints; } /** * A generator that produces candidate cards with initial scores. * * Generators are the "source" stage of a navigation pipeline. * They query the database for eligible cards and assign initial * suitability scores based on their strategy (ELO proximity, * review urgency, fixed order, etc.). * * ## Implementation Guidelines * * 1. **Create provenance**: Each card should have a provenance entry * with action='generated' documenting why it was selected. * * 2. **Score semantics**: Higher scores = more suitable for presentation. * Scores should be in [0, 1] range for composability. * * 3. **Limit handling**: Respect the limit parameter, but may over-fetch * internally if needed for scoring accuracy. * * 4. **Sort before returning**: Return cards sorted by score descending. * * 5. **Hints are optional**: Generators may return structured results with * `hints` when they need to apply pipeline-wide ephemeral pressure. * * ## Example Implementation * * ```typescript * const myGenerator: CardGenerator = { * name: 'My Generator', * async getWeightedCards(limit, context) { * const candidates = await fetchCandidates(context.course, limit); * return candidates.map(c => ({ * cardId: c.id, * courseId: context.course.getCourseID(), * score: computeScore(c, context), * provenance: [{ * strategy: 'myGenerator', * strategyName: 'My Generator', * strategyId: 'MY_GENERATOR', * action: 'generated', * score: computeScore(c, context), * reason: 'Explanation of selection' * }] * })); * } * }; * ``` */ interface CardGenerator { /** Human-readable name for this generator */ name: string; /** * Produce candidate cards with initial scores. * * @param limit - Maximum number of cards to return * @param context - Shared context (user, course, userElo, etc.) * @returns Cards sorted by score descending, with provenance, optionally * accompanied by one-shot replan hints */ getWeightedCards(limit: number, context: GeneratorContext): Promise; } /** * Factory function type for creating generators from configuration. * * Used by PipelineAssembler to instantiate generators from strategy documents. */ type CardGeneratorFactory = (config: TConfig) => CardGenerator; type StudySessionFailedItem = StudySessionFailedNewItem | StudySessionFailedReviewItem; interface StudySessionFailedNewItem extends StudySessionItem { status: 'failed-new'; } interface StudySessionFailedReviewItem extends StudySessionReviewItem { status: 'failed-review'; } interface StudySessionNewItem extends StudySessionItem { status: 'new'; } interface StudySessionReviewItem extends StudySessionItem { reviewID: string; status: 'review' | 'failed-review'; } declare function isReview(item: StudySessionItem): item is StudySessionReviewItem; interface StudySessionItem { status: 'new' | 'review' | 'failed-new' | 'failed-review'; contentSourceType: 'course' | 'classroom'; contentSourceID: string; cardID: string; courseID: string; elo?: number; /** * Pipeline suitability score at queue-build time, carried for observability * (the debug overlay renders the now-load-bearing supply ranking). `+INF` * marks a mandatory required card. Not used for any draw decision — the * supplyQ is already ordered, so the controller draws front-to-back. */ score?: number; } interface ContentSourceID { type: 'course' | 'classroom'; id: string; /** * Optional tag filter for scoped study sessions. * When present, creates a TagFilteredContentSource instead of a regular course source. */ tagFilter?: TagFilter; } /** * Interface for sources that provide study content to SessionController. * * Content sources return scored candidates via getWeightedCards(), which * SessionController uses to populate study queues. * * See: packages/db/docs/navigators-architecture.md */ interface StudyContentSource { /** * Get cards with suitability scores for presentation. * * Returns unified scored candidates that can be sorted and selected by SessionController. * The card origin ('new' | 'review' | 'failed') is determined by provenance metadata. * * @param limit - Maximum number of cards to return * @returns Cards sorted by score descending */ getWeightedCards(limit: number): Promise; /** * Get the orchestration context for this source. * Used for recording learning outcomes. */ getOrchestrationContext?(): Promise; /** * Set ephemeral hints for the next pipeline run. * No-op for sources that don't support hints. */ setEphemeralHints?(hints: ReplanHints): void; } declare function getStudySource(source: ContentSourceID, user: UserDBInterface): Promise; export { Navigators as $, type AdminDBInterface as A, type UsrCrsDataInterface as B, type CourseDBInterface as C, type DataLayerResult as D, type ClassroomRegistrationDesignation as E, type ClassroomRegistration as F, type GeneratorResult as G, type ClassroomRegistrationDoc as H, type SessionTrackingData as I, type UserConfig as J, type ActivityRecord as K, type CourseRegistration as L, type UserOutcomeRecord as M, type NavigatorConstructor as N, registerNavigator as O, getRegisteredNavigator as P, hasRegisteredNavigator as Q, type ReplanHints as R, type StudySessionItem as S, type TeacherClassroomDBInterface as T, type UserDBInterface as U, getRegisteredNavigatorRole as V, type WeightedCard as W, getRegisteredNavigatorNames as X, initializeNavigatorRegistry as Y, type StrategyContribution as Z, getCardOrigin as _, type UserDBReader as a, NavigatorRole as a0, NavigatorRoles as a1, isGenerator as a2, isFilter as a3, type CardGenerator as a4, type GeneratorContext as a5, type CardGeneratorFactory as a6, type LearnableWeight as a7, type OrchestrationContext as a8, computeDeviation as a9, computeSpread as aa, computeEffectiveWeight as ab, createOrchestrationContext as ac, type DocumentUpdater as ad, newInterval as ae, type CoursesDBInterface as b, type ClassroomDBInterface as c, type CourseInfo as d, type ContentNavigationStrategyData as e, ContentNavigator as f, type AssignedContent as g, type StudyContentSource as h, type StudentClassroomDBInterface as i, type ScheduledCard as j, type StudySessionFailedItem as k, type StudySessionFailedNewItem as l, type StudySessionFailedReviewItem as m, type StudySessionNewItem as n, type StudySessionReviewItem as o, isReview as p, type ContentSourceID as q, getStudySource as r, type CourseRegistrationDoc as s, type AssignedTag as t, type AssignedCourse as u, type AssignedCard as v, type UserDBWriter as w, type UserDBAuthenticator as x, type UserCourseSettings as y, type UserCourseSetting as z };