/* Declarations for lib/gate.js — the module itself ships unchanged. The gate is what stops material being taught out of order. buildGate() computes it from curriculum + progress; renderGate() turns it into the {{GATE}} section of prompt/tutor-system.md. The second half — scanTask(), proseIsKorean(), rejectionNote() — checks whether the tutor actually listened, and its scope was measured against real messages (audit-gate.mjs), not reasoned out. */ import type { ParsedMessage, Task } from "./blocks.js"; export interface Unit { id: string; ko: string; name: string; goal: string; vocabUnit: boolean; /** A 다지기 phase review: introduces nothing, confirms the whole phase. */ review?: boolean; teaches: string[]; avoid?: string[]; /** Strictly NEW vocabulary. Deliberate repeats live in revisits[]. */ words: string[]; revisits?: { word: string; from: string }[]; } /** A Unit with its phase stamped on, as flatten() returns it. */ export interface FlatUnit extends Unit { phase: number; phaseKo: string; phaseName: string; } export interface Phase { phase: number; ko: string; name: string; units: Unit[]; } export interface Curriculum { version: number; note?: string; phases: Phase[]; } export interface ProgressState { current: string; done: Record; confidence?: Record; } /** * Replaces the hand-listed word set with a dictionary query. Returning a * frequency band here is what turns 371 hand-typed words into something * that scales. */ export type VocabQuery = (unit: FlatUnit, done: FlatUnit[]) => string[]; export interface GateOptions { vocabQuery?: VocabQuery; } export interface Gate { unit: FlatUnit; phase: { n: number; ko: string; name: string }; taught: string[]; forbidden: { near: string[]; tailUnit: FlatUnit | null; count: number }; vocabulary: string[]; newWords: string[]; /** Words met earlier that this unit should deliberately bring back. */ revisits: string[]; confidence: number | null; next: FlatUnit | null; finished: string[]; } export function flatten(curriculum: Curriculum): FlatUnit[]; export function buildGate( curriculum: Curriculum, progress: ProgressState, opts?: GateOptions, ): Gate; /** Render the gate into the system prompt section. Keep the headings. */ export function renderGate(g: Gate): string; /* ── enforcing the gate ─────────────────────────────────────────────── */ /** Unit and phase names plus the course's metalanguage (받침, 비음화, …): Hangul a teacher may write without it being taught vocabulary. */ export function buildScaffold(curriculum: Curriculum): Set; /** Only the side of an exercise the student must decode. Recall prompts are English, so a recall task contributes nothing. */ export function taskMaterial(task: Task | null | undefined): string[]; export interface GateFinding { word: string; /** The unit that introduces it, or "" when no unit does. */ unit: string; /** false: no gloss exists anywhere — a typo or an invented word. */ known: boolean; } export interface ScanContext { allowed: Set; scaffold: Set; /** Every dictionary word this surface could be, best first. */ heads: (token: string) => string[]; /** The unit that introduces a word, or "". */ unitOf: (word: string) => string; } export function scanTask( parsed: Pick | null | undefined, ctx: ScanContext, ): GateFinding[]; /** ≥40 Hangul syllables in the prose outside blocks, and ≥50% of letters. */ export function proseIsKorean(text: string): boolean; /** What to tell the tutor when a message is sent back. */ export function rejectionNote(findings: GateFinding[]): string; /** Retries before a message is shown anyway, with its words flagged. */ export const GATE_TRIES: number;