Files
Hankan/types/lib/hangul.d.ts
MechaCat02 e72b77d6c2 chore: take in the 16 Sep bundle — lib, curriculum v5, prompt, gate audit
The artifact was reworked after real incidents: a week of lost data, a
student taught out of order, and spelling diagnoses the model invented.
This takes the new export in verbatim; the port catches up in the
commits that follow.

Copied byte-identical from the bundle:
  lib/        lexicon.js and sync.js are new; gate.js gains enforcement,
              hangul.js letter-level marking, srs.js recall evidence,
              conjugation.js deconjugate(); blocks.js now takes the last
              block, closes gloss at "=", and parses recall, ::result and
              ::confirmed
  data/       curriculum.json v5 — six 다지기 phase reviews; the 371
              roadmap words are unchanged and no band moves
  prompt/     English-only rule, recall, LETTER-LEVEL CHECK, marking
  audit-gate.mjs, run-checks.sh, fixtures/  — the word gate measured
              against 54 real tutor messages

CI runs run-checks.sh in place of validate.mjs alone, and `npm run check`
gains the audit. Baselines: validate PASS 0/0; audit 7 of 41 and 2 of 13.

types/lib/ declares the new API, and test/lib/ pins it: letterCheck on
the prompt's own 짧다/빫다 case, deconjugation, the roadmap-first order
that keeps 마셔 out of Phase 1, sync's three gates, and recall evidence —
including the two ways lib's evidence is looser than PORT.md, pinned as
they are so the call site that tightens them is visibly needed.

TaskHost gains a plain recall renderer so the tree typechecks against the
wider Task union.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 19:49:52 +02:00

64 lines
2.1 KiB
TypeScript

/* Declarations for lib/hangul.js — the module itself ships unchanged. */
export const CHO: string;
export const JUNG: string;
export const JONG: string;
/** [initialIndex, medialIndex, finalIndex], or null if not a syllable block. */
export function decompose(ch: string): [number, number, number] | null;
export function compose(i: number, m: number, f?: number): string;
export const isJamo: {
initial(c: string): boolean;
medial(c: string): boolean;
final(c: string): boolean;
};
/**
* A 두벌식 IME. Hold one per input: feed jamo with key(), literal text with
* text(), Backspace with back(). Each call returns the full new value.
*/
export class Composer {
cho: string | null;
jung: string | null;
jong: string | null;
reset(): void;
readonly empty: boolean;
/** The syllable currently being assembled, as text. */
render(): string;
key(value: string, jamo: string): string;
back(value: string): string;
/** Commit the buffer and append literal text (space, punctuation). */
text(value: string, t: string): string;
}
export const KEYBOARD: {
rows: string[][];
shift: Record<string, string>;
};
/* ── letter-level marking ───────────────────────────────────────────── */
/** Two-consonant batchim clusters, unpacked: "ㄼ" → "ㄹ+ㅂ". */
export const CLUSTER: Record<string, string>;
/** ["first consonant", "vowel", "batchim"] — the three slots of a syllable. */
export const SLOT: string[];
/** "짧다" → "짧=ㅉ+ㅏ+ㄼ(ㄹ+ㅂ) · 다=ㄷ+ㅏ" */
export function spellOut(word: string): string;
/** The exact jamo difference, naming what was correct as well as what was
wrong. "" when either side is empty; "identical" when they match. */
export function letterDiff(expected: string, written: string): string;
export interface LetterRow {
/** The item as the student saw it — for a recall task, the English. */
prompt: string;
expected: string;
written: string;
}
/** The LETTER-LEVEL CHECK block for a marking message; "" when nothing differs. */
export function letterCheck(rows: LetterRow[]): string;