---
name: memorytechniques-generate-test
description: Generate an AI practice test for the Memory Techniques app (VŠE FPH přijímačky — matematika nebo angličtina) locally, bez volání Anthropic API. Vloží test přímo do D1 (`saved_tests` tabulka) přes `npx wrangler d1 execute`, takže se okamžitě objeví v Practice → Saved tests. Triggers on `/memorytechniques-generate-test`, `/memorytechniques-test`, "vygeneruj test", "memory techniques test", "practice test math", "practice test english", "VŠE přijímačky test".
---

# Memory Techniques — Generate AI Test (local, no API spend)

Patrik runs this when he wants a fresh VŠE FPH entrance exam practice test but doesn't want to burn Anthropic API credits through the deployed `/api/generate-test` endpoint. Claude generates the test JSON itself, following the exact same format + LaTeX rules the deployed prompt enforces, and inserts it straight into the D1 `saved_tests` table so the Practice UI picks it up.

## Inputs

- `<subject>` — `math` or `english` (required; accept `matika`, `mat`, `en`, `aj` as aliases)
- `--weak=<topic1,topic2>` — optional: focus ≥60% of questions on these topics (same topic IDs as `MATH_TOPICS` / `ENGLISH_TOPICS` in `src/data/vse`)
- `--include=<natural-language list>` — optional: Patrik's free-text request for specific problem types to include. Examples: `--include="soustava x^2+y^2=5 a xy=2"`, `--include="logaritmická rovnice s podmínkami"`, `--include="parametrická úloha — dva různé kladné kořeny"`. Parse the freeform list and guarantee at least one question per named type. If the include item doesn't map cleanly to a standard topic, emit it as `soustava-rovnic`/`parametricke-ulohy`/the closest match.
- `--title=<string>` — optional override; default `"AI Test — Matematika"` / `"AI Test — English"`
- `--dry-run` — write only the JSON file, skip the D1 insert.

Examples:
- `/memorytechniques-generate-test math`
- `/memorytechniques-generate-test english --weak=phrasal-verbs,conditionals`
- `/memorytechniques-generate-test matika --weak=logaritmy,kombinatorika`
- `/memorytechniques-generate-test math --include="soustava kvadratická + nelineární, xy+y=6 styl"`

## Hard constraints

- **NO Anthropic API call.** Claude composes the test inline as part of this skill run. The deployed `/api/generate-test` endpoint is bypassed entirely.
- **Math in LaTeX inside `$...$` delimiters.** The frontend renders through `src/components/MathText.jsx` (KaTeX). See [LaTeX rules](#latex-rules-math) below. Never use Unicode math (`√`, `π`, `²`, `≤` etc.) — always wrap in `$...$`.
- **D1 write via wrangler, not curl.** The prod `pages.dev` API is fine, but wrangler is the canonical path for memory-techniques data writes (matches `feedback_koudy-finance-d1-import` pattern; prod API is public here but wrangler is still deterministic).
- **Repo root is `~/WebstormProjects/memory-techniques`.** All wrangler commands run from there.
- **JSON only between delimiters.** When composing, Claude keeps the questions array as valid JSON — backslashes in LaTeX must be escaped as `\\` in JSON strings (so `$\frac{1}{2}$` in the rendered output is `"$\\frac{1}{2}$"` in the JSON). For SQL safety we pass the INSERT through a file, not inline.
- **Difficulty floor: VŠE FPH přijímačky, NE maturita.** Patrik feedback 2026-04-24: první iterace byla moc jednoduchá ("Vypočtěte $\log_2(8) + \log_3(1/9)$" = trivial). Every math problem MUST require multiple steps or a non-obvious technique. Apply these rules:
  - **Q1–Q7 (5 pts):** Exponenciální/logaritmické rovnice vyžadují substituci (`$t=2^x$`) NEBO kontrolu definičního oboru (odhazování cizích kořenů). Goniometrie: počítání kořenů v intervalu, ne "spočti $\sin(\pi/6)$". Komplexní čísla: převod do polárního tvaru, Moivre, ne "spočti $|3-4i|$". Mocniny/odmocniny: tři různé exponenty se smíšenými zlomky.
  - **Q8–Q11 (13 pts, PŘESNĚ 4 slovní úlohy):** Vícekrokové realistické úlohy. Kombinatorika typicky "aspoň N" přes komplement; pohyb s posunutým startem (jeden vyráží dřív); směsi jako soustava dvou rovnic; procenta jako kombinace tří multiplikativních změn. Ne "dosaď $s=vt$".
  - **Q12–Q15 (doplňkové, součet 13 pts):** Konceptuální — lomená nerovnice s testem znamének, soustava kvadratická + nelineární, rovnice kružnice, parametrická úloha s Viètovými podmínkami, binomický koeficient pro konkrétní člen.
- **Soustava rovnic (kvadratická + nelineární) je "dobrá úloha".** Patrik explicitly wants this type (např. `$x^2+y^2=5$` + `$xy=2$`, nebo `$x^2+y=7$` + `$xy+y=6$`) — zahrň ji minimálně jednou v každém matematickém testu pokud Patrik nespecifikuje jinak. Topic: `soustava-rovnic`.
- **`--include=` je hard guarantee.** Když Patrik vyžádá konkrétní typ přes `--include`, MUSÍ být alespoň jedna otázka přesně toho typu v testu (i kdyby to znamenalo vytlačit jiný standard topic).

## Procedure

### Step 1 — Parse args

Resolve `<subject>`:
- `math`, `matika`, `mat`, `matematika` → `math`
- `english`, `en`, `aj`, `anglictina` → `english`
- anything else → error: `❌ Neznámý subject "<x>". Použij math nebo english.`

Resolve `--weak`:
- Comma-split, trim, dedupe. Validate against the topic whitelist:
  - **math:** `logaritmy, exponencialni-rovnice, goniometrie, komplexni-cisla, mocniny-odmocniny, funkce-definicni-obor, posloupnosti, nerovnice, kvadraticke-rovnice, parametricke-ulohy, kombinatorika, spolecna-prace, smesi, pohyb, prumery, procenta, pomery, binomicka-veta, analyticka-geometrie`
  - **english:** `tenses, conditionals, passive, reported-speech, modals, gerund-infinitive, relative-clauses, articles-prepositions, inversions, subjunctive-causative, phrasal-verbs, synonyms-antonyms, collocations, business-vocab, confused-words, reading-comprehension`
- Invalid topics → warn and drop, proceed with the rest.

### Step 2 — Compose the test

Claude composes the test **in this conversation**. Format matches the deployed `/api/generate-test` endpoint exactly. **Re-read the canonical prompts** from `~/WebstormProjects/memory-techniques/functions/api/generate-test.js` (`buildMathPrompt` / `buildEnglishPrompt`) before composing — if Patrik has changed them, follow the newer version. The generator here MUST stay in lockstep with what `functions/api/generate-test.js` asks for.

#### Math test shape

```json
{
  "id": "ai-math-<unix-timestamp>",
  "title": "AI Test — Matematika",
  "time": 3600,
  "questions": [
    {
      "id": 1,
      "points": 5,
      "topic": "logaritmy",
      "text": "...LaTeX math in $...$...",
      "options": ["...", "...", "...", "...", "žádná z předchozích odpovědí není správná"],
      "correct": 0,
      "explanation": "..."
    }
    // 15 questions total: ids 1-7 (5 pts each), 8-11 (13 pts each), 12-15 (splits the remaining 13 points to hit 100 total)
  ]
}
```

**Scoring distribution (must hit 100 pts):** 1–7 × 5 = 35, 8–11 × 13 = 52, 12–15 must sum to 13. Typical split: 12=3, 13=3, 14=3, 15=4 (or any split summing to 13).

#### English test shape — REAL VŠE FPH formát (2026-04-25 update)

**Canonical reference (always re-read before composing):**
`~/.claude/skills/memorytechniques-generate-test/reference/vse-fph-english-real-exam.pdf` — scan reálných příjímaček (verze A0, A1, A2, A4, A6) s Patrikovými rukopisnými poznámkami. Use Read tool with `pages: "1-10"` na začátku každé english generation.

**Reálný formát (NE to, co měl prompt dřív):**
- **50 otázek total**, vše v JEDNÉ posloupnosti — žádné `sections` array, žádné Part 1/Part 2/Part 3.
- **Každá otázka = jedna anglická věta s prázdným místem (`.....`)**, fill-in-the-blank style.
- **4 možnosti** (A/B/C/D), **NE 5** — žádná "none of the above" pseudo-option.
- **Žádné reading comprehension passages.** Žádný `passage` field.
- **British English textbook standard** — explicitně řečeno v hlavičce každé verze: "Only standard written textbook British English will be accepted".
- Témata smíchaná v jedné posloupnosti (NE seskupené po blocích): tenses, conditionals, modals, phrasal verbs, prepositions, articles, quantifiers, reported speech, inversions, question tags, causative (have/get sth done), wish + past, gerund/infinitive, relative clauses, comparatives, idioms, phrasal verbs, vocabulary nuance, confused words.
- Úroveň: B1/B2 (upper-intermediate), často "trick" otázky kde rozdíl mezi A a B je jemný (např. `for` vs `since`, `to take` vs `taking`, `would have` vs `had`).

**Příklady reálných otázek z PDF (use as style anchor, NOT verbatim copy):**
- "Look ..... ! There's a car coming!" → A/for B/through C/out D/after (correct: C)
- "I'll try to get in touch with him but he's ..... ever at home when I phone." → A/hardly B/occasionally C/rarely D/almost (correct: A)
- "By the time you arrive ....." → A/he left B/he'll have left C/he leaves D/he'll leave (correct: B — future perfect after time clause)
- "While the builders were repairing the roof they ..... the bathroom window." → A/broke B/was broken C/break D/have broken (correct: A — past simple in past continuous context)
- "It will cost a lot of money to have ....." → A/made that work B/that work done C/done that work D/that work made (correct: B — causative)
- "I am increasingly optimistic that we can ..... it changed." → A/get B/let C/be allowed D/had (correct: A — get sth done)

**JSON shape:**
```json
{
  "id": "ai-en-<unix-timestamp>",
  "title": "AI Test — English (VŠE FPH)",
  "time": 2700,
  "questions": [
    { "id": 1, "topic": "phrasal-verbs", "text": "Look ..... ! There's a car coming!", "options": ["for", "through", "out", "after"], "correct": 2, "explanation": "Phrasal verb 'look out' = pozor, dej si bacha. Varovné zvolání před nebezpečím. (Britská angličtina, učebnicový tvar.)" },
    { "id": 2, "topic": "tenses", "text": "By the time you arrive .....", "options": ["he left", "he'll have left", "he leaves", "he'll come"], "correct": 1, "explanation": "Po časové větě 'by the time' v budoucnosti se používá future perfect, protože akce odejde dřív, než se ten druhý stihne dostavit." }
    // ... 50 questions, all same shape, NO sections, NO passages
  ]
}
```

**Topic IDs (English):** tenses, conditionals, passive, reported-speech, modals, gerund-infinitive, relative-clauses, articles-prepositions, inversions, question-tags, causative, phrasal-verbs, prepositions, quantifiers, comparatives, synonyms-antonyms, collocations, confused-words, idioms, vocabulary-nuance.

**English questions don't need LaTeX.** Plain text fill-in-the-blank. Use `.....` (5 dots) for the gap to match the real exam visual.

**Distribuce témat (target ratios for 50 Q):**
- Tenses: 8–10
- Modals + causative + wish: 5–7
- Conditionals + inversions + reported speech: 4–6
- Phrasal verbs + prepositions: 8–10
- Articles + quantifiers + comparatives: 4–6
- Gerund/infinitive + relative clauses + question tags: 5–7
- Vocabulary (collocations, confused words, idioms, nuance): 8–10

**Vysvětlení v češtině** zůstává — krátké, ukáže pravidlo (např. "Po 'deny' následuje gerundium." nebo "'Hardly ever' = skoro nikdy, jediná možnost která dává smysl v negativním kontextu.").

### Step 3 — LaTeX rules (math)

Every math expression MUST be inside `$...$`. Obyčejný český text mimo `$...$`.

| Concept | LaTeX (inside `$...$`) | JSON escape |
|---|---|---|
| Zlomek | `\frac{a}{b}` | `"$\\frac{a}{b}$"` |
| Odmocnina | `\sqrt{x}`, `\sqrt[3]{x}` | `"$\\sqrt{x}$"` |
| Mocnina | `x^{2}`, `2^{n+1}` | `"$x^{2}$"` |
| Index | `a_{n}`, `\log_{2}(x)` | `"$a_{n}$"` |
| Goniometrie | `\sin(x)`, `\cos(\alpha)`, `\tan(\frac{\pi}{4})` | `"$\\sin(x)$"` |
| Konstanty | `\pi`, `\infty`, `e` | `"$\\pi$"` |
| Nerovnosti | `\leq`, `\geq`, `\neq`, `\approx` | `"$\\leq$"` |
| Sety / intervaly | `\langle 0; 1 \rangle`, `x \in \mathbb{R}` | `"$x \\in \\mathbb{R}$"` |
| Komplexní | `\|z\| = \sqrt{a^2 + b^2}`, `z = r(\cos\varphi + i\sin\varphi)` | `"$\|z\| = \\sqrt{a^2+b^2}$"` |
| Suma / limita | `\sum_{k=1}^{n} k`, `\lim_{x \to 0}` | `"$\\sum_{k=1}^{n} k$"` |
| Kombinační číslo | `\binom{n}{k}` | `"$\\binom{n}{k}$"` |

**JSON escape reminder:** Each `\` in LaTeX becomes `\\` in the JSON string. When you compose the test and then `JSON.stringify` mentally / explicitly, verify that backslashes are doubled.

LaTeX also belongs in `options` and `explanation`, not just `text`.

### Step 4 — Write JSON + SQL to tmp files

1. Generate `test_id` = `ai-<subject>-<unix-ms>` (matches the convention used by `generateTest()` in `src/components/practice/PracticeDashboard.jsx`).
2. Write the composed test JSON to `/tmp/mt-test-<test_id>.json` (pretty-printed, for Patrik to eyeball if needed).
3. Write SQL to `/tmp/mt-test-<test_id>.sql`:

   ```sql
   INSERT OR IGNORE INTO saved_tests (test_id, subject, title, questions, source)
   VALUES (
     '<test_id>',
     '<subject>',
     '<title>',
     '<JSON.stringify(questions) with single-quotes escaped as ''>',
     'ai'
   );
   ```

   **Escape rule:** SQLite strings escape `'` as `''`. Use `python3 -c` or Bun to build the SQL — don't hand-escape large JSON. Suggested one-liner:

   ```bash
   bun -e 'const t=JSON.parse(await Bun.file("/tmp/mt-test-<id>.json").text()); const sql=`INSERT OR IGNORE INTO saved_tests (test_id, subject, title, questions, source) VALUES (${[t.id, t.subject||"<subject>", t.title, JSON.stringify(t.questions), "ai"].map(v=>`'${String(v).replace(/'"'"'/g,"'"'"''"'"'")}'`).join(", ")});`; await Bun.write("/tmp/mt-test-<id>.sql", sql);'
   ```

   Simpler fallback (Python, always available on macOS):

   ```bash
   python3 - <<'PY'
   import json, sys
   test = json.load(open('/tmp/mt-test-<test_id>.json'))
   q = json.dumps(test['questions'], ensure_ascii=False).replace("'", "''")
   title = test['title'].replace("'", "''")
   sql = f"INSERT OR IGNORE INTO saved_tests (test_id, subject, title, questions, source) VALUES ('{test['id']}', '<subject>', '{title}', '{q}', 'ai');"
   open('/tmp/mt-test-<test_id>.sql', 'w').write(sql)
   PY
   ```

### Step 5 — Insert into D1 (remote)

From `~/WebstormProjects/memory-techniques`:

```bash
npx wrangler d1 execute memory-techniques-words --remote --file=/tmp/mt-test-<test_id>.sql
```

On failure:
- **"D1_ERROR: UNIQUE constraint failed"** → `test_id` already exists; regenerate with a new timestamp and retry.
- **"no such table: saved_tests"** → migration `0010_saved_tests.sql` not applied in remote. Prompt Patrik: `⚠ Migration 0010 not applied in remote D1. Run: npx wrangler d1 migrations apply memory-techniques-words --remote`
- **Auth error** → prompt: `⚠ Wrangler auth expired. Run: npx wrangler login`

### Step 6 — Output receipt

Print in this format (no Discord DM — this is a local action, not a team one):

```
✅ AI test vygenerovaný a vložený do D1 (bez volání API)

  Subject: <subject>
  Test ID: <test_id>
  Title:   <title>
  Questions: <count>
  Weak topics: <comma list or "—">
  File: /tmp/mt-test-<test_id>.json

Otevři Practice:
  https://memory-techniques.pages.dev → Procvičování → <Matematika | English> → Uložené testy
```

Do NOT paste the full test JSON into chat — it's ~12k tokens. If Patrik wants to see it, offer to cat the file.

## Edge cases

| Case | Behavior |
|---|---|
| Patrik passes no subject | Ask: `Math nebo english? (math / english)`. Wait for answer, then proceed. Don't guess. |
| `--weak` has topics from the wrong subject | Drop silently with a one-line warning; proceed with valid topics. |
| Claude generates fewer questions than expected (math<15 or english<50) | Stop. Don't write. Print `⚠ Test generation incomplete (<N>/<target> questions). Retry.` |
| JSON has invalid LaTeX (KaTeX would throw) | MathText.jsx catches with `throwOnError: false`, so the raw `$...$` would render as plain text. Before insert, optional: validate by rendering through a node KaTeX check — for now, rely on Patrik's eye. |
| Patrik passes an existing `test_id` via `--id=` flag (not standard) | Not supported; always generate a fresh id. |
| Wrangler not found | `⚠ wrangler not installed. Run: bun install (in memory-techniques repo) — it's a devDependency.` |
| Remote D1 write hits Cloudflare rate limit | One retry after 5 s, then surface the error. |
| Patrik wants a test *without* saving (dry run) | Support `--dry-run`: skip Step 5, only write JSON file, print path. |

## What this skill does NOT do

- Does not POST to `/api/generate-test` (the whole point is to skip it).
- Does not hit any LLM API — Claude IS the generator.
- Does not touch static tests in `src/data/vse/` — those stay authored.
- Does not deploy anything. D1 write is the only side effect.
- Does not send a Discord DM — local dev action, not team-visible.
