Answering a check advances the lesson (#9) #13
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/answer-advances-lesson"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #9.
Problem
POST .../check-answergraded the answer, recordedobjective_evidenceand appended the learner line — and stopped. Notutor_turnop was created, so the tutor never reacted and the lesson stalled until the learner typed something like "I got it!" by hand.Fix
The grade now starts the follow-up
tutor_turnop with a new synthetic learner intent__advance__(mirroring__begin__). The tutor opens with the verdict — confirm a correct answer, explain the misconception for a wrong one, give prerequisite-level remediation for "I don't know" (REQ-008.2/3) — and continues the lesson through the normalcheckpoint_savedtransact, on every outcome.Backend (
plugin/)check-answerkeeps its synchronous 200 grade contract; the response gains an optionalfollow_up_op_id.{check-answer-key}:advance, so a replayed grade (same key → early return of the recorded result, which still carries the original op id) never starts a second turn. A second answer with a different key is refused before any op starts.follow_up_op_id: null): the learner keeps the inline feedback and can simply send a message (single-flight discipline preserved)._tutor_handlerreturns the existing tutor line for a supersededADVANCEop (BEGIN-style guard: a tutor line already follows the lastcheck_answerline), so a retry never duplicates the verdict.sayrejects__advance__as reserved text;tutor.detect_challengeshort-circuits it (no model call — exactly oneTutorTurncall per follow-up).tutor.INSTRUCTIONSdocument the new intent: verdict first, then continue teaching / offer homework when every objective is covered.Desktop (
desktop/src/)CheckAnswerResultcarriesfollow_up_op_id;ModulePage.onAnsweredadopts it as the tracked say op, reusing all existing machinery (pending label, transcript poll, transcript + course refetch on success).retrySaystale-text fallback never fires for an advance op, so a failed verdict turn can never resend an old learner message.Docs
docs/specs/pedagogy-plugin/design.md: check-answer route line notes the follow-up turn; module/route docstrings updated (REQ-008.2/3, issue #9).Verification
pytest tests/ -q: 453 passed; the 5 failures are the known pre-existing Windows failures onmain(4×test_install.py,test_events.py::test_list_courses_skips_invalid_directory_names).learner_message == "__advance__",checkpoint_savedfrom the verdict), skip-while-in-flight, superseded-retry no-op, reserved-text refusal, and every existing check-answer flow now exercises the follow-up.npm run typecheck,npm test(21/21),npm run build(lint gate ok, 315 KiB).POST .../check-answer graded the answer, recorded objective_evidence and appended the learner line — and stopped. The tutor never reacted, so the lesson stalled until the learner typed something like "I got it!" by hand. The grade now starts the follow-up tutor_turn op with a new synthetic learner intent __advance__ (mirroring __begin__): the tutor opens with the verdict — confirm a correct answer, explain the misconception for a wrong one, give prerequisite-level remediation for "I don't know" (REQ-008.2/3) — and continues the lesson via the normal checkpoint_saved transact, on every outcome. - check-answer keeps its synchronous 200 grade contract and gains an optional follow_up_op_id; the derived idempotency key {key}:advance means a replayed grade never starts a second turn. - When a tutor turn is already in flight the follow-up is skipped (follow_up_op_id null): the learner keeps the inline feedback and can simply send a message. - The tutor handler returns the existing tutor line for a superseded ADVANCE op (BEGIN-style guard), so a retry never duplicates the verdict. - say rejects __advance__ as reserved; detect_challenge short-circuits it (no model call). - Desktop: ModulePage tracks follow_up_op_id like a say op (pending label, transcript poll, refetch on success); the stale-text retry fallback never fires for an advance op, so no old learner message is ever resent. Closes #9