WR-07: when storing the grader's score fails on the last River attempt,
record the answer as failed instead of leaving it pending with no job.
The terminal failure write runs on a context detached from the job's,
so a timed-out or cancelled job still records it.
WR-06: a Question save hook clears grading_status on the answers of a
question that changed type away from open, in the save's transaction,
so they leave the review queue and needs_review. GradeAnswer ignores a
question that is no longer open, and a choice or self-check re-answer
clears the text, grade and review columns of its row.
WR-05: Confirm clamps the grader's score to the question's current
points, so lowering a question's points no longer lets a confirm store
more than full marks, and Totals caps the percentage at 100 when a
question's points were raised after the attempt was scored.
WR-03: every new or changed open answer in a learning quiz with a
grader is a paid grading call. Add per-user buckets golem15.quizzes.answer
(60/min) and golem15.quizzes.start (10/min), configurable under
golem15.quizzes.throttle.*.
WR-02: Confirm and SetScore answer ErrAttemptOpen (409 attempt_open,
an admin refusal) for an answer of a running test, ConfirmBulk skips
such attempts, and the default open queue leaves them out, so
AnswerReviewed never fires before the test ends.
WR-01: a deterministic server-side guard behind the grader's prompt.
storeGrade drops feedback sharing a run of six words with the rubric
(runs also in the question or model answer do not count) and notes it
for the owner in grading_error.
- import document, manage routes and MCP question tools take rubric and
max_length; open questions refuse answers, max_length is 1..20000 and
both apply to open questions only
- quizzes take a grader; the manage routes refuse a code the registry
does not know, the import accepts any well-formed code
- quiz stats report unreviewed open answers and their average points
- import_quiz warns that replace deletes the grades and reviews
- SetScore (0..points in 0.5 steps, comment shown to the student),
ConfirmBulk per quiz or attempt and Regrade through the quiz's grader
- close() announces an attempt reviewed while it was open
- quizzes_review_set_score, quizzes_review_confirm_bulk,
quizzes_review_regrade; 409 already_reviewed and grader_manual
- classes/review.go: review queue, detail and Confirm, shared by MCP and
the admin screen; AnswerReviewed and AttemptReviewed after commit
- /api/v1/quiz-reviews routes behind the quizzes permission
- quizzes_review_list, quizzes_review_show, quizzes_review_confirm
- graders registry collected at boot from every HasGraders plugin,
manual first; a duplicate or reserved code fails boot
- grading job on queue quizzes_grading with three attempts; a stale
or reviewed answer is never touched; the last failure marks it failed
- learning answers enqueue at once, tests at close; a grade
recalculates a finished attempt and fires AnswerGradedEvent after
commit
- migration: open question type, rubric, max_length, quiz grader and
the grading and review columns of attempt answers
- open answers are checked, stored as pending and never scored on
answer; a changed text resets the grading state
- student JSON gains text, grading_status, grader_feedback,
review_comment, max_length, needs_review and open_answers; the
rubric is never serialized
- quizzes_create_question, update_question, delete_question and
reorder_questions on /api/v1/quizzes/{slug}/questions
- update_question replaces answers by id (kept, created, deleted), so
answer ids stored in past attempts stay valid; foreign ids are refused
- classes.CheckQuestion holds the importer's per-question rules, shared
with the manage routes; attempt scores are never touched (D-16)
- quizzes_create_quiz, update_quiz, delete_quiz (confirm repeats the
slug), publish, unpublish, import_quiz (the existing import route) and
quiz_stats on new manage routes
- stats close expired open test attempts first, then report per student
over completed whole-quiz attempts and per question over every
completed attempt
- input is checked before the database; TestManageAPIAgainstDatabase is
written and skipped under -short
- quizzes_list_quizzes and quizzes_show_quiz bound to GET /api/v1/quizzes
and /api/v1/quizzes/{slug} behind user.api_token, the per-user
golem15.quizzes.manage throttle, a scope and the access_quizzes permission
- the permission is grantable through pact.HasFrontendPermissions
- owner JSON shapes with solutions in manage_json.go, never used by a
student route
- Service.Start resumes an open test attempt, closes expired ones at
their deadline and refuses a start past max_attempts under an
advisory lock; StartAttempt stays as a wrapper
- answers to a test are accepted until the deadline plus
golem15.quizzes.test.grace_seconds; one_way answers are final
- a test covers the whole quiz and keeps a stable per-attempt order
- 409 bodies carry a code; list and attempt rows carry attempts_left,
question_types and seconds_left; import accepts the test keys
- mode, navigation and max_attempts columns with Go-side defaults
- classes.Withheld gates every solution field of the answer feedback
and the attempt result while a test attempt is open
- a test's quiz detail lists no questions before the attempt starts
- admin fields and lang keys for the three settings