Commit 84b22dc
A student returning to a finished lesson checkpoint or standalone exercise
saw a bare score badge. The AI's feedback was never missing — it was always
written to `lesson_checkpoint_attempts.evaluation` and `exercise_evaluations.
ai_result`; the loaders simply never read those columns back, and every
engine held its result in component state that a reload discarded.
Restore path
- `loadLessonCheckpoints` now selects `response` and `evaluation`, and
defensive parsers turn those two free-form jsonb columns back into the
student's own answer plus the feedback, next-step hint and per-question
explanations. Four writer shapes exist (ai / deterministic / fallback /
external sync); anything unrecognised yields "nothing to show" rather than
being trusted into the UI.
- The standalone exercise page needed no new query: artifact and essay rows
were already in the `exercise_evaluations` result set fetched for the
audio/video history and were simply never passed to their components.
- Per-question feedback rendered `correctValue` raw, so a multiple-choice
miss read "Incorrect — 1" (the option *index*). It now maps to option text
and localized true/false.
- Coding challenges write no evaluation row at all — they are graded by their
test runner and store a hardcoded completion. The card says so instead of
rendering an empty result.
Legibility (issue #581, found by an /impeccable critique verified live at
390x844 and 1440x900, light and dark, en and es)
- Six WCAG AA contrast failures in light and four in dark are fixed; the card
now measures clean in both themes. The worst was the PASSED badge at 2.47:1
— white on emerald-500, the single most important word on the card. The
"AI Feedback" label no longer uses `text-primary`: tenants override that
token, so small text in it cannot be guaranteed to clear AA.
- The artifact panel printed the score over the PASS MARK, twice: "62 / 70"
reads as 89% when 62 is a failure. That whole block was a near-copy of
ExerciseResultSummary, so it now renders the shared card instead.
- A failed checkpoint showed a green tick, because its tone came from
`completed` and a failed attempt is still completed.
- The checkpoint restored behind a closed disclosure. It now opens itself for
the one state that asks something of the student — a graded attempt they did
not pass — and an explicit toggle still wins.
- The retry field sat between the answer and the grade for that answer, and
looked identical to the read-only answer above it. Order is now answer →
result → retry, and the restored answer is borderless so it does not read
as an editable field.
- Adding the result card inside `lg:sticky lg:top-6` took that container to
1078px against a 900px viewport; `sticky` does not pin an element taller
than its scrollport, so the chat input scrolled away unreachable. The card
is now a sibling above it, which also puts the result first on mobile
instead of 1157px down the page behind instructions already followed.
- Prose is capped at 68ch (was 85-90), the header stacks below `sm:`, long
unbroken input wraps, and on phones the retry button is 36px tall and
inputs are 16px so iOS Safari stops zooming the page on focus.
23 unit tests pin the parsers and the auto-expand rule.
Claude-Session: https://claude.ai/code/session_01JUBJNELGqnwbQbQNp5Qd9h
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent d110d11 commit 84b22dc
15 files changed
Lines changed: 990 additions & 137 deletions
File tree
- app/[locale]/dashboard/student/courses/[courseId]/exercises/[exerciseId]
- components
- exercises
- lesson/checkpoints
- lib
- checkpoints
- exercises
- messages
- tests/unit
Lines changed: 56 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| 8 | + | |
| 9 | + | |
8 | 10 | | |
9 | 11 | | |
10 | 12 | | |
| |||
171 | 173 | | |
172 | 174 | | |
173 | 175 | | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
174 | 180 | | |
175 | | - | |
| 181 | + | |
176 | 182 | | |
177 | 183 | | |
178 | 184 | | |
179 | 185 | | |
180 | 186 | | |
181 | 187 | | |
182 | 188 | | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
183 | 193 | | |
184 | 194 | | |
185 | 195 | | |
| |||
192 | 202 | | |
193 | 203 | | |
194 | 204 | | |
195 | | - | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
196 | 208 | | |
| 209 | + | |
197 | 210 | | |
198 | 211 | | |
199 | 212 | | |
| |||
267 | 280 | | |
268 | 281 | | |
269 | 282 | | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
270 | 310 | | |
271 | 311 | | |
272 | 312 | | |
| |||
287 | 327 | | |
288 | 328 | | |
289 | 329 | | |
| 330 | + | |
290 | 331 | | |
291 | 332 | | |
292 | 333 | | |
| |||
325 | 366 | | |
326 | 367 | | |
327 | 368 | | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
328 | 381 | | |
329 | 382 | | |
330 | 383 | | |
| |||
355 | 408 | | |
356 | 409 | | |
357 | 410 | | |
| 411 | + | |
358 | 412 | | |
359 | 413 | | |
360 | 414 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| 6 | + | |
6 | 7 | | |
7 | 8 | | |
8 | 9 | | |
| |||
16 | 17 | | |
17 | 18 | | |
18 | 19 | | |
19 | | - | |
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
43 | 46 | | |
44 | 47 | | |
45 | 48 | | |
| |||
58 | 61 | | |
59 | 62 | | |
60 | 63 | | |
| 64 | + | |
61 | 65 | | |
62 | 66 | | |
63 | 67 | | |
| |||
68 | 72 | | |
69 | 73 | | |
70 | 74 | | |
71 | | - | |
| 75 | + | |
72 | 76 | | |
73 | 77 | | |
74 | 78 | | |
| |||
303 | 307 | | |
304 | 308 | | |
305 | 309 | | |
306 | | - | |
307 | | - | |
308 | | - | |
309 | | - | |
310 | | - | |
311 | | - | |
312 | | - | |
313 | | - | |
314 | | - | |
315 | | - | |
316 | | - | |
317 | | - | |
318 | | - | |
319 | | - | |
320 | | - | |
321 | | - | |
322 | | - | |
323 | | - | |
324 | | - | |
325 | | - | |
326 | | - | |
327 | | - | |
328 | | - | |
329 | | - | |
330 | | - | |
331 | | - | |
332 | | - | |
333 | | - | |
334 | | - | |
335 | | - | |
336 | | - | |
337 | | - | |
338 | | - | |
339 | | - | |
340 | | - | |
341 | | - | |
342 | | - | |
343 | | - | |
344 | | - | |
345 | | - | |
346 | | - | |
347 | | - | |
348 | | - | |
349 | | - | |
350 | | - | |
351 | | - | |
352 | | - | |
353 | | - | |
354 | | - | |
355 | | - | |
356 | | - | |
357 | | - | |
358 | | - | |
359 | | - | |
360 | | - | |
361 | | - | |
362 | | - | |
363 | | - | |
364 | | - | |
365 | | - | |
366 | | - | |
367 | | - | |
368 | | - | |
369 | | - | |
370 | | - | |
371 | | - | |
372 | | - | |
373 | | - | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
374 | 327 | | |
375 | | - | |
376 | 328 | | |
377 | | - | |
378 | | - | |
379 | | - | |
380 | | - | |
381 | | - | |
382 | | - | |
383 | | - | |
384 | | - | |
385 | | - | |
386 | | - | |
387 | | - | |
388 | | - | |
389 | | - | |
390 | | - | |
391 | | - | |
392 | | - | |
393 | | - | |
394 | | - | |
395 | | - | |
396 | | - | |
397 | | - | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
398 | 336 | | |
399 | 337 | | |
400 | 338 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| 15 | + | |
| 16 | + | |
15 | 17 | | |
16 | 18 | | |
17 | 19 | | |
18 | 20 | | |
19 | 21 | | |
20 | 22 | | |
| 23 | + | |
21 | 24 | | |
22 | 25 | | |
23 | 26 | | |
| |||
42 | 45 | | |
43 | 46 | | |
44 | 47 | | |
| 48 | + | |
| 49 | + | |
45 | 50 | | |
46 | 51 | | |
47 | 52 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
| 20 | + | |
19 | 21 | | |
20 | 22 | | |
21 | 23 | | |
| |||
40 | 42 | | |
41 | 43 | | |
42 | 44 | | |
| 45 | + | |
43 | 46 | | |
44 | 47 | | |
45 | 48 | | |
| |||
94 | 97 | | |
95 | 98 | | |
96 | 99 | | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
97 | 108 | | |
98 | 109 | | |
99 | 110 | | |
| |||
0 commit comments