From 7f6f14e63dbf6a78e5b2e878fac2490940835c45 Mon Sep 17 00:00:00 2001 From: sfw Date: Fri, 20 Mar 2026 16:59:26 -0600 Subject: [PATCH] Hide answer feedback until after submission Option body text (explanations, "Correct!" labels) was visible before the learner submitted their answer, giving away the correct choice. Now option.body is hidden until after submission, and correct/incorrect styling is applied post-submit. The LLM prompt schema also explicitly instructs that option body must never reveal correctness. Co-Authored-By: Claude Opus 4.6 --- .../content/InteractivePracticeBlock.tsx | 33 ++++++++++++++----- src/dibble/services/llm_prompting.py | 17 ++++++---- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/frontend/src/components/content/InteractivePracticeBlock.tsx b/frontend/src/components/content/InteractivePracticeBlock.tsx index ffaefc2..9a3be54 100644 --- a/frontend/src/components/content/InteractivePracticeBlock.tsx +++ b/frontend/src/components/content/InteractivePracticeBlock.tsx @@ -1,5 +1,5 @@ import { useRef, useState } from 'react' -import { CheckCircle2, Circle, ArrowRight } from 'lucide-react' +import { CheckCircle2, Circle, XCircle, ArrowRight } from 'lucide-react' import { Button } from '@/components/ui/button' import { Textarea } from '@/components/ui/textarea' @@ -26,6 +26,7 @@ export function InteractivePracticeBlock({ const interaction = block.interaction const startedAt = useRef(null) const [selectedOptionId, setSelectedOptionId] = useState(null) + const [submitted, setSubmitted] = useState(false) const [responseText, setResponseText] = useState('') if (interaction?.type !== 'multiple_choice') { @@ -47,11 +48,14 @@ export function InteractivePracticeBlock({

{interaction.prompt}

{interaction.options.map((option) => { const selected = option.option_id === selectedOptionId + const isCorrectOption = option.option_id === interaction.correct_option_id + const showCorrect = submitted && isCorrectOption + const showIncorrect = submitted && selected && !isCorrectOption return ( @@ -107,6 +121,7 @@ export function InteractivePracticeBlock({ if (!selectedOptionId) { return } + setSubmitted(true) onSubmit({ blockId: block.block_id ?? block.title, selectedOptionId, diff --git a/src/dibble/services/llm_prompting.py b/src/dibble/services/llm_prompting.py index cdc8cdb..f1e4aae 100644 --- a/src/dibble/services/llm_prompting.py +++ b/src/dibble/services/llm_prompting.py @@ -277,13 +277,17 @@ def _block_schema_contract(content_type: RequestedContentType) -> str: '{"kind":"summary","title":"...","body":"..."},' '{"kind":"practice_problem","title":"...","body":"...",' '"interaction":{"type":"multiple_choice","prompt":"...","options":[' - '{"option_id":"A","label":"Option A","body":"..."},' - '{"option_id":"B","label":"Option B","body":"..."}],' + '{"option_id":"A","label":"Option A","body":"why a student might pick this"},' + '{"option_id":"B","label":"Option B","body":"why a student might pick this"}],' '"correct_option_id":"B",' '"reveal":{"trigger":"after_selection","prompt":"...","support":"...","placeholder":"..."}}}' ']}. Always include at least one summary block and one practice_problem block. ' "For the practice_problem block, put the learner's actual choice set in interaction.options, " - "keep body to a short cue, and use plain text only." + "keep body to a short cue, and use plain text only. " + "CRITICAL: option body must NEVER reveal whether the option is correct or incorrect. " + "Do not use words like 'Correct', 'Right', 'Wrong', or 'Incorrect' in option body. " + "Option body should describe the reasoning or common misconception behind the choice, " + "not whether it is the right answer. Correctness feedback is shown separately after submission." ) return ( '{"blocks":[{"kind":"summary","title":"...","body":"..."},{"kind":"instruction","title":"...","body":"..."}]}. ' @@ -296,11 +300,12 @@ def _stream_schema_contract(content_type: RequestedContentType) -> str: return ( '{"block_index":0,"block":{"kind":"practice_problem","title":"...","body":"...",' '"interaction":{"type":"multiple_choice","prompt":"...","options":[' - '{"option_id":"A","label":"Option A","body":"..."},' - '{"option_id":"B","label":"Option B","body":"..."}],' + '{"option_id":"A","label":"Option A","body":"why a student might pick this"},' + '{"option_id":"B","label":"Option B","body":"why a student might pick this"}],' '"correct_option_id":"B",' '"reveal":{"trigger":"after_selection","prompt":"...","support":"...","placeholder":"..."}}},"done":true}. ' - "Emit one complete block object per line for interactive practice blocks." + "Emit one complete block object per line for interactive practice blocks. " + "Option body must NEVER reveal whether the option is correct or incorrect." ) return ( '{"block_index":0,"kind":"summary","title":"...","body_delta":"...","done":true}. '