--- name: fluent-db-updater description: Atomically update all 6 Fluent learner databases (learner-profile, progress, mistakes, mastery, spaced-repetition, session-log) at session end by calling .claude/hooks/update-db.py with a single JSON payload. Use at the end of every practice session — fluent-writing, fluent-vocab, fluent-speaking, fluent-reading, fluent-review, fluent-learn — to persist the session's errors, review results, new vocabulary, and session metadata. --- # DB Updater ## Overview Every practice skill ends with a DB update. Instead of hand-editing 6 JSON files (error-prone, racy, easy to desync), pipe one JSON report to `update-db.py`. The script runs pre-write backups, validates the payload, applies all changes atomically via `.tmp + fsync + rename`, and rebuilds the spaced-repetition queue. ## When to Use Load this skill whenever the tutor: - Finishes a practice session and needs to persist results. - Needs to add new vocabulary to the spaced-repetition queue. - Needs to record new errors, review results, or mastery changes. - Needs to bump `total_sessions`, `current_streak_days`, or `total_study_minutes`. Skip this skill for read-only operations (use the `fluent-progress` skill or `read-db.py` directly) and during session setup (use `fluent-setup` skill instead — `update-db.py` is for session deltas, not bootstrap). ## Instructions ### 1. Call the script Run from the repo root: ```bash python3 "${CLAUDE_PLUGIN_ROOT:-${CLAUDE_PROJECT_DIR:-.}}/.claude/hooks/update-db.py" <<'EOF' { ...payload... } EOF ``` Exit codes: `0` success, `1` validation error, `2` I/O error. ### 2. Fill the payload **Required fields** - `session_id` — string, convention `session-NNN`. Use `computed.next_session_id` from `read-db.py`. - `date` — YYYY-MM-DD. **Optional fields** — omit to skip. Full canonical example (copy-paste this and fill in): ``` ${CLAUDE_PLUGIN_ROOT:-${CLAUDE_PROJECT_DIR:-.}}/.claude/references/db-updater-payload.example.json ``` Key blocks the example covers: `skill_scores`, `errors[]`, `new_vocabulary[]`, `review_results[]`, `topics_covered`, `breakthroughs`, `focus_next_session`, `session_notes`, `achievements_earned`, `milestones`. ### 3. Field notes - `errors[]` — one entry per distinct mistake this session. Collapse duplicates (same `pattern_id`) before sending; `frequency` is bumped by the script. - `new_vocabulary[]` — items the learner met for the first time. Fill every field; incomplete entries yield incomplete spaced-repetition records. - `review_results[]` — items already in the queue that were reviewed. The script runs SM-2 on each. See the `fluent-sm2-calculator` skill. Mapping: `quality = floor(score / 2)`. - `skill_scores[].correct` counts correct exercises, not a percentage. Accuracy is derived. - `confidence` in `learner-profile.skills` is 0–100 integer; `accuracy` in `progress-db` is 0.0–1.0 float. The script handles the conversion. - `milestones[]` — each entry is a bare string OR an object `{ "milestone": , "date": }`. Don't set a nested `session_id`; the script stamps the authoritative top-level one. A malformed entry (neither string nor object, or an object missing/empty `milestone`) exits `1` with no files written. Each milestone becomes both a `session-log.milestones[]` record and a `learner-profile.achievements[]` entry. ### 4. Read before writing Always call `read-db.py` at session start to get current state + `next_session_id`. Don't read each JSON file separately: ```bash python3 "${CLAUDE_PLUGIN_ROOT:-${CLAUDE_PROJECT_DIR:-.}}/.claude/hooks/read-db.py" ``` Returns all 6 databases plus computed fields (`due_reviews_count`, `next_session_id`, `streak_active`, `days_since_last_session`). ## Examples ### Example 1 — /fluent-review session with 5 items ```bash python3 "${CLAUDE_PLUGIN_ROOT:-${CLAUDE_PROJECT_DIR:-.}}/.claude/hooks/update-db.py" <<'EOF' { "session_id": "session-012", "date": "2026-04-24", "duration_minutes": 12, "command_used": "/fluent-review", "skills_practiced": ["vocabulary", "grammar"], "skill_scores": { "vocabulary": { "exercises": 3, "correct": 3, "time_minutes": 7 }, "grammar": { "exercises": 2, "correct": 1, "time_minutes": 5 } }, "review_results": [ { "item_id": "vocab_huis", "quality": 5 }, { "item_id": "vocab_deur", "quality": 4 }, { "item_id": "vocab_raam", "quality": 5 }, { "item_id": "grammar_omdat_word_order", "quality": 2 }, { "item_id": "grammar_past_tense", "quality": 4 } ], "errors": [ { "pattern_id": "grammar_omdat_word_order", "category": "grammar", "your_answer": "omdat ik ben moe", "correct_answer": "omdat ik moe ben", "context": "subordinate clause word order", "severity": "critical" } ], "focus_next_session": ["Drill 'omdat' word order"] } EOF ``` ### Example 2 — /fluent-vocab session with a new word ```bash python3 "${CLAUDE_PLUGIN_ROOT:-${CLAUDE_PROJECT_DIR:-.}}/.claude/hooks/update-db.py" <<'EOF' { "session_id": "session-013", "date": "2026-04-25", "command_used": "/fluent-vocab", "skills_practiced": ["vocabulary"], "new_vocabulary": [ { "item_id": "vocab_keuken", "item_type": "vocabulary", "content": "de keuken", "answer": "the kitchen", "category": "household_rooms", "difficulty": "A1", "initial_quality": 4, "priority": "medium" } ] } EOF ``` ## Critical Rules - **Call once per session, at the end.** The script rebuilds the review queue each run — partial updates risk inconsistency. - **Never hand-edit `spaced-repetition.review_queue`.** It's regenerated from scratch on every run. - **Same `session_id` replaces.** Sending the same ID twice overwrites the first call. Useful for corrections, dangerous if unintentional. - **Backups are automatic.** Written to `.backups/pre-update-/` before any change. Check there to roll back. - **Exit code 1 means validation failed, no files touched.** Fix the payload and retry. - **Exit code 2 means I/O failure, no files touched.** Check disk space, permissions, then retry. ## Why This Matters Six interdependent JSON files must agree: a new `session-log` entry, a bumped `total_sessions`, updated SM-2 params, new mistake patterns, recalculated accuracy, refreshed streak. Hand-editing causes silent desync — streak says 7 days but session-log has 6 entries, mastery says 4 stars but accuracy says 45%. The script is the single source of truth.