|
| 1 | +export const meta = { |
| 2 | + name: "typed-french-parity", |
| 3 | + description: |
| 4 | + "Generate Typed French course chapters (+ their vocab) toward feature parity, then verify every snippet type-checks and resolves to its sentence.", |
| 5 | + whenToUse: |
| 6 | + "Run to add grammar-course chapters to typed-french. Pass args.chapters (a list of {id, level, order, titleEn, titleZh, topicEn, topicZh}); defaults to an elementary batch.", |
| 7 | + phases: [ |
| 8 | + { title: "Author", detail: "one agent per chapter writes chapters/<id>.ts + entries/<id>.ts" }, |
| 9 | + { title: "Verify & fix", detail: "run verify:snippets + verify:vocab, fix until both pass" }, |
| 10 | + ], |
| 11 | +}; |
| 12 | + |
| 13 | +// --------------------------------------------------------------------------- |
| 14 | +// Typed French DSL cheat-sheet handed to every authoring agent. Keep in sync |
| 15 | +// with src/*.d.ts. |
| 16 | +// --------------------------------------------------------------------------- |
| 17 | +const DSL = `TYPED FRENCH DSL — import from "typed-french": |
| 18 | +NOUNS (gender is required for common nouns): |
| 19 | + CommonNoun<"chat","m"> | CommonNoun<"femme","f"> ; ProperNoun<"Marie"> ; Pronoun<"je"> ; Adverb<"vite"> |
| 20 | +DETERMINER + NOUN (article surface auto-computed from gender / elision / number): |
| 21 | + DefiniteNP<Noun[, "p"]> -> "le chat" / "la femme" / "l'homme" / "les chats" |
| 22 | + IndefiniteNP<Noun[, "p"]> -> "un chat" / "une femme" / "des chats" |
| 23 | + PartitiveNP<Noun[, "p"]> -> "du pain" / "de la confiture" / "de l'eau" / "des fruits" |
| 24 | +ADJECTIVE agreement: |
| 25 | + Adjective<"noir"> ; ConjugateAdjective<Adj, "m"|"f"[, "s"|"p"]> |
| 26 | + (noir/noire/noirs/noires ; heureux->heureuse ; cher->chère ; grand->grande ; rouge invariant) |
| 27 | +VERBS: |
| 28 | + ErVerb<"parl"> (parler) ; IrVerb<"fin"> (finir) ; ReVerb<"vend"> (vendre) |
| 29 | + IrregularVerb<"être"> — FULLY supported across tenses: être, avoir, aller, faire. |
| 30 | + IrregularVerb<"venir"|"pouvoir"|"vouloir"|"prendre"|"voir"|"dire"> — Present, PasseCompose, Infinitif, PastParticiple only. |
| 31 | + ConjugateVerb<Verb, Tense, Person="il"> |
| 32 | + Tense = "Present"|"Imparfait"|"FuturSimple"|"Conditionnel"|"Subjonctif"|"PasseCompose"|"Imperatif"|"Infinitif"|"PastParticiple" |
| 33 | + Person = "je"|"tu"|"il"|"nous"|"vous"|"ils" (il also serves elle/on; ils serves elles) |
| 34 | +COMPOSITION: |
| 35 | + SubjectVerb<"je", Verb[, Tense]> -> "je parle" / "j'aime" (elides je->j') |
| 36 | + Clause<SubjectString, Verb[, Tense]> -> "<subject> <verb in 3rd person>" (e.g. a DefiniteNP subject) |
| 37 | + NegativeVerb<Subj, Verb[, Tense]> -> "il ne parle pas" / "il n'aime pas" |
| 38 | + EstCeQue<ClauseString> -> "est-ce que ..." / "est-ce qu'..." |
| 39 | + Sentence<S> -> Capitalize(S) + "." |
| 40 | + Question<S> -> Capitalize(S) + " ?" (French puts a space before ? ! :) |
| 41 | + Exclamation<S>-> Capitalize(S) + " !" |
| 42 | +
|
| 43 | +HARD RULES: |
| 44 | +- Every example's code is self-contained and imports only from "typed-french". |
| 45 | +- The LAST 'type' alias in the snippet must resolve to EXACTLY the example's 'fr' string |
| 46 | + (including the leading capital and trailing "." / " ?" — use Sentence/Question/Exclamation). |
| 47 | +- The regular -er conjugator does NOT do orthographic changes. AVOID with "nous": |
| 48 | + -ger verbs (manger->mangeons), -cer verbs (commencer->commençons), and stem-changers |
| 49 | + (préférer, acheter, appeler). Stick to clean stems (parler, aimer, habiter, regarder, |
| 50 | + travailler, chercher, donner, écouter, jouer) or use the supported irregulars. |
| 51 | +- Do NOT invent wrappers or tenses beyond the list above.`; |
| 52 | + |
| 53 | +const SCHEMA = `FILE 1 — playground/src/tutorial/chapters/<id>.ts (default-export a Chapter): |
| 54 | +import type { Chapter } from "../types"; |
| 55 | +const chapter: Chapter = { |
| 56 | + id: "<id>", level: "elementary"|"intermediate"|"advanced", order: <n>, |
| 57 | + titleEn, titleZh, summaryEn, summaryZh, // summaries: 2-4 sentences, immersive, scenario-led |
| 58 | + points: [ |
| 59 | + { id: "<id>-1", titleEn, titleZh, |
| 60 | + bodyEn, bodyZh, // bilingual teaching prose, paragraphs separated by \\n\\n; motivate the idea before mechanics; use inline \`code\` and **bold** |
| 61 | + examples: [ { fr, en, zh, code, ipa? } ] // 1-2 per point; fr = display sentence WITH capital + punctuation |
| 62 | + }, |
| 63 | + // 2-3 points total |
| 64 | + ], |
| 65 | +}; |
| 66 | +export default chapter; |
| 67 | +
|
| 68 | +FILE 2 — playground/src/vocab/entries/<id>.ts (default-export VocabEntry[]): |
| 69 | +import type { VocabEntry } from "../types"; |
| 70 | +const entries: VocabEntry[] = [ |
| 71 | + { word, gender?: "m"|"f", ipa?, pos, en, zh }, // include EVERY content word your snippets use that is not already in batch-01 |
| 72 | +]; |
| 73 | +export default entries; |
| 74 | +pos = "noun"|"proper-noun"|"pronoun"|"adjective"|"adverb"|"verb-er"|"verb-ir"|"verb-re"|"verb-irregular"|"preposition"|"conjunction"|"interjection"|"numeral"|"expression" |
| 75 | +Verb headword = the INFINITIVE (parler, finir, être). Noun headword = the singular noun; give its gender.`; |
| 76 | + |
| 77 | +// --------------------------------------------------------------------------- |
| 78 | +// Syllabus: use args.chapters when provided, else a default elementary batch. |
| 79 | +// --------------------------------------------------------------------------- |
| 80 | +const DEFAULT_CHAPTERS = [ |
| 81 | + { id: "e03", level: "elementary", order: 3, titleEn: "Plural nouns & des", titleZh: "复数名词与 des", |
| 82 | + topicEn: "Forming the plural of nouns and the plural articles les / des; agreement of adjectives in the plural.", |
| 83 | + topicZh: "名词复数的构成与复数冠词 les / des;形容词的复数一致。" }, |
| 84 | + { id: "e04", level: "elementary", order: 4, titleEn: "être & avoir", titleZh: "être 与 avoir", |
| 85 | + topicEn: "The two essential irregular verbs être (to be) and avoir (to have) in the present, and the idiom 'avoir' for age/hunger.", |
| 86 | + topicZh: "两个最重要的不规则动词 être(是)与 avoir(有)的现在时,以及 avoir 表年龄/饥饿的惯用法。" }, |
| 87 | + { id: "e05", level: "elementary", order: 5, titleEn: "Asking questions", titleZh: "提问", |
| 88 | + topicEn: "Yes/no questions with est-ce que and rising intonation; the difference from English inversion.", |
| 89 | + topicZh: "用 est-ce que 和升调提一般疑问句;与英语倒装的区别。" }, |
| 90 | + { id: "e06", level: "elementary", order: 6, titleEn: "-ir & -re verbs", titleZh: "-ir 与 -re 动词", |
| 91 | + topicEn: "The present tense of regular second-group (-ir, finir) and third-group (-re, vendre) verbs.", |
| 92 | + topicZh: "规则第二组(-ir,finir)与第三组(-re,vendre)动词的现在时。" }, |
| 93 | +]; |
| 94 | + |
| 95 | +const chapters = (args && args.chapters) || DEFAULT_CHAPTERS; |
| 96 | +const PG = "playground (run node from /Users/evan/code/typed-french/playground)"; |
| 97 | + |
| 98 | +log(`Authoring ${chapters.length} chapter(s): ${chapters.map((c) => c.id).join(", ")}`); |
| 99 | + |
| 100 | +// ---- Phase 1: author each chapter + its vocab in parallel ----------------- |
| 101 | +phase("Author"); |
| 102 | +await parallel( |
| 103 | + chapters.map((c) => () => |
| 104 | + agent( |
| 105 | + `Author Typed French grammar-course chapter "${c.id}" (level ${c.level}, order ${c.order}). |
| 106 | +Topic — EN: ${c.topicEn} |
| 107 | +Topic — 中文: ${c.topicZh} |
| 108 | +Suggested titles: titleEn="${c.titleEn}", titleZh="${c.titleZh}" (improve if you like). |
| 109 | +
|
| 110 | +Write TWO files (use the Write tool, absolute paths under /Users/evan/code/typed-french/): |
| 111 | + 1. playground/src/tutorial/chapters/${c.id}.ts |
| 112 | + 2. playground/src/vocab/entries/${c.id}.ts (only the NEW content words your snippets use) |
| 113 | +
|
| 114 | +${DSL} |
| 115 | +
|
| 116 | +${SCHEMA} |
| 117 | +
|
| 118 | +QUALITY BAR: match the voice and depth of the existing chapters — read |
| 119 | +playground/src/tutorial/chapters/e01.ts and e02.ts first as the template, and |
| 120 | +playground/src/vocab/entries/batch-01.ts for vocab style + which words already exist |
| 121 | +(do NOT redefine words already in batch-01.ts). Bilingual EN/简体中文 at equal depth. |
| 122 | +
|
| 123 | +VERIFY YOUR OWN WORK before finishing: from the playground directory run |
| 124 | + node scripts/verify-snippets.mjs --json |
| 125 | +and read the JSON. Your chapter's examples must NOT appear in the failures list |
| 126 | +(each must type-check AND resolve to exactly its 'fr'). Fix and re-run until your |
| 127 | +chapter is clean. (Other chapters being authored concurrently may show failures — |
| 128 | +ignore those; only ensure YOUR ${c.id} examples pass.) Then also confirm the words |
| 129 | +you used are covered (node scripts/verify-vocab.mjs --json — your words must not be 'missing'). |
| 130 | +
|
| 131 | +Return a one-line status: "${c.id}: OK" or "${c.id}: <problem>".`, |
| 132 | + { label: `author:${c.id}`, phase: "Author" } |
| 133 | + ) |
| 134 | + ) |
| 135 | +); |
| 136 | + |
| 137 | +// ---- Phase 2: one capable agent makes BOTH gates green -------------------- |
| 138 | +phase("Verify & fix"); |
| 139 | +const verdict = await agent( |
| 140 | + `You are the verification gate for the Typed French course. Working dir for commands: |
| 141 | +/Users/evan/code/typed-french/playground (${PG}). |
| 142 | +
|
| 143 | +Run these and make BOTH pass: |
| 144 | + 1. node scripts/gen-reverse-index.mjs (regenerates the vocab↔example index) |
| 145 | + 2. node scripts/verify-snippets.mjs (every example type-checks AND its last alias resolves to exactly its 'fr') |
| 146 | + 3. node scripts/verify-vocab.mjs (every content word used by the course is in the glossary) |
| 147 | + 4. node_modules/.bin/tsc --noEmit (the playground still type-checks) |
| 148 | +
|
| 149 | +For any failure, FIX the offending file(s) under playground/src/tutorial/chapters/ and |
| 150 | +playground/src/vocab/entries/ (and add missing vocab words with correct gender/pos). |
| 151 | +Use the DSL rules below. Re-run until all four are clean. |
| 152 | +
|
| 153 | +${DSL} |
| 154 | +
|
| 155 | +Common fixes: a snippet whose last alias resolves to a slightly different string than 'fr' |
| 156 | +(adjust the 'fr' field OR the code so they match EXACTLY, capital + punctuation included); |
| 157 | +a missing vocab word (add it to the relevant entries/<id>.ts with gender + pos); a -ger/-cer |
| 158 | +or stem-changing verb used with "nous" (rewrite the example to a clean verb). |
| 159 | +
|
| 160 | +When everything passes, run: node scripts/verify-snippets.mjs (human output) and report the |
| 161 | +final counts. Return a short summary: which chapters are now in the course, total examples |
| 162 | +checked, and confirmation that all four checks pass.`, |
| 163 | + { label: "verify+fix", phase: "Verify & fix" } |
| 164 | +); |
| 165 | + |
| 166 | +log("Parity batch complete."); |
| 167 | +return { chapters: chapters.map((c) => c.id), verdict }; |
0 commit comments