Phase 3: Check my work, follow-ups and turn-into-deck

Check my work (third homework mode)
- One model call with its own system prompt marks the student's own
  attempt: verdict, what's right, each issue with why + fix, and the
  corrected version that keeps everything the student got right.
  Unasked-for extras are never errors. The attempt is typed/pasted
  (photos of handwriting are documented as unsupported) and stored in
  the record so Redo re-checks the same work.
- Server: mode "check" + solution on POST /api/homework (zod, 400
  server.needSolution without one) and the same NDJSON stream; new
  checkHomework() pipeline with repair-once validation.

Follow-ups
- New POST /api/homework/refine (plain JSON, RATE_LIMIT_REFINE, zod,
  auth before body): shorter / more detail / explain, plain text back,
  language preserved.
- Shorter + More detail + Original rewrite the finished answer and the
  corrected version in place (FollowUpBar); every guide step has
  Explain step N differently; failed calls say so without losing text.

Turn into a study deck
- Any result hands its content (answer, corrected version or assembled
  guide) to New deck through a one-shot sessionStorage seed with the
  options step pre-filled.

UI
- Phone step 2 gains the third card: tapping it reveals the solution
  field + submit; desktop has three cards and the solution field shows
  with the mode. Homework history labels and share/card previews cover
  the new mode.

Verified: new phase-3 suite 20/20 against a stub Ollama (check payload,
missing-solution 400, refine validation, phone + desktop check flows,
shorter follow-up with restore, guide explain, deck hand-off) plus
regression 74/74, mobile 52/52, declutter 116/116, wizard 80/80,
animation 8/8, native 35/35, phase1 38/38, phase2 43/43, i18n 25/25.
Key parity 734/734. Lint + build clean. SW freezmo-v27.
This commit is contained in:
Martin Dvořák 2026-10-10 14:36:04 +00:00
commit 4513364019
22 changed files with 947 additions and 56 deletions

View file

@ -123,20 +123,27 @@ no database, no data leaving your machine except the requests you send to Ollama
- Same intake as a deck — paste the assignment, photograph it, or add a PDF/DOCX/PPTX/ODT/text
file; photos go through the same vision model and language guard that notes do
- Two modes: **Just make it** returns the finished homework (the answer only, ready to copy), and
**Explain how to make this homework** turns it into a guided walkthrough
- Three modes: **Just make it** returns the finished homework (the answer only, ready to copy),
**Explain how to make this homework** turns it into a guided walkthrough, and **Check my work**
marks the student's own attempt — what's right, what's wrong and why, plus a corrected version
(the attempt is typed or pasted; photos of handwriting are not supported yet, and the UI says so)
- **Follow-ups on a result**: *Shorter*, *More detail* and back to the *Original* rewrite the
finished text in place; inside a guide every step has *Explain step N differently*
- **Turn into a study deck** on any result opens New deck with the content (the answer, the
corrected version or the assembled guide) and the options step pre-filled
- On phones this is a **2-step flow**: the assignment first (same simplified input as the
deck flow, with its own step headline, formats hint, big add area and empty placeholder),
then the two mode cards under a short preview — tapping a card starts it immediately, so
there is no separate button. Step 1 starts with *Cancel* and the flat action bar matches
the deck wizard. Desktop keeps the select-then-confirm layout.
then the mode cards under a short preview — tapping a card starts it immediately (the check
card first reveals the solution field), so there is no separate button. Step 1 starts with
*Cancel* and the flat action bar matches the deck wizard. Desktop keeps the select-then-confirm
layout.
- The guide is one step per screen in the same snap-feed feel as the study feed: what to do and
why, **three progressive hints** per step (nudge → hint → the full answer), and a **checkpoint**
built from the normal activity components and graded through `/api/grade`
- The last screen assembles the solution from the step answers, with a copy button
- Results live in `freezmo.homework.v1` — the homework screen's history and the library's
**Homework** tab — where you can reopen or delete them; **Make it again** re-runs from the
stored assignment text, and original files are never saved
**Homework** tab — where you can reopen or delete them; **Redo** re-runs from the
stored assignment text (and the stored attempt for checks), and original files are never saved
- The answer and the guide are always written in the **language of the assignment**, never
translated into the interface language
@ -423,6 +430,7 @@ client IP with a sliding window. Everything is configurable in `.env.local`:
| `POST /api/generate` | 12 per 5 minutes | `RATE_LIMIT_GENERATE` |
| `POST /api/homework` | 12 per 5 minutes | `RATE_LIMIT_HOMEWORK` |
| `POST /api/grade` | 40 per 5 minutes | `RATE_LIMIT_GRADE` |
| `POST /api/homework/refine` | 30 per 5 minutes | `RATE_LIMIT_REFINE` |
| `GET /api/health` | 90 per 5 minutes | `RATE_LIMIT_HEALTH` |
| `POST/GET/DELETE /api/sync` | 60 per 5 minutes | `RATE_LIMIT_SYNC` |
| `GET/POST/DELETE /api/push`, `POST /api/push/test` | 30 per 5 minutes | `RATE_LIMIT_PUSH` |
@ -719,7 +727,8 @@ scripts/
| Route | Body | Returns |
| --- | --- | --- |
| `POST /api/generate` | `{ images?: string[], text?, difficulty, count?, focus?, language?, mode?, existing? }` | NDJSON stream: `{type:"stage"}`, `{type:"deck",deck,meta}`, `{type:"error"}` |
| `POST /api/homework` | `{ mode: "make" \| "guide", images?: string[], text?, language? }` | NDJSON stream: `{type:"stage"}`, `{type:"homework",homework,meta}`, `{type:"error"}` |
| `POST /api/homework` | `{ mode: "make" \| "guide" \| "check", images?: string[], text?, solution?, language? }` | NDJSON stream: `{type:"stage"}`, `{type:"homework",homework,meta}`, `{type:"error"}` |
| `POST /api/homework/refine` | `{ instruction: "shorter" \| "more" \| "explain", text, context?, language? }` | `{ text }` — one rewritten piece, plain text |
| `POST /api/grade` | `{ question, referenceAnswer, keyPoints, userAnswer, language? }` | `{ correct, score, feedback, missing }` |
| `GET /api/health` | – | `{ ok, reachable, mode, baseUrl, hasApiKey, vision, text, latencyMs, modelCount, error }` |
| `POST /api/sync` | `{ id, envelope }` (opaque AES-GCM blob) | `{ updatedAt }` — 413 over `SYNC_MAX_BYTES`, 507 over `SYNC_TOTAL_BYTES` |
@ -993,6 +1002,22 @@ Things I chose while building, with the reasoning:
second copy. Result records store the assignment text — the vision transcription when photos
were used — so “Make it again” works without storing a single original file.
42. **Check my work is a checker, not a second writer.** The third homework mode sends the
assignment and the student's attempt to one model call with its own system prompt
(`HOMEWORK_CHECK_SYSTEM_PROMPT`): the output is a verdict, a list of what is right, a list of
issues with *why* and *fix*, and the corrected version. Unasked-for extras are explicitly
never errors, and the corrected version keeps everything the student got right as they wrote
it. The attempt is typed or pasted — supporting photos of handwriting would need a second
vision pass and a way to separate assignment from attempt in the same image, so the UI says
plainly that it isn't supported yet. Follow-ups are a separate, cheaper route
(`POST /api/homework/refine`, plain JSON, `RATE_LIMIT_REFINE`) shared by the result, the check
and each guide step; they rewrite one piece and never touch the stored record. “Turn into a
study deck” hands the content to the New deck flow through sessionStorage (one-shot, like the
share target), with the options step pre-filled. Verified by the phase-3 suite (20 checks
against a stub Ollama: the check payload, the missing-solution 400, refine validation, the
phone and desktop check flows, the shorter follow-up with restore, the guide's explain action,
and the deck hand-off).
36. **The mobile declutter pass reshapes, it doesn't fork.** Phones (≤639px) get a 3-step deck
flow and a 2-step homework flow that share the exact state and handlers with the desktop
cards (one `UploadScreen` / one `HomeworkInput`, two layouts switched by `useIsPhone()`);