Skip to content

Commit 8496fa6

Browse files
doodlewindclaude
andcommitted
feat(course): add chapters e03–e06 + parity workflow
Generated via .claude/workflows/parity.ts (one author agent per chapter + a verify/fix gate). The course is now 6 chapters, 32 examples — every snippet type-checks AND its last alias resolves byte-identically to its French sentence (verify:snippets), and every content word is indexed (verify:vocab). - e03 Plural nouns & des - e04 être & avoir - e05 Asking questions (est-ce que & intonation) - e06 -ir & -re verbs - .claude/workflows/parity.ts — reusable iteration engine (pass args.chapters to extend toward full parity) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent ff4dfa5 commit 8496fa6

10 files changed

Lines changed: 1255 additions & 0 deletions

File tree

.claude/workflows/parity.ts

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
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

Comments
 (0)