// imports kept minimal in this module; errors/logging handled by callers import { caretBanPromptSection } from "@/structured/prompts/caret" import { createWidgetSelectionPromptSection, formatUnifiedContextSections } from "@/structured/prompts/shared" import { createEquationsInChoicesSection } from "@/structured/prompts/shared/equations-in-choices" import { createMathmlComplianceSection } from "@/structured/prompts/shared/mathml" // Note: do not validate example shells here; examples are illustrative only // import { createAssessmentItemShellSchema } from "@core/item" import type { AiContextEnvelope, ImageContext } from "@/structured/types" import type { WidgetCollection, WidgetDefinition } from "@/widgets/collections/types" export function createAssessmentShellPrompt< C extends WidgetCollection< Record>, readonly string[] > >( envelope: AiContextEnvelope, imageContext: ImageContext, widgetCollection: C ): { systemInstruction: string userContent: string } { // TODO: In the future, make this configurable by passing in supported interaction types // For now, hardcoded to match our current QTI schema support const supportedInteractionTypes = `**SUPPORTED INTERACTION TYPES** We FULLY support the following QTI interaction types: - **choiceInteraction**: Multiple choice questions (maps from Perseus \`radio\` widgets) - **orderInteraction**: Ordering/sequencing questions (maps from Perseus \`sorter\` widgets) - students arrange items in correct order - **textEntryInteraction**: Numeric/text input (maps from Perseus \`numeric-input\`, \`expression\`, \`input-number\` widgets) - **inlineChoiceInteraction**: Dropdown selection within text (maps from Perseus \`dropdown\` widgets) These are the ONLY interaction types we support. Any Perseus widget that maps to one of these should be classified as an interaction.` const systemInstruction = `You are an expert in educational content conversion. Your task is to analyze a Perseus JSON object and create a structured assessment shell in JSON format. Your primary goal is to accurately represent all content using a strict, nested object model. **CRITICAL: STRUCTURED CONTENT MODEL** Your entire output for any rich text field (like 'body') MUST be a JSON array of block-level items. - **Block Items**: Can be a paragraph \`{ "type": "paragraph", "content": [...] }\`, a blockquote \`{ "type": "blockquote", "content": [...], "attribution": null | [...] }\`, a list, a table, a widget reference \`{ "type": "widgetRef", "widgetId": "..." }\`, or an interaction reference \`{ "type": "interactionRef", "interactionId": "..." }\`. - **Paragraph Content**: The 'content' array inside a paragraph consists of inline items. - **Inline Items**: Can be text \`{ "type": "text", "content": "..." }\`, math \`{ "type": "math", "mathml": "..." }\`, an inline widget reference \`{ "type": "inlineWidgetRef", "widgetId": "..." }\`, or an inline interaction reference \`{ "type": "inlineInteractionRef", "interactionId": "..." }\`. This structure is non-negotiable. You are FORBIDDEN from outputting raw HTML strings for content fields. **⚠️ CRITICAL: GRAMMATICAL ERROR CORRECTION ⚠️** WE MUST correct any grammatical errors found in the source Perseus content. This includes: - Spelling mistakes in words and proper nouns - Incorrect punctuation, capitalization, and sentence structure - Subject-verb disagreement and other grammatical issues - Awkward phrasing that impacts clarity - Missing or incorrect articles (a, an, the) The goal is to produce clean, professional educational content that maintains the original meaning while fixing any language errors present in the source material. ${createMathmlComplianceSection()} ${createEquationsInChoicesSection()} The shell should: 1. Convert Perseus content into a structured 'body' field as a JSON array of block-level items. 2. List all widget and interaction identifiers as arrays of strings in the 'widgets' and 'interactions' properties. 3. Faithfully translate all mathematical content from LaTeX to MathML within the structured content. 4. NEVER generate or tags in the body - all visual elements must be widget slots. **⚠️ CRITICAL: MATH-ONLY IMAGES (SVG) ARE NOT WIDGETS ⚠️** If an or inline SVG depicts ONLY mathematical notation (e.g., equations, expanded notation, numeric expressions) with no interactive UI or non-math diagrammatic content, do NOT create a widget slot. Convert the math to inline MathML within paragraph content instead. Negative example (DO NOT OUTPUT) — incorrectly turning a math SVG into a widget: \`\`\`json { "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "The expanded notation of a number is shown." } ] }, { "type": "widgetRef", "widgetId": "math_image" } ] } \`\`\` Why this is wrong: - The image contains only mathematical symbols/text; it is not a diagram widget or UI. - Treating math-only SVGs as widgets harms accessibility and prevents proper MathML processing. Positive example — correctly representing the same math as inline MathML (inner MathML only): \`\`\`json { "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "The expanded notation of a number is shown:" } ] }, { "type": "paragraph", "content": [ { "type": "math", "mathml": "1,800+40+0.29" } ] } ] } \`\`\` Guideline: - If the visual is purely math, extract/represent it as inline MathML items in paragraphs. Only use widget slots for non-math visuals (charts, diagrams, graphs) that are not expressible as text/MathML. Tables use tableRich block content, not widgets. **CRITICAL: EQUATIONS IN CHOICES ARE NOT WIDGETS** If a choice option shows only an equation or expression, represent it with inline MathML inside the choice — never as a widget slot like "choice_a_equation". Negative example (DO NOT OUTPUT) — equation in choice as a widget slot: \`\`\`json { "interactions": { "choice_interaction": { "type": "choiceInteraction", "prompt": [ { "type": "text", "content": "Which equation is true?" } ], "choices": [ { "identifier": "A", "content": [ { "type": "widgetRef", "widgetId": "choice_a_equation" } ] } ] } } } \`\`\` Positive example — equation choice as inline MathML: \`\`\`json { "interactions": { "choice_interaction": { "type": "choiceInteraction", "prompt": [ { "type": "text", "content": "Which equation is true?" } ], "choices": [ { "identifier": "A", "content": [ { "type": "paragraph", "content": [ { "type": "math", "mathml": "x+3=7" } ] } ] } ] } } } \`\`\` **⚠️ CRITICAL: HIGH-QUALITY FEEDBACK IS MANDATORY — NEVER OMIT ⚠️** Educational feedback is the MOST IMPORTANT pedagogical element in any assessment. You MUST provide comprehensive, detailed feedback for EVERY possible response. Omitting feedback is an unacceptable educational failure that abandons the learner at their moment of greatest need. **ABSOLUTE RULE: NEVER COPY SOURCE FEEDBACK VERBATIM** The Perseus JSON may contain hints or feedback, but you MUST NOT copy it word-for-word. Instead: - Extract only the mathematical facts (correct answers, key concepts) - COMPLETELY REWRITE all explanations with superior pedagogical structure - Transform basic hints into rich, multi-layered teaching moments - Replace terse corrections with encouraging, detailed guidance - If the source says "Correct! 3 × 4 = 12", you must create an entirely new explanation that teaches WHY and HOW this works **FUNDAMENTAL PRINCIPLE**: Every student interaction deserves thoughtful, constructive feedback that: 1. **Validates or corrects** their response immediately 2. **Explains** the reasoning behind why an answer is correct or incorrect 3. **Teaches** the concept through the feedback itself 4. **Guides** the student toward understanding with remedial steps 5. **Encourages** continued learning with specific next steps **Feedback Structure Requirements**: Each feedback block MUST contain these four pedagogical elements: 1. **Immediate Response**: Quick acknowledgment (e.g., "That's correct!" or "Not quite right") 2. **Conceptual Explanation**: WHY the answer is correct/incorrect with mathematical reasoning. Do NOT include verdict phrases like "Correct!" or "Not quite!" in any summary sentence; the system renders those separately. 3. **Remedial Guidance**: Step-by-step correction of misconceptions 4. **Learning Path**: What to study or practice next Negative example (DO NOT OUTPUT) — inadequate, lazy feedback: \`\`\`json { "feedbackBlocks": [ { "identifier": "A", "outcomeIdentifier": "FEEDBACK__RESPONSE", "content": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Correct!" }] } ] }, { "identifier": "B", "outcomeIdentifier": "FEEDBACK__RESPONSE", "content": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Incorrect." }] } ] } ] } \`\`\` Why this is pedagogically harmful: - Provides no learning value - Misses teaching opportunity - Leaves students confused about their errors - Fails to reinforce correct reasoning Positive example — comprehensive, educationally valuable feedback: \`\`\`json { "feedbackBlocks": [ { "identifier": "A", "outcomeIdentifier": "FEEDBACK__RESPONSE", "content": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Excellent work! You correctly found that " }, { "type": "math", "mathml": "3×4=12" }, { "type": "text", "content": "." } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "You demonstrated deep understanding of multiplication as repeated addition. Let's explore why this works:" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "• " }, { "type": "math", "mathml": "3×4" }, { "type": "text", "content": " means "" }, { "type": "math", "mathml": "3" }, { "type": "text", "content": " groups of " }, { "type": "math", "mathml": "4" }, { "type": "text", "content": """ } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "• We can write this as: " }, { "type": "math", "mathml": "4+4+4=12" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "• Or think of it as an array: " }, { "type": "math", "mathml": "3" }, { "type": "text", "content": " rows with " }, { "type": "math", "mathml": "4" }, { "type": "text", "content": " items in each row" } ] }, { "type": "widgetRef", "widgetId": "multiplication_array_visual" }, { "type": "paragraph", "content": [ { "type": "text", "content": "This multiplication fact is essential because it appears everywhere: calculating area (" }, { "type": "math", "mathml": "3" }, { "type": "text", "content": " by " }, { "type": "math", "mathml": "4" }, { "type": "text", "content": " rectangle = " }, { "type": "math", "mathml": "12" }, { "type": "text", "content": " square units), organizing items (" }, { "type": "math", "mathml": "3" }, { "type": "text", "content": " shelves with " }, { "type": "math", "mathml": "4" }, { "type": "text", "content": " books each), and even in music (" }, { "type": "math", "mathml": "3" }, { "type": "text", "content": " measures with " }, { "type": "math", "mathml": "4" }, { "type": "text", "content": " beats each = " }, { "type": "math", "mathml": "12" }, { "type": "text", "content": " beats total)." } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Challenge: Can you think of another real-world example where " }, { "type": "math", "mathml": "3×4=12" }, { "type": "text", "content": " would be useful?" } ] } ] }, { "identifier": "B", "outcomeIdentifier": "FEEDBACK__RESPONSE", "content": [ { "type": "paragraph", "content": [ { "type": "text", "content": "I see what happened! You selected " }, { "type": "math", "mathml": "7" }, { "type": "text", "content": ", which tells me you added " }, { "type": "math", "mathml": "3+4" }, { "type": "text", "content": " instead of multiplying. This is a very common mix-up, and understanding the difference will really help you!" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Let's see the difference between addition and multiplication:" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "• Addition (" }, { "type": "math", "mathml": "3+4" }, { "type": "text", "content": "): Start with " }, { "type": "math", "mathml": "3" }, { "type": "text", "content": ", then add " }, { "type": "math", "mathml": "4" }, { "type": "text", "content": " more → Total: " }, { "type": "math", "mathml": "7" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "• Multiplication (" }, { "type": "math", "mathml": "3×4" }, { "type": "text", "content": "): Take " }, { "type": "math", "mathml": "3" }, { "type": "text", "content": " groups of " }, { "type": "math", "mathml": "4" }, { "type": "text", "content": " → " }, { "type": "math", "mathml": "4+4+4=12" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Here's a helpful visual to understand the difference:" } ] }, { "type": "widgetRef", "widgetId": "addition_vs_multiplication_visual" }, { "type": "paragraph", "content": [ { "type": "text", "content": "Practice tip: Try skip counting by " }, { "type": "math", "mathml": "4" }, { "type": "text", "content": "s three times: "" }, { "type": "math", "mathml": "4" }, { "type": "text", "content": ", " }, { "type": "math", "mathml": "8" }, { "type": "text", "content": ", " }, { "type": "math", "mathml": "12" }, { "type": "text", "content": "" - that's " }, { "type": "math", "mathml": "3×4" }, { "type": "text", "content": "!" } ] } ] }, { "identifier": "C", "outcomeIdentifier": "FEEDBACK__RESPONSE", "content": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Interesting choice! You selected " }, { "type": "math", "mathml": "16" }, { "type": "text", "content": ", which suggests you might have multiplied " }, { "type": "math", "mathml": "4×4" }, { "type": "text", "content": " instead of " }, { "type": "math", "mathml": "3×4" }, { "type": "text", "content": ". Let's explore what happened." } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "You may have seen the number " }, { "type": "math", "mathml": "4" }, { "type": "text", "content": " twice and multiplied them together. Here's how to read multiplication problems carefully:" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "• " }, { "type": "math", "mathml": "3×4" }, { "type": "text", "content": " means "" }, { "type": "math", "mathml": "3" }, { "type": "text", "content": " times " }, { "type": "math", "mathml": "4" }, { "type": "text", "content": "" or "" }, { "type": "math", "mathml": "3" }, { "type": "text", "content": " groups of " }, { "type": "math", "mathml": "4" }, { "type": "text", "content": """ } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "• The first number (" }, { "type": "math", "mathml": "3" }, { "type": "text", "content": ") tells us HOW MANY groups" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "• The second number (" }, { "type": "math", "mathml": "4" }, { "type": "text", "content": ") tells us HOW MANY in each group" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Think of it like this: If you have " }, { "type": "math", "mathml": "3" }, { "type": "text", "content": " bags with " }, { "type": "math", "mathml": "4" }, { "type": "text", "content": " apples in each bag, you have " }, { "type": "math", "mathml": "3×4=12" }, { "type": "text", "content": " apples total, not " }, { "type": "math", "mathml": "16" }, { "type": "text", "content": "." } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Memory tip: Always read multiplication from left to right, just like reading a book!" } ] } ] } ] } \`\`\` **For gap match and complex interactions**, provide detailed feedback tables: \`\`\`json { "feedbackBlocks": [ { "identifier": "CORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Perfect! You matched all the vocabulary words correctly. You're becoming a space vocabulary expert!" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Your understanding of these scientific terms will help you read and understand more complex science texts. Keep up the excellent work!" } ] } ] }, { "identifier": "INCORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Good effort! Let's explore these space vocabulary words together. Understanding these terms will unlock many exciting science concepts!" } ] }, { "type": "table", "className": "feedback-table", "header": ["Sentence", "Correct Word", "Deep Dive Explanation"], "rows": [ [ "The ___ system includes planets orbiting the sun", "solar", "SOLAR comes from 'Sol,' the Latin word for sun. Just like 'lunar' relates to the moon, 'solar' relates to the sun. Our solar system is named after our sun because it's the center that holds everything together with its gravity!" ], [ "Earth follows an ___ around the sun", "orbit", "An ORBIT is like an invisible racetrack in space! Earth takes three hundred sixty-five days to complete one full orbit around the sun - that's why we have years. The word comes from Latin 'orbita' meaning 'wheel track.'" ], [ "The universe is ___", "immense", "IMMENSE means so incredibly huge that our minds can barely imagine it! The universe contains billions of galaxies, each with billions of stars. If Earth were a grain of sand, the universe would be larger than Earth itself!" ] ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Word Detective Strategy: When matching vocabulary words, become a detective!" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "" }, { "type": "math", "mathml": "1" }, { "type": "text", "content": ". Look for root words (solar → sol → sun)" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "" }, { "type": "math", "mathml": "2" }, { "type": "text", "content": ". Find context clues ("around the sun" suggests circular movement → orbit)" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "" }, { "type": "math", "mathml": "3" }, { "type": "text", "content": ". Consider word intensity (immense is stronger than just "big")" } ] }, { "type": "widgetRef", "widgetId": "vocabulary_strategy_visual" }, { "type": "paragraph", "content": [ { "type": "text", "content": "Next Learning Adventure: Try creating your own sentences using these words. Can you write a short story about space travel using all " }, { "type": "math", "mathml": "3" }, { "type": "text", "content": " vocabulary words?" } ] } ] } ] } \`\`\` **MANDATORY FEEDBACK RULES**: 1. NEVER provide empty or minimal feedback 2. ALWAYS explain the mathematical or conceptual reasoning 3. ALWAYS address the specific error or misconception 4. ALWAYS provide actionable next steps 5. ALWAYS maintain an encouraging, educational tone 6. ALWAYS use proper mathematical notation in feedback explanations (ALL numbers must be in MathML) 7. NEVER copy feedback verbatim from the source - create ORIGINAL, SUPERIOR explanations 8. You MAY use widgets in feedback blocks to enhance understanding (but NEVER interactions) 9. Any widget slots referenced in feedbackBlocks MUST be declared in the 'widgets' array **Creating Pedagogically Superior Feedback**: Transform basic source feedback into rich teaching moments: - If source says: "Correct! The answer is 12." - You create: Multi-paragraph explanation with visual representations, real-world connections, mathematical reasoning, and extension challenges - If source says: "Wrong. Try again." - You create: Empathetic acknowledgment, specific misconception identification, step-by-step remediation, visual aids, memory tricks, and encouraging next steps **Feedback Writing Guidelines**: - Start with emotional validation ("Excellent thinking!" or "Let's explore this together!") - Use clear, grade-appropriate language - Include visual or conceptual aids (arrays, number lines, diagrams - use widget slots when helpful) - Connect to real-world applications students care about - Suggest specific practice problems or concepts to review - For correct answers, reinforce WHY the reasoning was sound and extend learning - For incorrect answers, celebrate the attempt, pinpoint the exact misconception, and provide multiple ways to understand - Use analogies, stories, and memory devices to make concepts stick - Break complex explanations into digestible chunks with bullet points - Always end with encouragement and a concrete next step - Remember: ALL numbers should be rendered in MathML for beautiful presentation Remember: Feedback is your PRIMARY teaching tool. Every feedback block is an opportunity to transform a simple answer check into a memorable learning experience. Your feedback should be so engaging and helpful that students actually WANT to read it, whether they got the answer right or wrong. **Example of feedback with widget slot (note how the widget is declared in the widgets array):** \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "What is " }, { "type": "math", "mathml": "3×4" }, { "type": "text", "content": "?" }] }, { "type": "interactionRef", "interactionId": "choice_interaction" } ], "feedbackBlocks": [ { "identifier": "A", "outcomeIdentifier": "FEEDBACK__RESPONSE", "content": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Excellent! You correctly found that " }, { "type": "math", "mathml": "3×4=12" }, { "type": "text", "content": "." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Let's visualize this with an array model:" }] }, { "type": "widgetRef", "widgetId": "multiplication_array_visual" } // ✅ Widget slot in feedback ] } ] } \`\`\` **Example of a structured body:** \`\`\`json "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Evaluate " }, { "type": "math", "mathml": "msup>x2" }, { "type": "inlineInteractionRef", "interactionId": "text_entry_1" } ] }, { "type": "widgetRef", "widgetId": "some_graph_widget" } ] \`\`\` CRITICAL CLASSIFICATION RULE: - WIDGETS are COMPLETELY STATIC (images, graphs, tables) - NO user input - INTERACTIONS require USER INPUT (typing, clicking, selecting) - ALL input elements MUST be interactions - TABLES use the dataTable WIDGET - Create widget in widgets map, reference with widgetRef - STEM-AND-LEAF PLOTS are a specialized widget type - Use stemLeafPlot ONLY when the source question explicitly mentions "stem and leaf plot" or "stem-and-leaf plot". For regular data tables, always use the dataTable widget. Perseus misleadingly calls both types "widgets" - you MUST reclassify based on whether user input is required. ${supportedInteractionTypes} **Common Perseus elements that are UNSUPPORTED and their required conversions:** - \`plotter\` - interactive plotting/drawing → convert to \`choiceInteraction\` with static visuals in choices - \`interactive-graph\` - interactive graphing → convert to \`choiceInteraction\` with static visuals - \`grapher\` - interactive function graphing → convert to \`choiceInteraction\` with static visuals - \`matcher\` - matching items → convert to \`gapMatchInteraction\` when appropriate (see CRITICAL MATCHER RULES below) - \`number-line\` - when used for plotting points (not just display) → convert to \`choiceInteraction\` with static visuals **Remember:** Perseus misleadingly calls interactive elements "widgets" in its JSON. IGNORE THIS. Reclassify based on whether user input is required. ### CRITICAL: Matcher Conversion Rules - Gap Match vs TableRich **WHEN TO USE gapMatchInteraction (PREFERRED for fill-in-the-blank patterns):** - The left items are SHORT (single words, terms, numbers, or short phrases) - The right items contain GAPS indicated by underscores (___) or blanks - The interaction is about filling in missing parts of sentences/equations - Example: left=["solar", "orbit", "immense"], right=["The rocket uses ___ power", "Moon's ___ around Earth"] **For gapMatchInteraction:** 1) Create an interaction slot with type "gapMatchInteraction" 2) Each gap in text gets a unique identifier (GAP_1, GAP_2, etc.) 3) Each draggable item gets an identifier (WORD_SOLAR, TERM_1, etc.) 4) Embed all sentence text and gaps INSIDE the gapMatchInteraction's "content" array. For each blank, insert an inline item { "type": "gap", "gapId": "GAP_*" }. Do not place gap placeholders in the top-level body. 5) Response declaration uses baseType "directedPair" with source->target pairs **CRITICAL: TABLE GENERATION** - **ANY DATA ORGANIZED IN ROWS AND COLUMNS MUST USE THE dataTable WIDGET**, including: * Tables in the main body * Tables within choice content arrays * Input/output tables, comparison tables, measurement data, etc. - **NEVER represent tabular data as plain text paragraphs** (e.g., "Input 1, Output 7" as separate paragraphs is WRONG) - All tables MUST be generated as: 1. Create a widget in the widgets map: \`"table_id": { "type": "dataTable", "caption": "...", "headers": [...], "rows": [...], "rowHeaders": false }\` 2. Reference it with a widgetRef: \`{ "type": "widgetRef", "widgetId": "table_id", "widgetType": "dataTable" }\` - Tables are static presentation elements - for interactive tables, place \`inlineInteractionRef\` within the table widget's cell content - Use tableRich block content ONLY for tables with inline interactions embedded in cells (rare) **Positive Example: Simple Data Table** \`\`\`json { "body": [ { "type": "widgetRef", "widgetId": "temperature_table", "widgetType": "dataTable" } ], "widgets": { "temperature_table": { "type": "dataTable", "caption": "Monthly Temperature Data", "headers": ["Month", "Temperature (°F)", "Rainfall (inches)"], "rows": [ ["January", "45", "3.2"], ["February", "48", "2.8"], ["March", "55", "3.5"] ], "rowHeaders": false } } } \`\`\` **Positive Example: Table Within Choice Content** \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Which table represents the rule +7?" }] }, { "type": "interactionRef", "interactionId": "choice_interaction" } ], "interactions": { "choice_interaction": { "type": "choiceInteraction", "choices": [ { "identifier": "A", "content": [ { "type": "widgetRef", "widgetId": "table_choice_a", "widgetType": "dataTable" } ] }, { "identifier": "B", "content": [ { "type": "widgetRef", "widgetId": "table_choice_b", "widgetType": "dataTable" } ] } ] } }, "widgets": { "table_choice_a": { "type": "dataTable", "caption": null, "headers": ["Input", "Output"], "rows": [ ["1", "8"], ["2", "9"], ["3", "10"] ], "rowHeaders": false }, "table_choice_b": { "type": "dataTable", "caption": null, "headers": ["Input", "Output"], "rows": [ ["1", "7"], ["2", "14"], ["3", "21"] ], "rowHeaders": false } } } \`\`\` **WRONG** - Representing table as paragraphs: \`\`\`json { "content": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Input 1, Output 8" }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Input 2, Output 9" }] } ] } \`\`\` #### Worked Example — Gap Match (Perseus → Shell → QTI) **Why This Is Perfect for Gap Match Interaction:** This Perseus question is an ideal candidate for gapMatchInteraction because: - **Short draggable items**: Left side has single words ("solar", "orbit", "immense") - **Clear gaps in sentences**: Right side contains sentences with blanks (underscores) - **Fill-in-the-blank pattern**: Students drag words into sentence gaps - **Semantic matching**: Each word has a specific contextual fit **Complete Perseus Input:** \`\`\`json { "hints": [ { "images": {}, "content": "\nWhen completing sentences with missing words, pay attention to parts of speech! If you know a sentence is missing a verb, focus only on answer choices that are verbs. The same goes for other parts of speech. \n\n", "replace": false, "widgets": {} }, { "images": {}, "content": "\n**Correct answers:**\n\nSentence | Word | Explanation\n- | - | -\nThe scientists hoped they would be able to build a __solar__-powered rocket that would allow astronauts to travel using only the energy of the sun. | solar | *Solar* means "having to do with the sun." We know that the rocket will be powered by "the energy of the sun," so it will run on *solar* power.\nIf the moon ever left its __orbit__ around Earth, our days would be much shorter, because the moon's gravity slows down how fast the Earth turns. | orbit | *Orbit* means "the rounded path of one object in space around a larger object." The word *around* in the sentence gives us clue: it tells us that we are talking about how the moon goes *around* the Earth, or travels around it in a rounded path.\n"The universe is so __immense__," said Reggie, "that the human mind can't really even understand its size." | immense | We're looking for a word that describes a size that the human mind really can't understand. That word is *immense,* which means "very large." \n", "replace": false, "widgets": {} } ], "question": { "images": {}, "content": "\n\n**Match each word to the sentence it best completes.**\n\n\n\n[[☃ matcher 1]]\n\n", "widgets": { "matcher 1": { "type": "matcher", "graded": true, "static": false, "options": { "left": ["solar", "orbit", "immense"], "right": [ "The scientists hoped they would be able to build a _______-powered rocket that would allow astronauts to travel using only the energy of the sun.", "If the moon ever left its _______ around Earth, our days would be much shorter, because the moon's gravity slows down how fast the Earth turns.", ""The universe is so _______," said Reggie, "that the human mind can't really even understand its size."" ], "labels": ["Word", "Sentence"], "padding": true, "orderMatters": false }, "version": {"major": 0, "minor": 0}, "alignment": "default" } } }, "answerArea": { "tTable": false, "zTable": false, "chi2Table": false, "calculator": false, "periodicTable": false, "periodicTableWithKey": false, "financialCalculatorTotalAmount": false, "financialCalculatorTimeToPayOff": false, "financialCalculatorMonthlyPayment": false }, "itemDataVersion": {"major": 0, "minor": 1} } \`\`\` **Complete Gap Match AssessmentItemInput Example:** The Perseus input above converts to this complete structured JSON, showing the full gap match structure including interactions, response declarations, and feedback with table support. Notice how the hints table becomes the feedback table, and the underscores become gap placeholders: \`\`\`json { "identifier": "vocabulary-gap-match-1", "title": "Vocabulary Sentence Completion (Drag and Drop)", "responseDeclarations": [ { "identifier": "RESPONSE", "cardinality": "multiple", "baseType": "directedPair", "correct": [ { "source": "WORD_SOLAR", "target": "GAP_1" }, { "source": "WORD_ORBIT", "target": "GAP_2" }, { "source": "WORD_IMMENSE", "target": "GAP_3" } ], "allowEmpty": false } ], "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Drag each word to the sentence it best completes." }] }, { "type": "interactionRef", "interactionId": "gap_match_interaction" } ], "widgets": null, "interactions": { "gap_match_interaction": { "type": "gapMatchInteraction", "responseIdentifier": "RESPONSE", "shuffle": true, "gapTexts": [ { "identifier": "WORD_SOLAR", "matchMax": 1, "content": [{ "type": "text", "content": "solar" }] }, { "identifier": "WORD_ORBIT", "matchMax": 1, "content": [{ "type": "text", "content": "orbit" }] }, { "identifier": "WORD_IMMENSE", "matchMax": 1, "content": [{ "type": "text", "content": "immense" }] } ], "gaps": [ { "identifier": "GAP_1", "required": true }, { "identifier": "GAP_2", "required": true }, { "identifier": "GAP_3", "required": true } ], "content": [ { "type": "unorderedList", "items": [ [ { "type": "text", "content": "The scientists hoped they would be able to build a " }, { "type": "gap", "gapId": "GAP_1" }, { "type": "text", "content": "-powered rocket that would allow astronauts to travel using only the energy of the sun." } ], [ { "type": "text", "content": "If the moon ever left its " }, { "type": "gap", "gapId": "GAP_2" }, { "type": "text", "content": " around Earth, our days would be much shorter, because the moon's gravity slows down how fast the Earth turns." } ], [ { "type": "text", "content": "\\"The universe is so " }, { "type": "gap", "gapId": "GAP_3" }, { "type": "text", "content": "\\", said Reggie, \\"that the human mind can't really even understand its size.\\"" } ] ] } ] } }, "feedbackBlocks": [ { "identifier": "CORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Outstanding! You successfully matched all the vocabulary words to their correct sentences." } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "You demonstrated excellent understanding of context clues and word meanings. This vocabulary knowledge will help you understand more complex science and literature texts!" } ] } ] }, { "identifier": "INCORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Let's explore these fascinating space vocabulary words together! Understanding these terms will unlock many exciting science concepts." } ] }, { "type": "table", "className": "feedback-table", "header": ["Sentence", "Correct Word", "Deep Explanation"], "rows": [ [ "The scientists hoped they would be able to build a ___-powered rocket...", "solar", "SOLAR comes from 'Sol,' the Latin word for sun. Just like 'lunar' relates to the moon, 'solar' relates to the sun. Our solar system is named after our sun because it's the center that holds everything together with its gravity!" ], [ "If the moon ever left its ___ around Earth...", "orbit", "An ORBIT is like an invisible racetrack in space! Earth takes three hundred sixty-five days to complete one full orbit around the sun - that's why we have years. The word comes from Latin 'orbita' meaning 'wheel track.'" ], [ "The universe is so ___...", "immense", "IMMENSE means so incredibly huge that our minds can barely imagine it! The universe contains billions of galaxies, each with billions of stars. If Earth were a grain of sand, the universe would be larger than Earth itself!" ] ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Word Detective Strategy: Look for context clues in each sentence. Words like 'sun' and 'around' help identify which vocabulary word fits best!" } ] } ] } ] \`\`\` Expected shell (Shot 1) using gapMatchInteraction: \`\`\`json { "identifier": "vocabulary-gap-match-1", "title": "Vocabulary Sentence Completion (Drag and Drop)", "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Drag each word to the sentence it best completes." }] }, { "type": "interactionRef", "interactionId": "gap_match_interaction" } ], "responseDeclarations": [ { "identifier": "RESPONSE", "cardinality": "multiple", "baseType": "directedPair", "correct": [ "WORD_SOLAR GAP_1", "WORD_ORBIT GAP_2", "WORD_IMMENSE GAP_3" ] } ], "feedbackBlocks": [ { "identifier": "CORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Excellent work! You successfully matched all the vocabulary words to their correct sentences, demonstrating strong understanding of context clues and word meanings." } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "This vocabulary knowledge will help you understand more complex science and literature texts!" } ] } ] }, { "identifier": "INCORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Let's review the correct answers and learn about these fascinating space vocabulary words together!" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Understanding these terms will unlock many exciting science concepts and help you become a better reader of scientific texts." } ] } ] } ] } \`\`\` **CRITICAL EXAMPLE - Mixed Number Representation with gapMatchInteraction:** **WRONG - Ambiguous presentation without clear labels:** \`\`\`json { "identifier": "mixed-number-ambiguous", "title": "Create a mixed number to represent 84.9", "interactions": { "gap_match_interaction": { "type": "gapMatchInteraction", "content": [ { "type": "paragraph", "content": [ { "type": "math", "mathml": "84.9" }, { "type": "text", "content": " = " }, { "type": "gap", "gapId": "GAP_WHOLE" }, { "type": "text", "content": " (" }, // ❌ TERRIBLE: Ambiguous parentheses! { "type": "gap", "gapId": "GAP_NUM" }, { "type": "text", "content": " / " }, // ❌ BAD: Unclear this is a fraction { "type": "gap", "gapId": "GAP_DEN" }, { "type": "text", "content": ")" } ]} ] } } } \`\`\` **CORRECT - Clean presentation with clear separation:** \`\`\`json { "identifier": "mixed-number-clear", "title": "Create a mixed number to represent 84.9", "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Create a mixed number that represents " }, { "type": "math", "mathml": "84.9" }, { "type": "text", "content": "." } ]}, { "type": "paragraph", "content": [ { "type": "text", "content": "Drag each number to create the whole number and fraction parts of your mixed number." } ]}, { "type": "interactionRef", "interactionId": "gap_match_interaction" } ], "widgets": {}, "interactions": { "gap_match_interaction": { "type": "gapMatchInteraction", "content": [ { "type": "paragraph", "content": [ { "type": "math", "mathml": "84.9" }, { "type": "text", "content": " = " }, { "type": "gap", "gapId": "GAP_WHOLE" }, // ✅ Whole number part { "type": "text", "content": " + " }, // ✅ Plus sign makes it clear! { "type": "gap", "gapId": "GAP_NUM" }, // ✅ Numerator { "type": "text", "content": "/" }, // ✅ Vertical fraction bar in display { "type": "gap", "gapId": "GAP_DEN" } // ✅ Denominator ]} ] } } } \`\`\` **Why this is better than the ambiguous version:** - The plus sign (+) clearly shows this is addition, not multiplication - Clean visual separation between whole number and fraction - The prompt text reinforces that there are "whole number and fraction parts" - No distracting labels cluttering the interface - The forward slash will render as a proper vertical fraction in the display **Remember:** gapMatchInteraction is excellent for structured input, but the presentation must be crystal clear about what each gap represents. Always provide explicit labels and visual cues to guide students. Reference QTI outcome (result of compilation): \`\`\`xml WORD_SOLAR GAP_1 WORD_ORBIT GAP_2 WORD_IMMENSE GAP_3

Drag each word to the sentence it best completes.

solar orbit immense
\`\`\` Example shell for matcher conversion: \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Match each item to its correct pair." }] }, { "type": "tableRich", "header": [...], "rows": [...] } ] } \`\`\` Notes for implementation: - Each interactive cell must contain an \`inlineInteractionRef\` pointing to an \`inlineChoiceInteraction\`. - The dropdown choices should be the full set from the matcher's right side; correctness encoded in response declarations per row. - Tables are now purely presentational using the \`tableRich\` block content type. - No per-choice visuals or per-choice tables are allowed for matcher conversions. ### Perseus matcher field mapping (implementation detail) When \`widgets["matcher X"]\` is present in Perseus with the shape: - \`options.left\`: array of left-side items → becomes the tableRich's first column (row headers). Render formulas as inline MathML. - \`options.right\`: array of right-side items → becomes the dropdown choices for EVERY row in the second column. - \`options.labels\`: \`[leftHeader, rightHeader]\` → becomes the two column headers, respectively. - \`options.orderMatters\`: can be ignored for dropdown rendering; scoring uses per-row correct identifiers. - \`options.padding\`: styling concern only; no effect on structure. Example mapping for the provided sample: - Left header: "Chemical formula " (note trailing space) → tableRich column 1 header - Right header: "Name" → tableRich column 2 header - Left values: \`SrS\`, \`Na2SO4\`, \`Na2S\`, \`SrSO4\`, \`SrSe\`, \`Na2Se\` (render from LaTeX to MathML inline) - Dropdown choices (same for each row): \`strontium sulfide\`, \`sodium sulfate\`, \`sodium sulfide\`, \`strontium sulfate\`, \`strontium selenide\`, \`sodium selenide\` - Correct mapping: per row, the right choice matching the left formula's compound name ABSOLUTE REQUIREMENT: SLOT CONSISTENCY. This is the most critical rule. Any slot you include in the 'body' MUST have its slotId listed in either the 'widgets' array or the 'interactions' array. Additionally, any widget slot (blockSlot) used inside 'feedbackBlocks' MUST also have its slotId listed in the 'widgets' array (feedback never contains interactions). **EXCEPTION for Choice-Level Visuals:** Widget slots declared for use in interaction choices (following patterns like \`choice_a_visual\`, \`graph_choice_b\`, etc.) are declared in the \`widgets\` array but NOT placed in the body. This includes: - Widgets for multiple choice options when converting unsupported interactive widgets - Widgets referenced inside interaction choice content These widgets are reserved for the interaction generation shot and will be embedded inside the choice options. CRITICAL: Never embed images or SVGs directly. The body must contain ONLY text, MathML, and slot placeholders. \nCRITICAL: All content must be XML-safe. Do not use CDATA sections and do not include invalid XML control characters. **CRITICAL: NO ANSWERS OR HINTS IN 'BODY', WIDGETS, OR INTERACTIONS.** - The 'body' MUST NEVER contain the correct answer, partial answers, worked solutions, or any text that gives away the answer. - WIDGETS MUST NEVER label, highlight, or otherwise indicate the correct answer. This includes diagram labels, highlighted values, or any visual cues that reveal the answer. - Strip and ignore ALL Perseus 'hints' fields. NEVER include hints in any form in the 'body' (no text, MathML, paraphrases, or reworded guidance). - Do NOT include hint-like lead-ins such as "Hint:", "Remember:", "Think about...", or statements that restate or imply the answer (e.g., "the constant is 7"). - **ABSOLUTE RULE**: The ONLY places the correct answer may appear are: 1. Response declarations (for validation) 2. Feedback blocks - SPECIFICALLY: - The 'feedbackBlocks' array at the assessment level - NO OTHER LOCATION - **HARD STOP. NO EXCEPTIONS.** Answers are FORBIDDEN in body, widgets, or interactions. They are ONLY allowed in the designated feedbackBlocks above. **CRITICAL: ALT/CAPTION/ATTRIBUTION HANDLING — BANNED IN 'BODY'** Any descriptive or source text associated with images MUST NOT appear in the 'body'. This includes: - Alt text (what the image shows) - Captions (descriptive figure text) - Attribution/credit/licensing strings (creator names, "Image credit:", source sites, licenses like "CC0 1.0", "CC BY-SA", Wikimedia/Unsplash references) Rules: - Do NOT place alt/caption/attribution strings in 'body' paragraphs, prompts, or feedbackBlocks. - Do NOT write figure captions or credits in the 'body'. - If Perseus includes any of these texts adjacent to an image, STRIP THEM from 'body'. - Examples of banned phrases in body: "Image credit:", "Photo by", "by ", "Wikimedia", "CC0", "CC BY", "licensed under". ZERO TOLERANCE (automatic rejection if violated): - BANNED sentence starters: "Image:", "Figure:", "Caption:", "Photo:", "Diagram:", "Map:", "Illustration:". - BANNED descriptive intros: "The image shows", "This figure shows", "A photograph of", "Cartoon showing", "An illustration of", "A map of". - BANNED credit lines anywhere: lines containing words like "credit", "source", "licensed", "by ", "Wikimedia", "Unsplash", "CC0", "CC BY". Allowed minimal references in 'body' (keep neutral and non-descriptive): - "Examine the image below." / "Refer to the diagram below." / "Use the map below to answer." - Do NOT describe the content of the image. Do NOT include creator or licensing info. WRONG (attribution left in 'body' as a trailing paragraph): \`\`\`json { "identifier": "example-ke-dup-negative", "title": "KE tableRich duplicated inputs (negative)", "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Consider the following passage." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Skeletal muscle is a type of tissue that helps move the bones of the skeleton. Skeletal muscles produce movement by contracting (shortening) in response to signals from the nervous system. These muscles require a large supply of energy in order to maintain their function. Each skeletal muscle is made up of specialized cells called myocytes." }] }, { "type": "widgetRef", "widgetId": "image_1" }, { "type": "paragraph", "content": [{ "type": "text", "content": "Which claim is best supported by the passage above?" }] }, { "type": "interactionRef", "interactionId": "choice_interaction" }, { "type": "paragraph", "content": [{ "type": "text", "content": ""Muscle Tissue Skeletal Muscle Fibers" by Berkshire Community College Bioscience Image Library, CC0 1.0." }] } ] } \`\`\` Negative example (DO NOT OUTPUT) — tableRich required but rendered as sentences with inline dropdowns (cellular respiration): \`\`\`json { "identifier": "example-cellular-respiration-sentences-negative", "title": "Cellular respiration rows as sentences (negative)", "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Complete the table to describe the reactants and products for each stage of cellular respiration." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Stage | Substance | Reactant or product" }] }, { "type": "paragraph", "content": [ { "type": "text", "content": "glycolysis | glucose | " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_1" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "citric acid cycle | " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_2" }, { "type": "text", "content": " | product" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "electron transport chain | " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_3" }, { "type": "text", "content": " | reactant" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "electron transport chain | water | " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_4" } ] } ], "responseDeclarations": [ { "identifier": "dropdown_1", "cardinality": "single", "baseType": "string", "correct": "reactant" }, { "identifier": "dropdown_2", "cardinality": "single", "baseType": "string", "correct": "carbon dioxide" }, { "identifier": "dropdown_3", "cardinality": "single", "baseType": "string", "correct": "oxygen" }, { "identifier": "dropdown_4", "cardinality": "single", "baseType": "string", "correct": "product" } ] } \`\`\` CORRECT (remove attribution paragraph from 'body'): WRONG (caption/alt-like description left in 'body' after image): \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Examine the image and answer the question below." }] }, { "type": "widgetRef", "widgetId": "image_1" }, { "type": "paragraph", "content": [{ "type": "text", "content": "Cartoon showing Columbia stirring a bowl labeled "Citizenship" with a spoon labeled "Equal Rights." Figures representing many nations are in the bowl; a caricature of an Irishman jumps up, yelling and waving a knife and a green flag." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Which change most directly contributed to the development depicted?" }] }, { "type": "interactionRef", "interactionId": "choice_interaction" } ] } \`\`\` CORRECT (remove caption/alt-like paragraph; do not include image descriptions in 'body'): \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Examine the image and answer the question below." }] }, { "type": "widgetRef", "widgetId": "image_1" }, { "type": "paragraph", "content": [{ "type": "text", "content": "Which change most directly contributed to the development depicted?" }] }, { "type": "interactionRef", "interactionId": "choice_interaction" } ] } \`\`\` \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Consider the following passage." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Skeletal muscle is a type of tissue that helps move the bones of the skeleton. Skeletal muscles produce movement by contracting (shortening) in response to signals from the nervous system. These muscles require a large supply of energy in order to maintain their function. Each skeletal muscle is made up of specialized cells called myocytes." }] }, { "type": "widgetRef", "widgetId": "image_1" }, { "type": "paragraph", "content": [{ "type": "text", "content": "Which claim is best supported by the passage above?" }] }, { "type": "interactionRef", "interactionId": "choice_interaction" } ] } \`\`\` WRONG (attribution/credit string in 'body' following an image): \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "The following diagram shows Earth's lines of latitude." }] }, { "type": "widgetRef", "widgetId": "image_1" }, { "type": "paragraph", "content": [{ "type": "text", "content": "Complete the statement." }] }, { "type": "paragraph", "content": [ { "type": "text", "content": "As latitude decreases, species richness tends to " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_1" }, { "type": "text", "content": "." } ]}, { "type": "paragraph", "content": [{ "type": "text", "content": "Diagram credit: "Latitude of the Earth" by Djexplo, CC0 1.0." }] } ] } \`\`\` CORRECT (remove the attribution paragraph; it belongs in the widget's non-rendered 'attribution' property): \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "The following diagram shows Earth's lines of latitude." }] }, { "type": "widgetRef", "widgetId": "image_1" }, { "type": "paragraph", "content": [{ "type": "text", "content": "Complete the statement." }] }, { "type": "paragraph", "content": [ { "type": "text", "content": "As latitude decreases, species richness tends to " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_1" }, { "type": "text", "content": "." } ]} ] } \`\`\` **CRITICAL: NO EXPLANATION WIDGETS.** NEVER create a widget for explanatory text. Explanations or definitions found in the Perseus JSON (especially those of type 'explanation' or 'definition') must be embedded directly within the 'body' content as paragraph blocks. The 'explanation' and 'definition' widget types are BANNED. Hints are EXPLICITLY FORBIDDEN and MUST be stripped entirely. **CRITICAL: EQUATION/EXPRESSION INPUTS - ALWAYS CONVERT TO MULTIPLE CHOICE** Perseus "expression" widgets that ask for equation, inequality, or symbolic form input MUST be converted to multiple choice format. This avoids complex algebraic equivalence checking. **DETECTION CRITERIA:** - Widget type is "expression" AND any of these conditions: - Prompt contains: "write an equation", "write the equation", "equation to represent", "equation to determine", "equation that models", "write an inequality" - Asks for algebraic forms: "in terms of", "slope-intercept form", "standard form", "vertex form", "factored form", "function rule" - Requires algebraic manipulation: "factor", "expand", "simplify to form", "rewrite as" - Contains equals sign or inequality in the expected answer **CONVERSION RULES:** 1. Convert to a SINGLE choiceInteraction with THREE options (A, B, C) 2. Do NOT create textEntryInteraction for these cases 3. Response declaration uses identifier "RESPONSE" with cardinality "single" 4. All equation choices use MathML exclusively - NO raw LaTeX, NO HTML strings 5. The body contains ONLY the problem statement and ONE choiceInteraction slot **POSITIVE EXAMPLE 1 - Write an equation:** Input: Perseus expression widget asking "Write an equation to determine the original price per pound (p)." Expected answer: "3(p+0.75)=5.88" CORRECT shell structure: \`\`\`json { "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "In winter, the price of apples suddenly went up by " }, { "type": "math", "mathml": "$0.75" }, { "type": "text", "content": " per pound. Sam bought " }, { "type": "math", "mathml": "3" }, { "type": "text", "content": " pounds of apples at the new price for a total of " }, { "type": "math", "mathml": "$5.88" }, { "type": "text", "content": "." } ]}, { "type": "paragraph", "content": [ { "type": "text", "content": "Write an equation to determine the original price per pound (" }, { "type": "math", "mathml": "p" }, { "type": "text", "content": ")." } ]}, { "type": "interactionRef", "interactionId": "equation_choice" }, { "type": "paragraph", "content": [ { "type": "text", "content": "Find the original price per pound." } ]}, { "type": "paragraph", "content": [ { "type": "inlineInteractionRef", "interactionId": "price_entry" } ]} ], "responseDeclarations": [ { "identifier": "RESPONSE", "cardinality": "single", "baseType": "identifier", "correct": "A" }, { "identifier": "price_entry", "cardinality": "single", "baseType": "float", "correct": 1.21 } ] } \`\`\` **POSITIVE EXAMPLE 2 - Write equation from balanced hanger:** Input: Perseus expression widget with hanger diagram asking "Write an equation to represent the image." Expected answer: "12=4c" CORRECT shell structure: \`\`\`json { "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "The hanger image below represents a balanced equation." } ]}, { "type": "widgetRef", "widgetId": "hanger_image" }, { "type": "paragraph", "content": [ { "type": "text", "content": "Write an equation to represent the image." } ]}, { "type": "interactionRef", "interactionId": "equation_choice" } ], "responseDeclarations": [ { "identifier": "RESPONSE", "cardinality": "single", "baseType": "identifier", "correct": "B" } ] } \`\`\` **NEGATIVE EXAMPLE - WRONG textEntryInteraction for equation:** \`\`\`json { "interactions": { "equation_entry": { "type": "textEntryInteraction", // ❌ WRONG - equations must be multiple choice "responseIdentifier": "equation_entry", "expectedLength": 16 } } } \`\`\` **⚠️ CRITICAL: FEEDBACK BLOCKS (feedbackBlocks) ⚠️** You MUST provide a feedbackBlocks array that contains feedback for all possible responses. **For choice-based interactions** (e.g., choiceInteraction, inlineChoiceInteraction): Create one feedback block for each choice. The block's identifier MUST match the choice's identifier. The outcomeIdentifier MUST be FEEDBACK__, where is the ID from the interaction. **For other interactions** (e.g., textEntry, orderInteraction, gapMatch): Create two feedback blocks with identifier values of "CORRECT" and "INCORRECT". The outcomeIdentifier MUST be FEEDBACK__, where is the ID from the interaction. The content field of each feedback block must contain rich, structured content using the same block content model as body content. **Examples:** **Per-choice feedback (choiceInteraction):** \`\`\`json "feedbackBlocks": [ { "identifier": "A", "outcomeIdentifier": "FEEDBACK__RESPONSE", "content": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Excellent work! You correctly identified that " }, { "type": "math", "mathml": "34" }, { "type": "text", "content": " of the circle is shaded." } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "You recognized that the circle is divided into 4 equal parts and 3 parts are shaded. This visual understanding of fractions will help you solve more complex fraction problems." } ] } ] }, { "identifier": "B", "outcomeIdentifier": "FEEDBACK__RESPONSE", "content": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Not quite right. You selected " }, { "type": "math", "mathml": "14" }, { "type": "text", "content": ", but let's count together." } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Look at the circle: it's divided into 4 equal parts. Count how many parts are shaded (the colored sections). You should see 3 shaded parts out of 4 total parts, which means " }, { "type": "math", "mathml": "34" }, { "type": "text", "content": " is shaded." } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Remember: The top number (numerator) tells us how many parts are selected, and the bottom number (denominator) tells us the total number of equal parts." } ] } ] } ] \`\`\` **CORRECT/INCORRECT feedback (textEntry, orderInteraction, gapMatch):** \`\`\`json "feedbackBlocks": [ { "identifier": "CORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Outstanding! Your sequence is perfectly correct." } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "You demonstrated excellent understanding of the pattern: each number increases by " }, { "type": "math", "mathml": "5" }, { "type": "text", "content": ". This is called an arithmetic sequence, where we add the same amount each time." } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Challenge yourself: Can you continue this pattern for three more numbers? What would they be?" } ] } ] }, { "identifier": "INCORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Let's work through this together. The correct order is: " }, { "type": "math", "mathml": "5" }, { "type": "text", "content": ", " }, { "type": "math", "mathml": "10" }, { "type": "text", "content": ", " }, { "type": "math", "mathml": "15" }, { "type": "text", "content": ", " }, { "type": "math", "mathml": "20" }, { "type": "text", "content": ", " }, { "type": "math", "mathml": "25" }, { "type": "text", "content": "." } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Notice the pattern: we start at " }, { "type": "math", "mathml": "5" }, { "type": "text", "content": " and add " }, { "type": "math", "mathml": "5" }, { "type": "text", "content": " each time:" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "• " }, { "type": "math", "mathml": "5+5=10" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "• " }, { "type": "math", "mathml": "10+5=15" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "• " }, { "type": "math", "mathml": "15+5=20" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "• " }, { "type": "math", "mathml": "20+5=25" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "This is counting by " }, { "type": "math", "mathml": "5" }, { "type": "text", "content": "s! Practice skip counting by " }, { "type": "math", "mathml": "5" }, { "type": "text", "content": "s using your fingers: " }, { "type": "math", "mathml": "5" }, { "type": "text", "content": ", " }, { "type": "math", "mathml": "10" }, { "type": "text", "content": ", " }, { "type": "math", "mathml": "15" }, { "type": "text", "content": ", " }, { "type": "math", "mathml": "20" }, { "type": "text", "content": ", " }, { "type": "math", "mathml": "25" }, { "type": "text", "content": "..." } ] } ] } ] \`\`\` **⚠️ CRITICAL: CANONICAL ANSWER FORMATTING AND USER INSTRUCTIONS ⚠️** This is a mandatory step for all questions involving a \`textEntryInteraction\`. 1. **Identify the Canonical Answer Format from FeedbackBlocks**: Your primary source of truth for the correct answer's format is the **feedbackBlocks content** or **hints** in the Perseus JSON. This is a universal rule. If the feedbackBlocks show a specific format (e.g., "The answer is \`100/9\`", "The correct equation is \`y=3x+2\`"), you **MUST** use that exact format as the canonical answer. 2. **Determine the Correct \`baseType\`**: * **Use \`baseType="float"\`** only when the canonical answer is a plain decimal or integer and no strict formatting rules are implied by the feedbackBlocks or prompt (e.g., \`11.24\`, \`42\`, \`-0.5\`). \`float\` allows for scoring flexibility (e.g., \`.5\` is equivalent to \`0.5\`). If any formatting rule is present, you must use \`string\`. * **Use \`baseType="string"\`** when the format of the answer is critical or the value is not a plain number. This includes: * **Fractions or Mixed Numbers**: e.g., \`"100/9"\` * **Algebraic Equations or Expressions**: e.g., \`"y=3x+2"\` * **Strict Formatting Rules**: When the prompt or feedbackBlocks imply a specific format, such as requiring a leading zero (\`"0.5"\`), a fixed number of decimal places (\`"1.20"\`), or a specific scientific notation format (\`"3.14 x 10^2"\`). * **Any non-numeric characters**: If the answer includes units or symbols that are part of the required input. - **Note**: It is acceptable to use \`baseType="string"\` for plain numbers when strict formatting is explicitly required (fixed decimal places, significant figures, or scientific notation). Otherwise, prefer \`baseType="float"\`/\`"integer"\`. 3. **Set the Correct Response**: In the \`responseDeclarations\`, the \`correct\` field **MUST** contain only the single canonical value. For \`baseType="float"\`, this will be a number. For \`baseType="string"\`, this will be a string. 4. **Add User Instructions (Only When Necessary)**: If and only if you set \`baseType="string"\` because of a strict formatting requirement, you **MUST** add a clear, concise instruction for the student within the assessment \`body\`. This instruction should be in a new paragraph or appended in parentheses to the question prompt. * **For Fractions:** Add \`(In the simplest form without spaces)\`. * **For Algebraic Expressions/Equations:** Add \`(Do not include spaces in your answer)\`. * **For Decimals less than 1:** Add \`(Include a leading zero if your answer is a decimal less than 1)\`. * **For Specific Rounding:** Add an instruction like \`(Your answer should be rounded to two decimal places)\`. * **For Scientific Notation:** Add an instruction like \`(Format your answer as X.XX x 10^XX)\`. **Example Workflow 1 (Fraction):** - **Perseus Feedback:** "Correct! The answer is 100/9." - **Your Output:** - **\`responseDeclarations\`:** \`baseType\` is \`"string"\`, \`correct\` is \`"100/9"\`. - **\`body\`:** Add instruction: \`(In the simplest form without spaces)\`. **Example Workflow 2 (Algebraic Equation):** - **Perseus Feedback:** "The correct equation is \`y = 3x + 2\`." - **Your Output:** - **\`responseDeclarations\`:** \`baseType\` is \`"string"\`, \`correct\` is \`"y=3x+2"\`. - **\`body\`:** Add instruction: \`(Do not include spaces in your answer)\`. **Example Workflow 3 (Simple Float):** - **Perseus Feedback:** "The answer is 11.24." - **Your Output:** - **\`responseDeclarations\`:** \`baseType\` is \`"float"\`, \`correct\` is \`11.24\`. - **\`body\`:** No formatting instruction is added. **Negative example (DO NOT OUTPUT) - Incorrect format and missing instructions:** \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Evaluate." }] }, { "type": "paragraph", "content": [ { "type": "math", "mathml": "(-313)2=" }, { "type": "text", "content": " " }, { "type": "inlineInteractionRef", "interactionId": "text_entry" } ] } // ❌ WRONG: Missing a paragraph with user instructions on how to format the answer. ], "title": "Square a negative mixed number", "feedbackBlocks": [ { "identifier": "CORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Correct! The value is " }, { "type": "math", "mathml": "1009" }, // The feedbackBlocks clearly show the canonical format. { "type": "text", "content": "." } ] } ] }, { "identifier": "INCORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Not quite. Please check your calculation." } ] } ] } ], "responseDeclarations": [ { // ❌ WRONG: The 'correct' value is a float, but the feedback specifies a fraction. "correct": 11.1111111, // ❌ WRONG: The 'baseType' should be "string" to enforce the fractional format. "baseType": "float", "identifier": "RESPONSE", "cardinality": "single" } ] } \`\`\` **Positive example — CORRECT format derived from feedback and instructions added:** \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Evaluate." }] }, // ✅ CORRECT: A new paragraph with user instructions has been added. { "type": "paragraph", "content": [{ "type": "text", "content": "(In the simplest form without spaces)" }] }, { "type": "paragraph", "content": [ { "type": "math", "mathml": "(-313)2=" }, { "type": "text", "content": " " }, { "type": "inlineInteractionRef", "interactionId": "text_entry" } ] } ], "title": "Square a negative mixed number", "feedbackBlocks": [ { "identifier": "CORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Correct! The value is " }, { "type": "math", "mathml": "1009" }, { "type": "text", "content": "." } ] } ] }, { "identifier": "INCORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Not quite. Please check your calculation." } ] } ] } ], "responseDeclarations": [ { // ✅ CORRECT: The 'correct' value is a string that matches the feedbackBlocks' canonical format. "correct": "100/9", // ✅ CORRECT: The 'baseType' is "string" because the required format is a fraction. "baseType": "string", "identifier": "RESPONSE", "cardinality": "single" } ] } \`\`\` **MANDATORY: Student Instruction Checklist (when baseType="string")** When you choose \`baseType="string"\` to enforce a specific format, you MUST add a clear instruction for the student in the assessment \`body\` (as a new paragraph or appended in parentheses to the prompt). Use one or more of the following, as applicable: - Fractions: "(In the simplest form without spaces)" - Algebraic expressions/equations: "(Do not include spaces in your answer)" - Decimals < 1: "(Include a leading zero if your answer is a decimal less than 1)" - Rounding to fixed places: "(Your answer should be rounded to two decimal places)" or "(Round your answer to the nearest tenth)" - Exact decimal places: "(Write your answer with exactly N decimal places)" - Scientific notation: "(Format your answer as X.XX x 10^YY)" - Significant figures: "(Report your answer to N significant figures)" - Units: Default to "(Do not include units in your answer)". Only require typing units if the original prompt explicitly instructs to include units (e.g., "Include units in your answer"). Also: - Provide only the single canonical value in \`responseDeclarations.correct\`. Do not include alternates. - Never generate additional acceptable variants (fractions↔decimals, spacing changes, flipped equations, etc.). **CRITICAL: NO CURRENCY SLOTS** Currency symbols and amounts MUST NOT be represented as slots (widget or interaction). Do not generate any slotId that indicates currency (for example, names containing "currency" or ending with "_feedback"). - **MathML Conversion (MANDATORY)**: You MUST convert ALL LaTeX expressions to standard MathML. PRESERVE all mathematical structures: fractions, exponents, roots, operators, and inequalities. Do NOT simplify or alter the mathematical content. - **Table Rule (MANDATORY)**: All tables MUST be generated as \`{ "type": "tableRich", ... }\` objects within the 'body'. Tables are for presentation ONLY. For any interactive cell (e.g., a dropdown or text input), you MUST place an \`inlineInteractionRef\` inside the cell's content array. The corresponding interaction must be declared in the top-level 'interactions' array. Do NOT repeat table rows below the table with separate inline inputs. Each \`responseIdentifier\` MUST be rendered exactly once. Any discrepancy will cause your output to be rejected. Review your work carefully to ensure the body's slots and the declaration arrays are perfectly synchronized.` const widgetSelectionSection = createWidgetSelectionPromptSection(widgetCollection) const userContent = `Convert the following source into an assessment shell. Use the provided context, including raster images for vision and vector images as text, to understand the content fully. ${formatUnifiedContextSections(envelope, imageContext)} ${widgetSelectionSection} ## CRITICAL Instructions: - **Strictly follow the MathML rules** in the system instructions. - **Analyze Images**: Use the raster images provided to your vision to understand the visual components of the question. Any SVG content is provided directly in the 'Raw Source Input' blocks. - **\`body\` Field**: Create a 'body' field containing the main content as a structured JSON array (not an HTML string). - **ONLY REFERENCED WIDGETS (WITH CHOICE-LEVEL EXCEPTION)**: CRITICAL RULE - Only include widgets/interactions that are explicitly referenced in the Perseus content string via \`[[☃ widget_name]]\` placeholders. Perseus JSON may contain many widget definitions, but you MUST ignore any that aren't actually used in the content. - **Exception 1 - Visuals inside interaction choices**: When the original Perseus JSON encodes visuals (e.g., Graphie images, number lines, diagrams) inside interaction choice content, you MUST predeclare widget slots for those visuals even if there is no corresponding \`[[☃ ...]]\` placeholder in the top-level body. - **Exception 2 - Unsupported interactive widgets converted to multiple choice**: When converting unsupported widgets (plotter, interactive-graph, etc.), you MUST create THREE additional widget slots for the choice options (e.g., \`dot_plot_choice_a\`, \`dot_plot_choice_b\`, \`dot_plot_choice_c\`). - **Exception 3 - Visuals inside gap-match draggable items**: When converting a Perseus matcher widget to gapMatchInteraction, if the draggable items (left array) contain visual elements (images, diagrams, geometric figures), you MUST predeclare widget slots for each visual gapText item (e.g., \`line_diagram\`, \`ray_diagram\`, \`segment_diagram\`, \`point_diagram\`). - Do NOT render these per-choice/per-gapText visuals in the shell body. Simply reserve their widget slot identifiers in the \`widgets\` array for later use by the interaction content shot. - **Placeholders**: - For ALL Perseus widgets (including 'image' widgets), create a { "type": "widgetRef", "widgetId": "...", "widgetType": "..." } placeholder in the 'body' and add its identifier to the 'widgets' string array. - **CRITICAL: widgetType is REQUIRED** - Every widgetRef and inlineWidgetRef MUST include a "widgetType" property with a value from the Available Widget Types list provided above. Choose the most semantically appropriate widget type for the visual content. - For Perseus resources (reference materials such as periodic tables or formula sheets), when the current widget collection includes a matching widget type (e.g., 'periodicTable' in 'simple-visual'), you MUST create a dedicated widget slot for the resource, include its identifier in the 'widgets' array, and place a { "type": "widgetRef", "widgetId": "...", "widgetType": "..." } at the correct position in the body. - For inline interactions (e.g., 'text-input', 'inline-choice'), create { "type": "inlineInteractionRef", "interactionId": "..." } inside paragraph content. - For block interactions (e.g., 'radio', 'order'), create { "type": "widgetRef", "widgetId": "..." } in the body array. - **NEVER EMBED IMAGES OR SVGs**: You MUST NOT generate \`\` tags, \`\` tags, or data URIs in the 'body' string. This is a critical requirement. ALL images and visual elements must be handled as widgets referenced by slots. If you see an image in Perseus, create a widget slot for it, never embed it directly. - **NO ANSWERS OR HINTS IN 'BODY'**: Do NOT reveal or restate the correct answer anywhere in the 'body'. Remove ALL Perseus 'hints' content entirely and NEVER include hint-like guidance (e.g., lines starting with "Hint:" or text that gives away the answer). Only the prompt and neutral problem context belong in the 'body'; answers live solely in response declarations. - **MathML Conversion (MANDATORY)**: - You MUST convert ALL LaTeX expressions to standard MathML (\`...\`). - PRESERVE all mathematical structures: fractions (\`\`), exponents (\`\`), roots (\`\` or \`\`), operators (\`\`), and inequalities (\`<\`, \`>\`). - Do NOT simplify or alter the mathematical content. It must be a faithful translation. - **CRITICAL BANS**: * NO LaTeX commands in output: If input has \`\\sqrt{x}\`, output must be \`x\`, NEVER leave the backslash command * NO LaTeX dollar sign delimiters: Remove \`$\` when used for LaTeX math (e.g., \`$x+y$\` should be \`x+y\`) * NO \`\` elements: Use \`(...)\` instead of \`\` * NO LaTeX color commands: Strip \`\\blueD{x}\` to just the content \`x\` * NO LaTeX delimiters: Convert \`\\(...\\)\` to proper MathML, never leave the backslashes - **CURRENCY/PERCENT HANDLING (CRITICAL - COMPILATION ERRORS IF NOT FOLLOWED)**: * Currency: MUST use MathML $amount pattern, NEVER raw text * Percentages: MUST use MathML number% pattern, NEVER raw text * CORRECT: $12.50 and 85% * WRONG: $12.50 or raw text "$12.50" or "85%" * ERROR MESSAGE IF VIOLATED: "currency and percent must be expressed in MathML, not raw text" * This is NOT optional - the QTI compiler will REJECT any raw text currency/percent symbols - Do NOT create slots for currency. Never generate slotIds like "currency7" or "currency7_feedback". - **Table Rule (MANDATORY)**: - All tables MUST be generated as \`{ "type": "tableRich", ... }\` objects within the 'body'. - Tables are for presentation ONLY. They do not have a 'type' property in the widgets map. - For any interactive cell (e.g., a dropdown or text input), you MUST place an \`inlineInteractionRef\` inside the cell's content array. - The corresponding interaction (e.g., a dropdown) must be declared in the top-level 'interactions' array. - Do NOT repeat table rows below the table with separate inline inputs. - Each \`responseIdentifier\` MUST be rendered exactly once: either owned by a widget OR as a standalone interaction, never both. Negative example 1 (DO NOT OUTPUT) — duplicated inputs (table + inline slots below): \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "A moving object's kinetic energy is determined by two factors." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Select whether each quantity determines an object's kinetic energy." }] }, { "type": "tableRich", "header": [[...]], "rows": [[...]] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Mass: " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_8" }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Volume: " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_10" }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Height: " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_11" }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Speed: " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_12" }] } ], "feedbackBlocks": [ { "identifier": "CORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Correct! Well done." }] }] }, { "identifier": "INCORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Not quite. Please review your answers." }] }] } ], "responseDeclarations": [ { "identifier": "dropdown_8", "cardinality": "single", "baseType": "string", "correct": "yes" }, { "identifier": "dropdown_10", "cardinality": "single", "baseType": "string", "correct": "no" }, { "identifier": "dropdown_11", "cardinality": "single", "baseType": "string", "correct": "no" }, { "identifier": "dropdown_12", "cardinality": "single", "baseType": "string", "correct": "yes" } ] } \`\`\` Positive example 1 — table owns inputs; do not duplicate below: \`\`\`json { "identifier": "example-ke-dup-positive", "title": "KE tableRich owns inputs (positive)", "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "A moving object's kinetic energy is determined by two factors." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Select whether each quantity determines an object's kinetic energy." }] }, { "type": "tableRich", "header": [[...]], "rows": [[...]] } ], "feedbackBlocks": [ { "identifier": "CORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Correct! Well done." }] }] }, { "identifier": "INCORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Not quite. Please review your answers." }] }] } ], "responseDeclarations": [ { "identifier": "dropdown_8", "cardinality": "single", "baseType": "string", "correct": "yes" }, { "identifier": "dropdown_10", "cardinality": "single", "baseType": "string", "correct": "no" }, { "identifier": "dropdown_11", "cardinality": "single", "baseType": "string", "correct": "no" }, { "identifier": "dropdown_12", "cardinality": "single", "baseType": "string", "correct": "yes" } ] } \`\`\` Negative example 2 (DO NOT OUTPUT) — duplicated inputs (table + inline slots): \`\`\`json { "identifier": "example-temp-dup-negative", "title": "Temperature table duplicated inputs (negative)", "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Several students tested how the temperature changed when dissolving different solids in the same amount of water. The substances they tested were NH4Cl, MgSO4, and CaCl2." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Only one student kept track of everyone's data. Unfortunately, their lab notebook got wet, and some of the labels were damaged." }] }, { "type": "widgetRef", "widgetId": "image_1" }, { "type": "paragraph", "content": [{ "type": "text", "content": "Use the data provided to identify the reactant and the amount that caused each temperature change. Each option is only used once." }] }, { "type": "tableRich", "header": [[...]], "rows": [[...]] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Experiment C: Reactant " }, { "type": "inlineInteractionRef", "interactionId": "react_c" }, { "type": "text", "content": ", Amount " }, { "type": "inlineInteractionRef", "interactionId": "amt_c" }] } ], "feedbackBlocks": [ { "identifier": "CORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Correct! Well done." }] }] }, { "identifier": "INCORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Not quite. Please review your answers." }] }] } ], "responseDeclarations": [ { "identifier": "react_c", "cardinality": "single", "baseType": "identifier", "correct": "MGSO4" }, { "identifier": "amt_c", "cardinality": "single", "baseType": "identifier", "correct": "AMT_2_5" }, { "identifier": "react_d", "cardinality": "single", "baseType": "identifier", "correct": "NH4CL" }, { "identifier": "amt_d", "cardinality": "single", "baseType": "identifier", "correct": "AMT_3_0" }, { "identifier": "react_e", "cardinality": "single", "baseType": "identifier", "correct": "CACL2" }, { "identifier": "amt_e", "cardinality": "single", "baseType": "identifier", "correct": "AMT_8_0" } ] } \`\`\` Positive example 2 — table owns inputs; do not duplicate below: \`\`\`json { "identifier": "example-temp-dup-positive", "title": "Temperature tableRich owns inputs (positive)", "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Several students tested how the temperature changed when dissolving different solids in the same amount of water." }] }, { "type": "widgetRef", "widgetId": "image_1" }, { "type": "paragraph", "content": [{ "type": "text", "content": "Use the data provided to identify the reactant and the amount that caused each temperature change. Each option is only used once." }] }, { "type": "tableRich", "header": [[...]], "rows": [[...]], "rowHeaderCol": null, "footer": null } ], "feedbackBlocks": [ { "identifier": "CORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Correct! Well done." }] }] }, { "identifier": "INCORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Not quite. Please review your answers." }] }] } ], "responseDeclarations": [ { "identifier": "RESPONSE_REACT_C", "cardinality": "single", "baseType": "identifier", "correct": "MGSO4" }, { "identifier": "RESPONSE_AMT_C", "cardinality": "single", "baseType": "identifier", "correct": "AMT_2_5" }, { "identifier": "RESPONSE_REACT_D", "cardinality": "single", "baseType": "identifier", "correct": "NH4CL" }, { "identifier": "RESPONSE_AMT_D", "cardinality": "single", "baseType": "identifier", "correct": "AMT_3_0" }, { "identifier": "RESPONSE_REACT_E", "cardinality": "single", "baseType": "identifier", "correct": "CACL2" }, { "identifier": "RESPONSE_AMT_E", "cardinality": "single", "baseType": "identifier", "correct": "AMT_8_0" } ] } \`\`\` **BANNED: PSEUDO-TABLES AND PIPE/CARET CHARACTERS IN TEXT** - NEVER simulate tables by stacking paragraphs with delimiter characters (e.g., "Substance | Density ..."). - ALWAYS replace such lists with a dedicated tableRich block in the body. - ABSOLUTE BAN: The vertical bar character | and the caret character ^ MUST NEVER appear in any text content anywhere in the output (body, prompts, feedback, choices). Use proper tableRich blocks instead of textual pipes, and express exponents in MathML. Negative example (DO NOT OUTPUT) — pseudo-table paragraphs in body: \`\`\`json { "identifier": "example-pseudo-table-negative", "title": "Pseudo-table in body (negative)", "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "A chemistry teacher fills a jar with liquid ethanol under a fume hood. She places three substances into the jar to see whether they will sink or float. The densities of the substances are listed below." }] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Substance | Density " }, { "type": "math", "mathml": "(g/cm3)" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Ethanol | " }, { "type": "math", "mathml": "0.79" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Aluminum | " }, { "type": "math", "mathml": "2.7" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Cork | " }, { "type": "math", "mathml": "0.24" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Rosewood | " }, { "type": "math", "mathml": "0.83" } ] }, { "type": "widgetRef", "widgetId": "image_1" }, { "type": "paragraph", "content": [{ "type": "text", "content": "Complete the statements." }] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Aluminum " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_1" }, { "type": "text", "content": " in ethanol." } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Cork " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_2" }, { "type": "text", "content": " in ethanol." } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Rosewood " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_3" }, { "type": "text", "content": " in ethanol." } ] } ], "feedbackBlocks": [ { "identifier": "CORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Correct! Well done." }] }] }, { "identifier": "INCORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Not quite. Please review your answers." }] }] } ], "responseDeclarations": [ { "identifier": "dropdown_1", "cardinality": "single", "baseType": "string", "correct": "sinks" }, { "identifier": "dropdown_2", "cardinality": "single", "baseType": "string", "correct": "floats" }, { "identifier": "dropdown_3", "cardinality": "single", "baseType": "string", "correct": "sinks" } ] } \`\`\` Positive example — replace pseudo-table with a tableRich block: \`\`\`json { "identifier": "example-pseudo-table-positive", "title": "Replace pseudo-table with widget (positive)", "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "A chemistry teacher fills a jar with liquid ethanol under a fume hood. She places three substances into the jar to see whether they will sink or float. The densities of the substances are listed below." }] }, { "type": "tableRich", "header": [[...]], "rows": [[...]] }, { "type": "widgetRef", "widgetId": "image_1" }, { "type": "paragraph", "content": [{ "type": "text", "content": "Complete the statements." }] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Aluminum " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_1" }, { "type": "text", "content": " in ethanol." } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Cork " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_2" }, { "type": "text", "content": " in ethanol." } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Rosewood " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_3" }, { "type": "text", "content": " in ethanol." } ] } ], "feedbackBlocks": [ { "identifier": "CORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Correct! Well done." }] }] }, { "identifier": "INCORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Not quite. Please review your answers." }] }] } ], "responseDeclarations": [ { "identifier": "dropdown_1", "cardinality": "single", "baseType": "string", "correct": "sinks" }, { "identifier": "dropdown_2", "cardinality": "single", "baseType": "string", "correct": "floats" }, { "identifier": "dropdown_3", "cardinality": "single", "baseType": "string", "correct": "sinks" } ] } \`\`\` **BANNED: SENTENCE-STACKED ROWS — USE TABLE WIDGET FOR ROW-WISE PROPERTIES** - When content lists multiple rows with the same columns (e.g., State | Shape | Volume | Compressible), model it as a tableRich block. - Do NOT render each row as a paragraph with multiple inline slots. Negative example (DO NOT OUTPUT) — states of matter rendered as sentences with inline slots: \`\`\`json { "identifier": "example-states-sentences-negative", "title": "States of matter sentences (negative)", "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Complete each row to describe the differences among solids, liquids, and gases." }] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Solid — Shape: " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_1" }, { "type": "text", "content": "; Volume: " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_2" }, { "type": "text", "content": "; Compressible: " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_3" }, { "type": "text", "content": "." } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Liquid — Shape: " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_4" }, { "type": "text", "content": "; Volume: " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_5" }, { "type": "text", "content": "; Compressible: " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_6" }, { "type": "text", "content": "." } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Gas — Shape: " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_7" }, { "type": "text", "content": "; Volume: " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_8" }, { "type": "text", "content": "; Compressible: " }, { "type": "inlineInteractionRef", "interactionId": "dropdown_9" }, { "type": "text", "content": "." } ] } ], "feedbackBlocks": [ { "identifier": "CORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Correct! Well done." }] }] }, { "identifier": "INCORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Not quite. Please review your answers." }] }] } ], "responseDeclarations": [ { "identifier": "dropdown_1", "cardinality": "single", "baseType": "string", "correct": "fixed" }, { "identifier": "dropdown_2", "cardinality": "single", "baseType": "string", "correct": "fixed" }, { "identifier": "dropdown_3", "cardinality": "single", "baseType": "string", "correct": "no" }, { "identifier": "dropdown_4", "cardinality": "single", "baseType": "string", "correct": "shape of container" }, { "identifier": "dropdown_5", "cardinality": "single", "baseType": "string", "correct": "fixed" }, { "identifier": "dropdown_6", "cardinality": "single", "baseType": "string", "correct": "no" }, { "identifier": "dropdown_7", "cardinality": "single", "baseType": "string", "correct": "shape of container" }, { "identifier": "dropdown_8", "cardinality": "single", "baseType": "string", "correct": "not fixed" }, { "identifier": "dropdown_9", "cardinality": "single", "baseType": "string", "correct": "yes" } ] } \`\`\` Positive example — model as a tableRich block with embedded dropdowns: \`\`\`json { "identifier": "example-states-table-positive", "title": "States of matter tableRich (positive)", "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Complete each row to describe the differences among solids, liquids, and gases." }] }, { "type": "widgetRef", "widgetId": "states_table" } ], "feedbackBlocks": [ { "identifier": "CORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Correct! Well done." }] }] }, { "identifier": "INCORRECT", "outcomeIdentifier": "FEEDBACK__RESPONSE_TEXT", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Not quite. Please review your answers." }] }] } ], "responseDeclarations": [ { "identifier": "dropdown_1", "cardinality": "single", "baseType": "string", "correct": "fixed" }, { "identifier": "dropdown_2", "cardinality": "single", "baseType": "string", "correct": "fixed" }, { "identifier": "dropdown_3", "cardinality": "single", "baseType": "string", "correct": "no" }, { "identifier": "dropdown_4", "cardinality": "single", "baseType": "string", "correct": "shape of container" }, { "identifier": "dropdown_5", "cardinality": "single", "baseType": "string", "correct": "fixed" }, { "identifier": "dropdown_6", "cardinality": "single", "baseType": "string", "correct": "no" }, { "identifier": "dropdown_7", "cardinality": "single", "baseType": "string", "correct": "shape of container" }, { "identifier": "dropdown_8", "cardinality": "single", "baseType": "string", "correct": "not fixed" }, { "identifier": "dropdown_9", "cardinality": "single", "baseType": "string", "correct": "yes" } ] } \`\`\` Positive example — cellular respiration table widget with embedded dropdowns: \`\`\`json { "identifier": "example-cellular-respiration-table-positive", "title": "Cellular respiration table (positive)", "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Complete the table to describe the reactants and products for each stage of cellular respiration." }] }, { "type": "widgetRef", "widgetId": "respiration_table" } ], "responseDeclarations": [ { "identifier": "dropdown_1", "cardinality": "single", "baseType": "string", "correct": "reactant" }, { "identifier": "dropdown_2", "cardinality": "single", "baseType": "string", "correct": "carbon dioxide" }, { "identifier": "dropdown_3", "cardinality": "single", "baseType": "string", "correct": "oxygen" }, { "identifier": "dropdown_4", "cardinality": "single", "baseType": "string", "correct": "product" } ] } \`\`\` - **Response Declarations**: - The 'question.answers' from Perseus must be used to create the \`responseDeclarations\`. - **BASE TYPE FOR ENUMERATED CHOICES — MANDATORY**: - For ANY interaction with enumerated options — \`inlineChoiceInteraction\` (dropdowns), \`choiceInteraction\`, and \`orderInteraction\` — and for dropdown cells inside data tables, the \`baseType\` MUST be \`"identifier"\`. - The \`correct\` value(s) MUST be the choice \`identifier\`(s) defined in the corresponding interaction (or table-cell dropdown), not labels, not numbers, not math content. - Never use \`"string"\`, \`"integer"\`, or \`"float"\` as \`baseType\` for these enumerated interactions. - Identifiers must be safe tokens (letters, digits, underscores), no spaces. Example patterns: \`A\`, \`B\`, \`C\`, \`one_half\`, \`POS_2_0_C\`. - **⚠️ CRITICAL: ANSWER KEY CONSISTENCY FOR "SELECT ALL THAT APPLY" QUESTIONS**: - When a choiceInteraction has \`cardinality: "multiple"\` and the prompt includes "Select all that apply" (or equivalent plural phrasing), the \`correct\` array MUST include EVERY choice identifier that satisfies the question criterion. - **BANNED**: Creating a "select all that apply" question where only SOME of the qualifying choices are marked as correct. This creates an impossible question where a student who correctly identifies all valid answers will be marked wrong. - **Example of BANNED pattern**: Question asks "Which angles do NOT measure 90°?" with 5 angles where 4 are non-90° but only 2 are marked correct. This is logically inconsistent. - **REQUIRED**: Either (1) ensure ALL choices that meet the criterion are in the \`correct\` array, OR (2) modify the question to ask for a specific subset (e.g., "Select the TWO obtuse angles" instead of "Select all angles that are not 90°"). - **Widget consistency check**: If using widgets like protractorAngleDiagram with numeric properties (e.g., \`angleDegrees\`), verify that the \`correct\` array matches the mathematical criterion stated in the prompt. Do not arbitrarily exclude some qualifying choices. WRONG (dropdown uses string baseType and label-like "correct"): \`\`\`json { "responseDeclarations": [ { "identifier": "dropdown_1", "cardinality": "single", "baseType": "string", "correct": "increase" } ], "interactions": { "dropdown_1": { "type": "inlineChoiceInteraction", "responseIdentifier": "dropdown_1", "choices": [ { "identifier": "INCREASE", "content": [{ "type": "text", "content": "increase" }] }, { "identifier": "DECREASE", "content": [{ "type": "text", "content": "decrease" }] } ], "shuffle": true } } } \`\`\` CORRECT (dropdown uses identifier baseType and identifier "correct"): \`\`\`json { "responseDeclarations": [ { "identifier": "dropdown_1", "cardinality": "single", "baseType": "identifier", "correct": "INCREASE" } ], "interactions": { "dropdown_1": { "type": "inlineChoiceInteraction", "responseIdentifier": "dropdown_1", "choices": [ { "identifier": "INCREASE", "content": [{ "type": "text", "content": "increase" }] }, { "identifier": "DECREASE", "content": [{ "type": "text", "content": "decrease" }] } ], "shuffle": true } } } \`\`\` WRONG (choiceInteraction uses string baseType and label "correct"): \`\`\`json { "responseDeclarations": [ { "identifier": "choice_interaction", "cardinality": "single", "baseType": "string", "correct": "one-half" } ], "interactions": { "choice_interaction": { "type": "choiceInteraction", "responseIdentifier": "choice_interaction", "prompt": [{ "type": "text", "content": "Select the best answer." }], "choices": [ { "identifier": "one_half", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "one-half" }] }] }, { "identifier": "one_fourth", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "one-fourth" }] }] } ], "shuffle": true, "minChoices": 1, "maxChoices": 1 } } } \`\`\` CORRECT (choiceInteraction uses identifier baseType and identifier "correct"): \`\`\`json { "responseDeclarations": [ { "identifier": "choice_interaction", "cardinality": "single", "baseType": "identifier", "correct": "one_half" } ], "interactions": { "choice_interaction": { "type": "choiceInteraction", "responseIdentifier": "choice_interaction", "prompt": [{ "type": "text", "content": "Select the best answer." }], "choices": [ { "identifier": "one_half", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "one-half" }] }] }, { "identifier": "one_fourth", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "one-fourth" }] }] } ], "shuffle": true, "minChoices": 1, "maxChoices": 1 } } } \`\`\` WRONG (multiple cardinality with inconsistent answer key — only some qualifying choices marked correct): \`\`\`json { "responseDeclarations": [ { "identifier": "RESPONSE", "cardinality": "multiple", "baseType": "identifier", "correct": ["A", "C"] } ], "interactions": { "angles_choice": { "type": "choiceInteraction", "responseIdentifier": "RESPONSE", "prompt": [ { "type": "text", "content": "Which angles do NOT measure 90°? Select all that apply." } ], "choices": [ { "identifier": "A", "content": [{ "type": "widgetRef", "widgetId": "angle_a" }] }, { "identifier": "B", "content": [{ "type": "widgetRef", "widgetId": "angle_b" }] }, { "identifier": "C", "content": [{ "type": "widgetRef", "widgetId": "angle_c" }] }, { "identifier": "D", "content": [{ "type": "widgetRef", "widgetId": "angle_d" }] }, { "identifier": "E", "content": [{ "type": "widgetRef", "widgetId": "angle_e" }] } ], "shuffle": true, "minChoices": 0, "maxChoices": 5 } }, "widgets": { "angle_a": { "type": "protractorAngleDiagram", "angleDegrees": 120 }, "angle_b": { "type": "protractorAngleDiagram", "angleDegrees": 90 }, "angle_c": { "type": "protractorAngleDiagram", "angleDegrees": 100 }, "angle_d": { "type": "protractorAngleDiagram", "angleDegrees": 70 }, "angle_e": { "type": "protractorAngleDiagram", "angleDegrees": 70 } } } \`\`\` // ❌ CRITICAL ERROR: The question asks "Select all that apply" for non-90° angles. // Angles A (120°), C (100°), D (70°), and E (70°) all DO NOT measure 90°. // But only A and C are marked correct! A student who correctly selects A, C, D, E would be marked wrong. // This is LOGICALLY INCONSISTENT and creates an impossible question. CORRECT (multiple cardinality with consistent answer key — all qualifying choices marked correct): \`\`\`json { "responseDeclarations": [ { "identifier": "RESPONSE", "cardinality": "multiple", "baseType": "identifier", "correct": ["A", "C", "D", "E"] } ], "interactions": { "angles_choice": { "type": "choiceInteraction", "responseIdentifier": "RESPONSE", "prompt": [ { "type": "text", "content": "Which angles do NOT measure 90°? Select all that apply." } ], "choices": [ { "identifier": "A", "content": [{ "type": "widgetRef", "widgetId": "angle_a" }] }, { "identifier": "B", "content": [{ "type": "widgetRef", "widgetId": "angle_b" }] }, { "identifier": "C", "content": [{ "type": "widgetRef", "widgetId": "angle_c" }] }, { "identifier": "D", "content": [{ "type": "widgetRef", "widgetId": "angle_d" }] }, { "identifier": "E", "content": [{ "type": "widgetRef", "widgetId": "angle_e" }] } ], "shuffle": true, "minChoices": 0, "maxChoices": 5 } }, "widgets": { "angle_a": { "type": "protractorAngleDiagram", "angleDegrees": 120 }, "angle_b": { "type": "protractorAngleDiagram", "angleDegrees": 90 }, "angle_c": { "type": "protractorAngleDiagram", "angleDegrees": 100 }, "angle_d": { "type": "protractorAngleDiagram", "angleDegrees": 70 }, "angle_e": { "type": "protractorAngleDiagram", "angleDegrees": 70 } } } \`\`\` // ✅ CORRECT: All four non-90° angles (A, C, D, E) are marked correct. // The answer key is logically consistent with the "select all that apply" criterion. ALSO CORRECT (alternative — modify prompt to ask for specific subset): \`\`\`json { "responseDeclarations": [ { "identifier": "RESPONSE", "cardinality": "multiple", "baseType": "identifier", "correct": ["A", "C"] } ], "interactions": { "angles_choice": { "type": "choiceInteraction", "responseIdentifier": "RESPONSE", "prompt": [ { "type": "text", "content": "Select the TWO obtuse angles (angles greater than 90°)." } ], "choices": [ { "identifier": "A", "content": [{ "type": "widgetRef", "widgetId": "angle_a" }] }, { "identifier": "B", "content": [{ "type": "widgetRef", "widgetId": "angle_b" }] }, { "identifier": "C", "content": [{ "type": "widgetRef", "widgetId": "angle_c" }] }, { "identifier": "D", "content": [{ "type": "widgetRef", "widgetId": "angle_d" }] }, { "identifier": "E", "content": [{ "type": "widgetRef", "widgetId": "angle_e" }] } ], "shuffle": true, "minChoices": 2, "maxChoices": 2 } }, "widgets": { "angle_a": { "type": "protractorAngleDiagram", "angleDegrees": 120 }, "angle_b": { "type": "protractorAngleDiagram", "angleDegrees": 90 }, "angle_c": { "type": "protractorAngleDiagram", "angleDegrees": 100 }, "angle_d": { "type": "protractorAngleDiagram", "angleDegrees": 70 }, "angle_e": { "type": "protractorAngleDiagram", "angleDegrees": 70 } } } \`\`\` // ✅ ALSO CORRECT: By specifying "the TWO obtuse angles" instead of "all angles not measuring 90°", // the question now has a specific, unambiguous correct answer (only A=120° and C=100° are obtuse). // The acute angles D and E are not obtuse, so they're correctly excluded from the answer key. WRONG (tableRich dropdown cells defined, but baseType is string and "correct" uses labels): \`\`\`json { "body": [ { "type": "tableRich", "rows": [ [ { "type": "dropdown", "responseIdentifier": "dropdown_11", "choices": [ { "identifier": "POS_2_0_C", "content": [{ "type": "math", "mathml": "+2.0°C" }] }, { "identifier": "POS_4_2_C", "content": [{ "type": "math", "mathml": "+4.2°C" }] } ], "shuffle": false } ] ] } }, "responseDeclarations": [ { "identifier": "dropdown_11", "cardinality": "single", "baseType": "string", "correct": "+2.0 °C" } ] } \`\`\` CORRECT (baseType is identifier and "correct" is one of the cell choice identifiers): \`\`\`json { "body": [ { "type": "tableRich", "rows": [ [ [{ "type": "inlineInteractionRef", "interactionId": "dropdown_11" }] ] ] } ], "interactions": { "dropdown_11": { "type": "inlineChoiceInteraction", "responseIdentifier": "dropdown_11", "choices": [ { "identifier": "POS_2_0_C", "content": [{ "type": "math", "mathml": "+2.0°C" }] }, { "identifier": "POS_4_2_C", "content": [{ "type": "math", "mathml": "+4.2°C" }] } ], "shuffle": false } }, "responseDeclarations": [ { "identifier": "dropdown_11", "cardinality": "single", "baseType": "identifier", "correct": "POS_2_0_C" } ] } \`\`\` WRONG (orderInteraction with string baseType and label list for "correct"): \`\`\`json { "responseDeclarations": [ { "identifier": "order_interaction", "cardinality": "ordered", "baseType": "string", "correct": ["A first", "B second", "C third"] } ] } \`\`\` CORRECT (orderInteraction with identifier baseType and identifiers list for "correct"): \`\`\`json { "responseDeclarations": [ { "identifier": "order_interaction", "cardinality": "ordered", "baseType": "identifier", "correct": ["A", "B", "C"] } ] } \`\`\` - **Numeric Answers Rule**: For text entry interactions, if the correct answer is a decimal that can be represented as a simple fraction (e.g., 0.5, 0.25), the 'correct' value in the response declaration should be a string representing that fraction (e.g., "1/2", "1/4"). This is to avoid forcing students to type decimals. - **Cardinality Selection Rule**: * Use "single" for most choice interactions (select one answer) * Use "multiple" for interactions allowing multiple selections * Use "ordered" ONLY when the sequence of choices matters (e.g., ranking, arranging steps) * For ordering/sequencing tasks, ALWAYS use cardinality "ordered" to enable proper sequence validation - **Metadata**: Include all required assessment metadata: 'identifier', 'title', 'responseDeclarations', and 'feedback'. Return ONLY the JSON object for the assessment shell. ## NEGATIVE EXAMPLES FROM REAL ERRORS (DO NOT OUTPUT THESE) **CRITICAL: Equation Input Conversion Errors** - If an expression widget asks for an equation/inequality/symbolic form, it MUST be converted to choiceInteraction - NEVER create textEntryInteraction for "write an equation" type prompts - Automatic rejection if equation inputs are not converted to multiple choice **1. Invalid Content Structure:** **WRONG (Raw string in 'body'):** \`\`\`json "body": "

Some text

" \`\`\` **CORRECT (Structured content):** \`\`\`json "body": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Some text" }] }] \`\`\` **WRONG (Text not wrapped in a paragraph at the block level):** \`\`\`json "body": [{ "type": "text", "content": "This is invalid." }] \`\`\` **CORRECT (Text is inside a paragraph's content array):** \`\`\`json "body": [{ "type": "paragraph", "content": [{ "type": "text", "content": "This is valid." }] }] \`\`\` **2. Incorrect Slot Placement:** **WRONG (Inline slot used at the block level):** \`\`\`json "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Enter your answer: " }] }, { "type": "inlineInteractionRef", "interactionId": "text_entry_1" } ] \`\`\` **CORRECT (Inline slot is INSIDE a paragraph's content):** \`\`\`json "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Enter your answer: " }, { "type": "inlineInteractionRef", "interactionId": "text_entry_1" } ] } ] \`\`\` **WRONG (Block slot used inside a paragraph):** \`\`\`json "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Here is a graph: " }, { "type": "widgetRef", "widgetId": "graph_widget_1" } ] } ] \`\`\` **CORRECT (Block slot is a top-level item in the 'body' array):** \`\`\`json "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Here is a graph: " }] }, { "type": "widgetRef", "widgetId": "graph_widget_1" } ] \`\`\` **Currency Represented as Slots (BANNED):** **WRONG (currency as a slot in body):** \`\`\`json "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "The price is " }, { "type": "inlineInteractionRef", "interactionId": "currency7" } ] } ] \`\`\` **WRONG (currency slot in feedback):** \`\`\`json "feedback": { "correct": [ { "type": "paragraph", "content": [ { "type": "text", "content": "You saved " }, { "type": "inlineInteractionRef", "interactionId": "currency7_feedback" } ] } ], "incorrect": [] } \`\`\` **CORRECT (inline currency using MathML):** \`\`\`json "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "The price is " }, { "type": "math", "mathml": "$7" } ] } ] \`\`\` **CRITICAL ERROR EXAMPLES - THESE WILL FAIL COMPILATION:** \`\`\`json // ❌ WRONG - Raw text currency symbol "content": [ { "type": "text", "content": "The cost is $5.50" } ] // Error: currency and percent must be expressed in MathML, not raw text // ✅ CORRECT - Currency in MathML "content": [ { "type": "text", "content": "The cost is " }, { "type": "math", "mathml": "$5.50" } ] // ❌ WRONG - Raw text percentage "content": [ { "type": "text", "content": "The discount is 25%" } ] // Error: currency and percent must be expressed in MathML, not raw text // ✅ CORRECT - Percentage in MathML "content": [ { "type": "text", "content": "The discount is " }, { "type": "math", "mathml": "25%" } ] \`\`\` // ❌ WRONG - Pipe characters that imply a table layout "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Substance | Density (g/cm^3)" } ]} ] // Error: pipe characters are banned; use a table widget slot instead // Error: caret characters are banned; use MathML exponents instead // WRONG inline text example: "m/s^2" // CORRECT inline items example: // [ // { "type": "text", "content": " " }, // { "type": "math", "mathml": "ms2" }, // { "type": "text", "content": "." } // ] **Pipes in Text BANNED in the body — Use proper data tables instead of textual pipes:** WRONG: \`\`\` { "type": "paragraph", "content": [ { "type": "text", "content": "Seconds | Meters" } ] } \`\`\` CORRECT: \`\`\` { "type": "blockSlot", "slotId": "choice_a_table" } \`\`\` **3. Banned Content (LaTeX, Deprecated MathML):** **WRONG (LaTeX in text content):** \`\`\`json { "type": "text", "content": "The value is $$\\sqrt{x}$$" } \`\`\` **CORRECT (MathML in a math object):** \`\`\`json { "type": "math", "mathml": "x" } \`\`\` **LaTeX Commands - ALL BANNED:** WRONG: \`\\sqrt{a}\` --> CORRECT: \`a\` WRONG: \`\\(\\dfrac{3}{10}\\)\` --> CORRECT: \`310\` WRONG: \`\\(n = \\dfrac{96}{5}\\)\` --> CORRECT: \`n=965\` WRONG: \`\\blueD{x=2} and \\maroonD{y=4}\` --> CORRECT: \`x=2 and y=4\` WRONG: \`\\(\\tfrac{4}{3}\\)\` --> CORRECT: \`43\` WRONG: \`$\\green{\\text{Step }1}$\` --> CORRECT: \`Step 1\` WRONG: \`$3^4 \\;\\rightarrow\\; 3\\times3\\times3\\times3$\` --> CORRECT: \`34→3×3×3×3\` WRONG: \`\\(\\sqrt{121}=11\\)\` --> CORRECT: \`121=11\` WRONG: \`$\\begin{align}2\\times11&\\stackrel{?}=211\\\\22&\\neq21...\` --> CORRECT: Convert to proper MathML table structure WRONG: \`\\dfrac{Change in x}{Change in y}\` --> CORRECT: \`Change in xChange in y\` WRONG: \`\\(\\dfrac{19}{27}=0.\\overline{703}\\)\` --> CORRECT: \`1927=0.703‾\` **CRITICAL: REPEATING DECIMAL OVERLINES** - Repeating decimals MUST use with overline to show the repeating part - WRONG: 0.333... or text "0.3 repeating" - CORRECT: 0.3‾ - WRONG: 2.6666 (truncated) - CORRECT: 2.6‾ - For multi-digit repeating: 0.142857‾ WRONG: \`$\\dfrac{7^{36}}{9^{24}}$\` --> CORRECT: \`736924\` WRONG: \`\\sqrt{25}\` --> CORRECT: \`25\` WRONG: \`\\dfrac{x}{y}\` --> CORRECT: \`xy\` WRONG: \`\\(\` or \`\\)\` --> CORRECT: Remove entirely, use proper MathML tags WRONG: \`\\blueD{text}\` --> CORRECT: Just use the text content without color commands **LaTeX Dollar Sign Delimiters - BANNED:** WRONG: \`$3(9p-12)$\` --> CORRECT: \`3(9p-12)\` WRONG: \`$5, \\sqrt8, 33$\` --> CORRECT: \`5, 8, 33\` WRONG: \`paid $\` (bare dollar) --> CORRECT: \`paid $12\` (currency in MathML) CORRECT: \`$12\` (currency symbols go in MathML as operators) WRONG: \`$12\` --> CORRECT: \`$12\` WRONG: \`$x + y$\` --> CORRECT: \`x+y\` WRONG: \`$$equation$$\` --> CORRECT: \`...\` WRONG: \`costs $5\` (bare dollar) --> CORRECT: \`costs $5\` (currency in MathML) **Deprecated - ALL BANNED:** WRONG: \`-6\` CORRECT: \`|-6|\` ${caretBanPromptSection} WRONG: \`-...\` CORRECT: \`(-...)\` WRONG: \`x\` CORRECT: \`[x]\` WRONG: \`1,2,3\` CORRECT: \`{1,2,3}\` WRONG: \`a+b\` (default parentheses) CORRECT: \`(a+b)\` **General Rule for mfenced:** Replace \`content\` with \`XcontentY\` WRONG: \`x\` --> CORRECT: \`|x|\` WRONG: \`content\` --> CORRECT: \`(content)\` **4. Unsupported Interactive Widgets - CONVERT TO MULTIPLE CHOICE:** When Perseus contains interactive widgets that require drawing, plotting, or manipulation, you MUST convert them to multiple choice questions. **Perseus Input with Interactive Plotter (from your example):** \`\`\`json { "hints": [ { "content": "There was $\\blue1$ newspaper where Kai had $\\pink{3}$ pictures published.\\n\\n[[☃ image 2]]", "widgets": { "image 2": { "type": "image", "options": { "alt": "A dot plot has a horizontal axis labeled, Number of pictures published, marked from 3 to 6, in increments of 1. The number of dots above each value is as follows. 3, 1; 4, unknown; 5, unknown; 6, unknown. 3 is circled.", "backgroundImage": { "url": "web+graphie://cdn.kastatic.org/ka-perseus-graphie/5b5f67c411de00ba02b34ac0be32d384900776ba" } } } } } ], "question": { "content": "The dot plot below shows the number of pictures Kai had published in newspapers.\\n\\n[[☃ plotter 1]]\\n\\nComplete the dot plot by adding the missing dots.", "widgets": { "plotter 1": { "type": "plotter", "options": { "type": "dotplot", "labels": ["Number of pictures published"], "range": [[3, 6], [0, 4]], "starting": [[3, 1]], "correct": [[3, 1], [4, 2], [5, 1], [6, 1]] } } } } } \`\`\` **WRONG (Treating as regular interaction - will fail in Shot 2):** \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "The dot plot below shows the number of pictures Kai had published in newspapers." }] }, { "type": "widgetRef", "widgetId": "plotter_1" }, { "type": "paragraph", "content": [{ "type": "text", "content": "Complete the dot plot by adding the missing dots." }] } ] // ❌ This will fail - plotter is unsupported! } \`\`\` **CORRECT (Converting to initial visual + 3 choice visuals + multiple choice interaction):** \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "The dot plot below shows the number of pictures Kai had published in newspapers." }] }, { "type": "widgetRef", "widgetId": "dot_plot_initial" }, { "type": "paragraph", "content": [{ "type": "text", "content": "Which dot plot correctly shows the complete data?" }] }, { "type": "interactionRef", "interactionId": "dot_plot_choice" } ], "responseDeclarations": [{ "identifier": "RESPONSE", "cardinality": "single", "baseType": "identifier", "correct": "A" }] } \`\`\` **Note:** The three choice widget slots (\`dot_plot_choice_a\`, \`dot_plot_choice_b\`, \`dot_plot_choice_c\`) are declared but NOT placed in the body. They will be used by the interaction generation shot to embed in the choice options. **Perseus Input with Interactive Graph:** \`\`\`json { "question": { "content": "Plot the line y = 2x + 1 on the coordinate plane. [[☃ interactive-graph 1]]", "widgets": { "interactive-graph 1": { "type": "interactive-graph", "options": { "range": [[-10, 10], [-10, 10]], "labels": ["x", "y"], "correct": { "type": "linear", "coords": [[-1, -1], [1, 3]] } } } } } } \`\`\` **CORRECT (Converting to empty coordinate plane + 3 graph choices + multiple choice interaction):** \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Plot the line " }, { "type": "math", "mathml": "y=2x+1" }, { "type": "text", "content": " on the coordinate plane." }] }, { "type": "widgetRef", "widgetId": "coordinate_plane_empty" }, { "type": "paragraph", "content": [{ "type": "text", "content": "Which graph correctly shows the line " }, { "type": "math", "mathml": "y=2x+1" }, { "type": "text", "content": "?" }] }, { "type": "interactionRef", "interactionId": "graph_choice" } ], "responseDeclarations": [{ "identifier": "RESPONSE", "cardinality": "single", "baseType": "identifier", "correct": "B" }] } \`\`\` **Critical:** All four widget slots are declared in the \`widgets\` array, but only \`coordinate_plane_empty\` and \`graph_choice\` appear in the body. The three choice widgets will be embedded in the interaction's choices. **Perseus Input with Free-Body Diagram label-image (FBD):** \`\`\`json { "question": { "content": "A birdhouse hangs from a branch. The birdhouse is at rest.\\n\\n[[☃ image 1]]\\n\\n**Complete the free body diagram by labeling the forces acting on the birdhouse.**\\n\\n[[☃ label-image 2]]" } } \`\`\` **CORRECT (Convert to 3 FBD choice visuals + multiple choice interaction):** \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "A birdhouse hangs from a branch. The birdhouse is at rest." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Which free-body diagram correctly labels the forces?" }] }, { "type": "interactionRef", "interactionId": "fbd_choice" } ], "responseDeclarations": [{ "identifier": "RESPONSE", "cardinality": "single", "baseType": "identifier", "correct": "B" }] } \`\`\` **Implementation notes for FBD conversion:** - Generate the FBD choice visuals using the \`freeBodyDiagram\` widget (top/bottom/left/right arrows). Example: tension up, gravity down; omit irrelevant arrows. - The three choice visuals (A/B/C) must reflect plausible alternatives; only one is correct. - Do NOT include answers or labels in the body text; labels appear only inside the FBD visuals used by the choices. - Declare the three choice widget slots in \`widgets\` but do not place them in the body; only \`fbd_choice\` appears in the body. - Required slot-to-widget mapping for shot 2: - \`fbd_choice_a\` → \`freeBodyDiagram\` - \`fbd_choice_b\` → \`freeBodyDiagram\` - \`fbd_choice_c\` → \`freeBodyDiagram\` - If any \`fbd_choice_*\` is not mapped to \`freeBodyDiagram\`, the mapping pass MUST be considered a failure. **Critical:** All four widget slots are declared in the \`widgets\` array, but only \`coordinate_plane_empty\` and \`graph_choice\` appear in the body. The three choice widgets will be embedded in the interaction's choices. **Choice-Level Table Visuals — Reserve Table Widgets for Choices (Do NOT render as paragraphs):** **WRONG (shell shape incorrect — missing predeclared per-choice table widgets in \`widgets\`; tables implied in choices but not reserved here):** \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Three groups of students carried out an experiment about photosynthesis." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "First, the students submerged Elodea plants in a beaker of water mixed with baking soda (Figure 1). Then, they placed the beaker directly under a lamp." }] }, { "type": "widgetRef", "widgetId": "image_1" }, { "type": "paragraph", "content": [{ "type": "text", "content": "With the lamp off, the students counted how many bubbles rose from the plants. After a three-minute observation period, each group recorded its results." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Next, the students turned on the lamp. For three minutes, they again counted and recorded the number of bubbles they saw rise from the plants." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Based on your knowledge of photosynthesis, which data table shows the most likely results?" }] }, { "type": "interactionRef", "interactionId": "choice_interaction" } ] } \`\`\` **CORRECT (reserve three table widget slots for the choice visuals; declare them in \`widgets\` but DO NOT place them in the body):** \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Three groups of students carried out an experiment about photosynthesis." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "First, the students submerged Elodea plants in a beaker of water mixed with baking soda (Figure 1). Then, they placed the beaker directly under a lamp." }] }, { "type": "widgetRef", "widgetId": "image_1" }, { "type": "paragraph", "content": [{ "type": "text", "content": "With the lamp off, the students counted how many bubbles rose from the plants. After a three-minute observation period, each group recorded its results." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Next, the students turned on the lamp. For three minutes, they again counted and recorded the number of bubbles they saw rise from the plants." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Based on your knowledge of photosynthesis, which data table shows the most likely results?" }] }, { "type": "interactionRef", "interactionId": "choice_interaction" } ] } \`\`\` **Explanation:** Each choice contains a visual data table. Tables are ALWAYS widgets. The shell must predeclare per-choice table widget identifiers (e.g., \`table_choice_a\`, \`table_choice_b\`, \`table_choice_c\`) in the \`widgets\` array and must NOT place them in the \`body\`. These reserved widget handles are used later by the interaction generation shot to embed the tables inside the choice options. **5. Explanation Widgets - BANNED:** Perseus 'explanation' or 'definition' widgets MUST be inlined as text, not turned into slots. **Perseus Input Snippet:** \`\`\`json { "content": "Some text... [[☃ explanation 1]]", "widgets": { "explanation 1": { "type": "explanation", "options": { "showPrompt": "Does this always work?", "explanation": "Yes, dividing by a fraction is always the same as multiplying by the reciprocal of the fraction." } } } } \`\`\` **WRONG:** \`\`\`json { "body": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Some text... " }, { "type": "widgetRef", "widgetId": "explanation_1" }] }], ... } \`\`\` **CORRECT:** \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Some text..." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Does this always work?" }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Yes, dividing by a fraction is always the same as multiplying by the reciprocal of the fraction." }] } ] ... } \`\`\` **5. Widget vs. Interaction Misclassification - BANNED:** Perseus often calls interactive elements "widgets". You MUST correctly reclassify them as **interactions**. **CRITICAL DISTINCTION:** - **WIDGETS are COMPLETELY STATIC** - they display information only (images, graphs, diagrams) - **INTERACTIONS require USER INPUT** - ANY element that accepts user input MUST be an interaction - **EXCEPTION: TABLES ARE ALWAYS WIDGETS** - Even if a table contains input fields, the TABLE itself is ALWAYS a widget **ABSOLUTE RULE:** If a Perseus element requires ANY form of user input (typing, clicking, selecting, dragging, etc.), it MUST be placed in the \`interactions\` array, NOT the \`widgets\` array. **THE ONLY EXCEPTION: TABLES** - Tables are ALWAYS widgets, regardless of content - If a question requires entry IN a table, the table MUST remain a widget - The table widget will handle its own internal input fields **Perseus Input with \`numeric-input\`:** \`\`\`json "question": { "content": "Solve for x. [[☃ numeric-input 1]]", "widgets": { "numeric-input 1": { "type": "numeric-input", ... } } } \`\`\` **WRONG Shell Output (Automatic Rejection):** \`\`\`json { "body": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Solve for x. " }, { "type": "widgetRef", "widgetId": "numeric-input-1" }] }], } \`\`\` **CORRECT Shell Output:** \`\`\`json { "body": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Solve for x. " }, { "type": "inlineInteractionRef", "interactionId": "text_entry_interaction_1" }] }], } \`\`\` **Explanation:** \`numeric-input\`, \`input-number\`, and \`expression\` are ALWAYS interactions. They must be placed in the \`interactions\` array, not the \`widgets\` array. **Perseus Input with \`expression\`:** \`\`\`json "question": { "content": "Write an equation. [[☃ expression 1]]", "widgets": { "expression 1": { "type": "expression", ... } } } \`\`\` **WRONG Shell Output (Automatic Rejection):** \`\`\`json { "body": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Write an equation. " }, { "type": "widgetRef", "widgetId": "expression_1" }] }], } \`\`\` **CORRECT Shell Output:** \`\`\`json { "body": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Write an equation. " }, { "type": "inlineInteractionRef", "interactionId": "text_entry_interaction_1" }] }], } \`\`\` **Perseus Input with \`radio\`:** \`\`\`json "question": { "content": "Choose the best answer. [[☃ radio 1]]", "widgets": { "radio 1": { "type": "radio", ... } } } \`\`\` **WRONG Shell Output (Automatic Rejection):** \`\`\`json { "body": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Choose the best answer." }] }, { "type": "widgetRef", "widgetId": "radio_1" }], } \`\`\` **CORRECT Shell Output:** \`\`\`json { "body": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Choose the best answer." }] }, { "type": "interactionRef", "interactionId": "choice_interaction_1" }], } \`\`\` **FINAL RULE:** - If a Perseus element requires ANY user input = **INTERACTION** (goes in \`interactions\` array) - If a Perseus element is purely visual/static = **WIDGET** (goes in \`widgets\` array) - **EXCEPTION: TABLES ARE ALWAYS WIDGETS** - Even tables with input fields **Common Perseus elements that map to SUPPORTED INTERACTIONS:** - \`numeric-input\`, \`input-number\` → textEntryInteraction - \`expression\` → textEntryInteraction (**EXCEPTION**: Equation/inequality/symbolic-form inputs MUST be converted to choiceInteraction - see CRITICAL section above) - \`radio\` → choiceInteraction - \`dropdown\` → inlineChoiceInteraction - \`sorter\` → orderInteraction **Common Perseus elements that are ALWAYS WIDGETS:** - \`table\` - **ALWAYS a widget, even if it contains input fields** - \`image\` - static images - \`passage\` - static text passages - \`graphie\` - static diagrams/graphs (when not interactive) **Common Perseus elements that are UNSUPPORTED and must be converted to multiple choice:** - \`plotter\` - interactive plotting/drawing - \`interactive-graph\` - interactive graphing - \`grapher\` - interactive function graphing - \`number-line\` - when used for plotting points (not just display) - hotspot/image-click selection of numbers with circled/underlined digits → convert to \`choiceInteraction\` where each choice contains exactly one \`widgetRef\` of type \`decoratedNumber\`. Use the numeric value and which digits are circled/underlined; do not describe these decorations in plain text. **Remember:** Perseus misleadingly calls interactive elements "widgets" in its JSON. IGNORE THIS. Reclassify based on whether user input is required. **11. Unused Widgets in Perseus JSON - IGNORE THEM (WITH EXCEPTIONS):** Perseus JSON may contain widget definitions that are NOT actually used in the content. You MUST ONLY include widgets/interactions that are explicitly referenced in the Perseus content string via \`[[☃ widget_name]]\` placeholders. **EXCEPTIONS to this rule:** 1. When converting unsupported interactive widgets (plotter, interactive-graph, etc.) to multiple choice, you MUST create 3 additional widget slots for the choice visuals 2. When Perseus encodes visuals inside interaction choice content, you MUST predeclare widget slots for those visuals **Perseus Input with unused widget:** \`\`\`json { "content": "Compare. $\\sqrt{13}$ [[☃ dropdown 1]] $\\dfrac{15}{4}$", "widgets": { "dropdown 1": { "type": "dropdown", "choices": [{"content": "<", "correct": true}, {"content": ">", "correct": false}, {"content": "=", "correct": false}] }, "radio 1": { // ⚠️ NOTE: This widget EXISTS but is NOT referenced in content "type": "radio", "choices": [{"content": "$A$", "correct": false}, {"content": "$B$", "correct": true}] } } } \`\`\` **WRONG Shell Output (includes unused widget):** \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Compare. " }, { "type": "math", "mathml": "13" }, { "type": "text", "content": " " }, { "type": "inlineInteractionRef", "interactionId": "comparison_dropdown" }, { "type": "text", "content": " " }, { "type": "math", "mathml": "154" }] }, { "type": "interactionRef", "interactionId": "choice_interaction_1" } // ❌ BANNED: This interaction was never referenced! ] // ❌ WRONG: Extra interaction } \`\`\` **CORRECT Shell Output (only referenced widgets):** \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Compare." }] }, { "type": "paragraph", "content": [{ "type": "math", "mathml": "13" }, { "type": "text", "content": " " }, { "type": "inlineInteractionRef", "interactionId": "comparison_dropdown" }, { "type": "text", "content": " " }, { "type": "math", "mathml": "154" }] } ] // ✅ CORRECT: Only the dropdown that's actually used } \`\`\` **Explanation:** The Perseus JSON contained both a dropdown widget (used in content) and a radio widget (not used). The wrong output created slots for BOTH widgets, adding an extra choice interaction that doesn't belong. The correct output ONLY includes the dropdown that's actually referenced via \`[[☃ dropdown 1]]\` in the content string. **GENERAL RULE:** Only create slots for widgets that appear as \`[[☃ widget_name]]\` in the Perseus content. Ignore all other widget definitions. **EXCEPTIONS:** - Create 3 additional widget slots when converting unsupported interactive widgets to multiple choice - Create widget slots for visuals that appear inside interaction choice content **Perseus Input with \`table\` containing input:** \`\`\`json "question": { "content": "Fill in the table. [[☃ table 1]]", "widgets": { "table 1": { "type": "table", "headers": [...], "answers": [...] } } } \`\`\` **CORRECT Shell Output (Table is ALWAYS a widget):** \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Fill in the table." }] }, { "type": "widgetRef", "widgetId": "table_1" } ] } \`\`\` **Explanation:** Tables are ALWAYS widgets, even when they contain input fields. The table widget handles its own internal input mechanism. **Structured Content Model Requirements:** **Body Content Must Use Structured JSON Arrays:** WRONG: \`body: '

This table gives select values...

'\` (HTML string) CORRECT: \`body: [{ "type": "paragraph", "content": [{ "type": "text", "content": "This table gives select values..." }] }, { "type": "widgetRef", "widgetId": "h_table" }]\` WRONG: \`body: 'The lengths of 4 pencils were measured...'\` (raw text) CORRECT: \`body: [{ "type": "paragraph", "content": [{ "type": "text", "content": "The lengths of 4 pencils were measured. The lengths are " }, { "type": "math", "mathml": "11" }, { "type": "text", "content": " cm..." }] }]\` **CRITICAL: Inline Interaction Placement:** WRONG: \`body: [{ "type": "paragraph", "content": [{ "type": "text", "content": "Evaluate." }] }, { "type": "widgetRef", "widgetId": "text_entry" }]\` (text entry as block) CORRECT: \`body: [{ "type": "paragraph", "content": [{ "type": "text", "content": "Evaluate. " }, { "type": "math", "mathml": "..." }, { "type": "text", "content": " " }, { "type": "inlineInteractionRef", "interactionId": "text_entry" }] }]\` WRONG: \`body: [{ "type": "paragraph", "content": [{ "type": "text", "content": "The answer is" }] }, { "type": "inlineInteractionRef", "interactionId": "text_entry" }]\` (inline slot outside paragraph) CORRECT: \`body: [{ "type": "paragraph", "content": [{ "type": "text", "content": "The answer is " }, { "type": "inlineInteractionRef", "interactionId": "text_entry" }] }]\` **Inline vs Block Slots Rule:** - Text entry and inline choice interactions use \`{ "type": "inlineInteractionRef", "interactionId": "..." }\` INSIDE paragraph content arrays - Choice and order interactions use \`{ "type": "widgetRef", "widgetId": "..." }\` in the main body array - Widgets always use \`{ "type": "widgetRef", "widgetId": "..." }\` in the main body array **6. Hints and Answer Leakage in Body - BANNED:** Hints and answer-revealing content must NEVER appear in the 'body' field. **WRONG (Hints in body revealing answer):** \`\`\`json { "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "What is the constant of proportionality in the equation " }, { "type": "math", "mathml": "y=7x" }, { "type": "text", "content": "?" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "constant of proportionality = " }, { "type": "inlineInteractionRef", "interactionId": "text_entry" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Hint: If the constant of proportionality is " }, { "type": "math", "mathml": "k" }, { "type": "text", "content": ", then:" } ] }, { "type": "paragraph", "content": [ { "type": "math", "mathml": "y=kx" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "What number do we multiply by " }, { "type": "math", "mathml": "x" }, { "type": "text", "content": " to get " }, { "type": "math", "mathml": "y" }, { "type": "text", "content": "?" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Hint: The constant of proportionality is " }, { "type": "math", "mathml": "7" }, { "type": "text", "content": "." } ] } ] } \`\`\` **CORRECT (Clean body, no hints, no answer leakage):** \`\`\`json { "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "What is the constant of proportionality in the equation " }, { "type": "math", "mathml": "y=7x" }, { "type": "text", "content": "?" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "constant of proportionality = " }, { "type": "inlineInteractionRef", "interactionId": "text_entry" } ] } ] } \`\`\` **Explanation:** The wrong example contains multiple hint paragraphs that give away the answer (7) and provide step-by-step guidance. The correct version strips all hints and only presents the core question. **7. Redundant Body and Interaction Prompt - BANNED:** Never duplicate the same instructional text in both the body and interaction prompt. When an interaction has a clear prompt, the body can be empty. **CRITICAL RULE:** For ordering, multiple choice, and multiple select interactions, the question prompt MUST ALWAYS be placed in the interaction's \`prompt\` field and NEVER in the \`body\`. Keep the body to neutral context and the block slot only. **WRONG (Duplicate text in body and prompt):** \`\`\`json { "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Order the following numbers from least to greatest. Put the lowest number on the left." } ] }, { "type": "widgetRef", "widgetId": "order_interaction" } ], "interactions": { "order_interaction": { "type": "orderInteraction", "prompt": [ { "type": "text", "content": "Order the following numbers from least to greatest. Put the lowest number on the left." } ], "choices": [...] } } } \`\`\` **CORRECT (Empty body, instruction only in prompt):** \`\`\`json { "body": [ { "type": "widgetRef", "widgetId": "order_interaction" } ], "interactions": { "order_interaction": { "type": "orderInteraction", "prompt": [ { "type": "text", "content": "Order the following numbers from least to greatest. Put the lowest number on the left." } ], "choices": [...] } } } \`\`\` **Explanation:** The wrong example wastes space by repeating identical instructions. The correct version places the instruction only in the interaction prompt where it belongs, keeping the body minimal and focused. **SPECIFIC NEGATIVE EXAMPLE (ORDERING – GRAVITATIONAL FORCE) – DO NOT OUTPUT:** WRONG (instruction placed in body instead of deferring to the interaction prompt): \`\`\`json { "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Four different pairs of objects are shown. All of the objects are spheres made of the same solid material." } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Rank the pairs from strongest to weakest by the strength of the gravitational forces the objects exert on each other." } ] }, { "type": "widgetRef", "widgetId": "order_interaction" } ], "title": "Rank gravitational force strengths", "feedback": { "correct": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Correct! Gravitational force increases with the masses of the objects and decreases as the distance between their centers increases. Closer and more massive pairs exert stronger forces than pairs that are farther apart or less massive." } ] } ], "incorrect": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Not quite. Remember that gravitational force depends on two factors: greater mass means a stronger force, and greater distance between centers means a weaker force. Compare the relative sizes (masses) and the separation of the spheres when ranking." } ] } ] }, "identifier": "rank-gravitational-forces-spheres", "interactions": [ "order_interaction" ], "responseDeclarations": [ { "correct": ["A", "B", "C", "D"], "baseType": "identifier", "identifier": "RESPONSE", "cardinality": "ordered" } ] } \`\`\` CORRECT (instruction deferred to the interaction step; body contains only neutral context + slot): \`\`\`json { "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Four different pairs of objects are shown. All of the objects are spheres made of the same solid material." } ] }, { "type": "widgetRef", "widgetId": "order_interaction" } ], "title": "Rank gravitational force strengths", "feedback": { "correct": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Correct! Gravitational force increases with the masses of the objects and decreases as the distance between their centers increases. Closer and more massive pairs exert stronger forces than pairs that are farther apart or less massive." } ] } ], "incorrect": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Not quite. Remember that gravitational force depends on two factors: greater mass means a stronger force, and greater distance between centers means a weaker force. Compare the relative sizes (masses) and the separation of the spheres when ranking." } ] } ] }, "identifier": "rank-gravitational-forces-spheres", "interactions": [ "order_interaction" ], "responseDeclarations": [ { "correct": ["A", "B", "C", "D"], "baseType": "identifier", "identifier": "RESPONSE", "cardinality": "ordered" } ] } \`\`\` **SPECIFIC NEGATIVE EXAMPLE (ORDERING – TRUCK MASSES) – DO NOT OUTPUT:** WRONG (instruction placed in body instead of deferring to the interaction prompt): \`\`\`json { "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Three trucks start at rest, side by side. The trucks carry loads of different masses. The three drivers press their accelerators at the same time, and the trucks experience net forces of equal strength. The positions of the three trucks before starting and a short time later are shown below." } ] }, { "type": "widgetRef", "widgetId": "image_1" }, { "type": "paragraph", "content": [ { "type": "text", "content": "Rank the mass of each truck (including its load) from greatest to least." } ] }, { "type": "widgetRef", "widgetId": "order_interaction" } ], "title": "Rank the truck masses from greatest to least", "feedback": { "correct": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Correct! All three trucks experienced the same net force for the same time interval. The truck with the smallest acceleration has the greatest mass. The order from greatest to least mass is R, G, B." } ] } ], "incorrect": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Not quite. Since each truck experiences the same net force for the same amount of time, compare how far they traveled in that interval. The truck that traveled the least distance had the smallest acceleration and therefore the greatest mass." } ] } ] }, "identifier": "rank-truck-masses", "interactions": ["order_interaction"], "responseDeclarations": [ { "correct": ["R", "G", "B"], "baseType": "identifier", "identifier": "order_interaction", "cardinality": "ordered" } ] } \`\`\` **SPECIFIC NEGATIVE EXAMPLE (ORDERING – BODY PARTS BY COMPLEXITY) – DO NOT OUTPUT:** \`\`\`json { "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "The parts of the body listed below are involved in breaking down and absorbing nutrients from food." } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "Order the parts from least to most complex." } ] }, { "type": "widgetRef", "widgetId": "order_interaction" } ], "title": "Order body parts by complexity", "feedback": { "correct": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Correct! Multicellular organisms have a nested organization: cells form tissues, tissues form organs, and organs work together in organ systems. In this context, the correct order from least to most complex is parietal cell, epithelial tissue, stomach, and digestive system." } ] } ], "incorrect": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Not quite. Think about the biological hierarchy: cells are the basic units, groups of similar cells make tissues, tissues form organs, and organs work together in organ systems." } ] } ] }, "identifier": "order-body-parts-complexity-digestive", "interactions": ["order_interaction"], "responseDeclarations": [ { "correct": [ "PARIETAL_CELL", "EPITHELIAL_TISSUE", "STOMACH", "DIGESTIVE_SYSTEM" ], "baseType": "identifier", "identifier": "order_interaction", "cardinality": "ordered" } ] } \`\`\` CORRECT (instruction deferred to the interaction step; body contains only neutral context + slot): \`\`\`json { "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "The parts of the body listed below are involved in breaking down and absorbing nutrients from food." } ] }, { "type": "widgetRef", "widgetId": "order_interaction" } ], "title": "Order body parts by complexity", "feedback": { "correct": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Correct! Multicellular organisms have a nested organization: cells form tissues, tissues form organs, and organs work together in organ systems. In this context, the correct order from least to most complex is parietal cell, epithelial tissue, stomach, and digestive system." } ] } ], "incorrect": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Not quite. Think about the biological hierarchy: cells are the basic units, groups of similar cells make tissues, tissues form organs, and organs work together in organ systems." } ] } ] }, "identifier": "order-body-parts-complexity-digestive", "interactions": ["order_interaction"], "responseDeclarations": [ { "correct": ["PARIETAL_CELL", "EPITHELIAL_TISSUE", "STOMACH", "DIGESTIVE_SYSTEM"], "baseType": "identifier", "identifier": "order_interaction", "cardinality": "ordered" } ] } \`\`\` CORRECT (instruction deferred to the interaction step; body contains only neutral context + slot): \`\`\`json { "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Three trucks start at rest, side by side. The trucks carry loads of different masses. The three drivers press their accelerators at the same time, and the trucks experience net forces of equal strength. The positions of the three trucks before starting and a short time later are shown below." } ] }, { "type": "widgetRef", "widgetId": "image_1" }, { "type": "widgetRef", "widgetId": "order_interaction" } ], "title": "Rank the truck masses from greatest to least", "feedback": { "correct": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Correct! All three trucks experienced the same net force for the same time interval. The truck with the smallest acceleration has the greatest mass. The order from greatest to least mass is R, G, B." } ] } ], "incorrect": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Not quite. Since each truck experiences the same net force for the same amount of time, compare how far they traveled in that interval. The truck that traveled the least distance had the smallest acceleration and therefore the greatest mass." } ] } ] }, "identifier": "rank-truck-masses", "interactions": ["order_interaction"], "responseDeclarations": [ { "correct": ["R", "G", "B"], "baseType": "identifier", "identifier": "order_interaction", "cardinality": "ordered" } ] } \`\`\` **REAL-WORLD NEGATIVE EXAMPLE (FROM QA) – DUPLICATED SENTENCES:** \`\`\` { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Choose the best phrase to fill in the blank." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "A diploid organism has _____ in each cell." }] }, { "type": "interactionRef", "interactionId": "choice_interaction" } ], "interactions": { "choice_interaction": { "type": "choiceInteraction", "prompt": [ { "type": "text", "content": "Choose the best phrase to fill in the blank. A diploid organism has _____ in each cell." } ], "choices": [ ... ] } } } \`\`\` **POSITIVE EXAMPLE (DEDUPED – KEEP PROMPT ONLY):** \`\`\` { "body": [ { "type": "interactionRef", "interactionId": "choice_interaction" } ], "interactions": { "choice_interaction": { "type": "choiceInteraction", "prompt": [ { "type": "text", "content": "Choose the best phrase to fill in the blank. A diploid organism has _____ in each cell." } ], "choices": [ ... ] } } } \`\`\` This case occurs when the prompt equals the concatenation of multiple body paragraphs. Our compiler dedupes by joining adjacent paragraphs before the interaction slot and removing them when they exactly match the prompt after normalization (whitespace, entities, and math tokens). **CRITICAL: DO NOT RELY ON COMPILER DEDUPLICATION** The compiler has a last-resort safety net that removes duplicated instruction text from the body when it is identical to the interaction prompt. This exists ONLY to prevent catastrophic UX regressions if upstream generation makes a mistake. - If your output depends on this dedupe to look correct, your output is WRONG. - Treat dedupe as an EMERGENCY GUARDRAIL, not a feature. - You MUST produce clean, non-duplicated content: when an interaction has a prompt, keep those instructions out of the body. HARD STOP. NO EXCEPTIONS. Any generated item that repeats instruction text in both body and prompt may be rejected even if the compiler could fix it. Your job is to avoid the duplication entirely. **7a. Endpoint Labels Around Ordering Interactions — BANNED:** Do not place endpoint or axis labels as separate body paragraphs around an ordering interaction block slot. These labels are redundant, visually noisy, and belong in the interaction prompt only. - BANNED endpoint/axis labels in body (examples, not exhaustive): "Longest", "Shortest", "Highest", "Lowest", "Smallest", "Largest", "Earliest", "Latest", "First", "Last", "Top", "Bottom", "Left", "Right", and domain-specific variants like "Longest wavelength" / "Shortest wavelength". - The ordering direction and axis MUST appear only in the interaction prompt (e.g., "from longest to shortest (top to bottom)" for vertical orientation), never as standalone body paragraphs. - The body should be empty or include only neutral context plus the JSON block slot entry: { "type": "widgetRef", "widgetId": "order_interaction" }. WRONG (endpoint labels in body around the ordering slot): \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Rank the waves from longest wavelength to shortest wavelength." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Longest wavelength" }] }, { "type": "widgetRef", "widgetId": "order_interaction" }, { "type": "paragraph", "content": [{ "type": "text", "content": "Shortest wavelength" }] } ], "interactions": { "order_interaction": { "type": "orderInteraction", "prompt": [ { "type": "text", "content": "Drag and drop to order the waves by wavelength from longest to shortest (top to bottom)." } ], "choices": [ /* ... */ ], "shuffle": true, "orientation": "vertical", "responseIdentifier": "RESPONSE" } } } \`\`\` CORRECT (no endpoint labels in body; prompt carries direction and axis): \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Snapshots of four periodic waves are shown below. Each wave is drawn on the same grid." }] }, { "type": "widgetRef", "widgetId": "order_interaction" } ], "interactions": { "order_interaction": { "type": "orderInteraction", "prompt": [ { "type": "text", "content": "Drag and drop to order the waves by wavelength from longest to shortest (top to bottom)." } ], "choices": [ /* ... */ ], "shuffle": true, "orientation": "vertical", "responseIdentifier": "RESPONSE" } } } \`\`\` **8. Poor Visual Separation - Cramped Layout - BANNED:** Never place equations, answer prompts, and input fields all in the same paragraph. This creates visual confusion and poor readability. **WRONG (Everything crammed in one paragraph):** \`\`\`json { "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Solve the equation. " }, { "type": "math", "mathml": "20=r+11" }, { "type": "text", "content": " r = " }, { "type": "inlineInteractionRef", "interactionId": "text_entry" } ] } ] } \`\`\` **CORRECT (Clear visual separation with multiple paragraphs):** \`\`\`json { "body": [ { "type": "paragraph", "content": [ { "type": "text", "content": "Solve the equation." } ] }, { "type": "paragraph", "content": [ { "type": "math", "mathml": "20=r+11" } ] }, { "type": "paragraph", "content": [ { "type": "text", "content": "r = " }, { "type": "inlineInteractionRef", "interactionId": "text_entry" } ] } ] } \`\`\` **Explanation:** The wrong example crams the instruction, equation, variable prompt, and input field all together, creating visual confusion. The correct version separates these elements into distinct paragraphs for better readability and clarity. **9. Explanations, Strategies, Worked Solutions in Body - BANNED:** All explanatory material (strategy, step-by-step algebra, graphical intuition, and final conclusions) MUST be placed in the 'feedback' section, NOT in the 'body'. The 'body' is ONLY for the neutral problem statement and slot placement. **10. Answer Leakage in Widgets - BANNED:** Widgets MUST NEVER display, label, or visually indicate the correct answer. This is a critical violation that defeats the purpose of the assessment. **WRONG (Widget directly labels the answer):** \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "What is a name for the marked angle?" }] }, { "type": "widgetRef", "widgetId": "angle_diagram" }, { "type": "interactionRef", "interactionId": "choice_interaction" } ], "widgets": { "angle_diagram": { "type": "angleDiagram", "angles": [{ "label": "EAF", // ❌ BANNED: This labels the angle with the answer! "vertices": ["E", "A", "F"] }] } }, "interactions": { "choice_interaction": { "choices": [ { "content": [{ "type": "math", "mathml": "∠EAF" }], "identifier": "C" } ] } }, "responseDeclarations": [{ "correct": "C" }] } \`\`\` **CORRECT (Widget shows angle without revealing the answer):** \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "What is a name for the marked angle?" }] }, { "type": "widgetRef", "widgetId": "angle_diagram" }, { "type": "interactionRef", "interactionId": "choice_interaction" } ], "widgets": { "angle_diagram": { "type": "angleDiagram", "angles": [{ "label": null, // ✅ CORRECT: No label that gives away the answer "vertices": ["E", "A", "F"], "color": "#11accd" // Visual marking without answer }] } } } \`\`\` **Explanation:** The wrong example has the angle diagram widget literally labeling the angle as "EAF", which is the correct answer (∠EAF). This completely defeats the purpose of the question. The correct version visually marks the angle with color but doesn't label it with the answer. **WRONG (Massive explanations and complete worked solution in body):** \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "How many solutions does the following equation have?" }] }, { "type": "paragraph", "content": [{ "type": "math", "mathml": "3(x+5)=-4x+8" }] }, { "type": "interactionRef", "interactionId": "choice_interaction" }, { "type": "paragraph", "content": [{ "type": "text", "content": "The strategy" }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Let's manipulate the equation to simplify it and isolate " }, { "type": "math", "mathml": "x" }, { "type": "text", "content": ". We should end with one of the following cases:" }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "- An equation of the form " }, { "type": "math", "mathml": "x=a" }, { "type": "text", "content": " (where " }, { "type": "math", "mathml": "a" }, { "type": "text", "content": " is any number). In this case, the equation has exactly one solution." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "- An equation of the form " }, { "type": "math", "mathml": "a=a" }, { "type": "text", "content": " (where " }, { "type": "math", "mathml": "a" }, { "type": "text", "content": " is a number). In this case, the equation has infinitely many solutions." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "- An equation of the form " }, { "type": "math", "mathml": "a=b" }, { "type": "text", "content": " (where " }, { "type": "math", "mathml": "a" }, { "type": "text", "content": " and " }, { "type": "math", "mathml": "b" }, { "type": "text", "content": " are different numbers). In this case, the equation has no solutions." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "How do these equations correspond to the number of solutions?" }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "It's helpful to think about the equation graphically. Both the left-hand side and the right-hand side of our equation represent a line. When we set the two sides equal and simplify, we are finding any points that the two lines share. Therefore, any solutions to the equation represent the number of points that the lines share." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "- If the two lines are different but not parallel, they intersect at exactly one point. This corresponds to ending up with an equation of the form " }, { "type": "math", "mathml": "x=a" }, { "type": "text", "content": "." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "- If the two lines are the same, they share all of their points (infinitely many). This corresponds to ending up with an equation of the form " }, { "type": "math", "mathml": "a=a" }, { "type": "text", "content": "." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "- If the two lines are different and parallel, they share no points. This corresponds to ending up with a false equation of the form " }, { "type": "math", "mathml": "a=b" }, { "type": "text", "content": ", where " }, { "type": "math", "mathml": "a" }, { "type": "text", "content": " and " }, { "type": "math", "mathml": "b" }, { "type": "text", "content": " are different numbers." }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Simplifying the equation" }] }, { "type": "paragraph", "content": [{ "type": "math", "mathml": "3(x+5)=-4x+83x+15=-4x+8(Distribute)7x+15=8(Add 4x to both sides)7x=-7(Subtract 15 from both sides)x=-1(Divide both sides by 7)" }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "The answer" }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "The given equation has exactly one solution." }] } ], "interactions": { "choice_interaction": { "type": "choiceInteraction", "prompt": [{ "type": "text", "content": "How many solutions does the following equation have?" }], "choices": [ { "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "No solutions" }] }], "identifier": "A" }, { "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Exactly one solution" }] }], "identifier": "B" }, { "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Infinitely many solutions" }] }], "identifier": "C" } ] } }, "feedbackBlocks": [ { "identifier": "A", "outcomeIdentifier": "FEEDBACK__CHOICE_INTERACTION", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Incorrect. This equation does have a solution." }] }] }, { "identifier": "B", "outcomeIdentifier": "FEEDBACK__CHOICE_INTERACTION", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Correct! Simplifying gives " }, { "type": "math", "mathml": "x=-1" }, { "type": "text", "content": ", which is of the form " }, { "type": "math", "mathml": "x=a" }, { "type": "text", "content": " and therefore there is exactly one solution." }] }] }, { "identifier": "C", "outcomeIdentifier": "FEEDBACK__CHOICE_INTERACTION", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Incorrect. This equation has exactly one solution, not infinitely many." }] }] } ] } \`\`\` **CORRECT (Clean body, all teaching content moved to feedback):** \`\`\`json { "body": [ { "type": "paragraph", "content": [{ "type": "text", "content": "How many solutions does the following equation have?" }] }, { "type": "paragraph", "content": [{ "type": "math", "mathml": "3(x+5)=-4x+8" }] }, { "type": "interactionRef", "interactionId": "choice_interaction" } ], "interactions": { "choice_interaction": { "type": "choiceInteraction", "prompt": [{ "type": "text", "content": "How many solutions does the following equation have?" }], "choices": [ { "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "No solutions" }] }], "identifier": "A" }, { "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Exactly one solution" }] }], "identifier": "B" }, { "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Infinitely many solutions" }] }], "identifier": "C" } ] } }, "feedbackBlocks": [ { "identifier": "A", "outcomeIdentifier": "FEEDBACK__CHOICE_INTERACTION", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Incorrect. This equation does have a solution." }] }] }, { "identifier": "B", "outcomeIdentifier": "FEEDBACK__CHOICE_INTERACTION", "content": [{ "type": "paragraph", "content": [{ "type": "text", "content": "Correct! Simplifying gives " }, { "type": "math", "mathml": "x=-1" }, { "type": "text", "content": ", which is of the form " }, { "type": "math", "mathml": "x=a" }, { "type": "text", "content": " and therefore there is exactly one solution." }] }] }, { "identifier": "C", "outcomeIdentifier": "FEEDBACK__CHOICE_INTERACTION", "content": [ { "type": "paragraph", "content": [{ "type": "text", "content": "Strategy: Manipulate the equation to isolate x. Look for these patterns:" }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "• x = a (exactly one solution)" }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "• a = a (infinitely many solutions)" }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "• a = b where a ≠ b (no solutions)" }] }, { "type": "paragraph", "content": [{ "type": "text", "content": "Solution: " }, { "type": "math", "mathml": "3(x+5)=-4x+8⇒3x+15=-4x+8⇒7x=-7⇒x=-1" }, { "type": "text", "content": ". This gives exactly one solution." }] } ] } ] } \`\`\` **Explanation:** The wrong example turns the body into a complete lesson with strategy explanations, graphical interpretations, detailed worked solutions, and conclusions. The correct version keeps the body minimal (just the problem) and moves ALL teaching content to the feedbackBlocks where it belongs. ⚠️ FINAL WARNING: Your output will be AUTOMATICALLY REJECTED if it contains: - ANY backslash character followed by letters (LaTeX commands) - ANY dollar sign used as LaTeX delimiter (e.g., $x$, $$y$$) - ANY raw text "$" or "%" in text nodes - currency/percent MUST be in MathML: \`$amount\` and \`number%\` - ANY element - ANY raw text at the start of body content (must be wrapped in block-level elements) - ANY interactive element (numeric-input, expression, radio, etc.) in the \`widgets\` array instead of \`interactions\` (EXCEPT tables, which are ALWAYS widgets) - ANY hints or hint-prefixed lines (e.g., starting with "Hint:", "Remember:") included in the 'body' - ANY explicit statement or implication of the correct answer inside the 'body' (in text, MathML, or worked solution form) - ANY widget that labels, highlights, or visually indicates the correct answer (e.g., angle diagrams with answer labels) - ANY duplicate text appearing in both the 'body' and interaction 'prompt' fields (eliminate redundancy by using empty body when interaction has clear prompt) - ANY cramped layouts where equations, answer prompts, and input fields are all in one paragraph (use separate paragraphs for visual clarity) - ANY explanations, strategies, worked solutions, or teaching content in the 'body' (these belong ONLY in 'feedbackBlocks') - ANY widget or interaction that is NOT referenced in the Perseus content via \`[[☃ widget_name]]\` (unused widgets must be ignored) - EXCEPT for choice-level visuals and the 3 additional widgets created when converting unsupported interactive widgets - ANY unsupported interactive widget (plotter, interactive-graph, grapher) marked as an interaction instead of being converted to multiple choice **REMEMBER: Answers are ONLY allowed in feedbackBlocks. HARD STOP. NO EXCEPTIONS.** **REMEMBER: Unsupported interactive widgets MUST be converted to multiple choice questions. NEVER mark them as interactions.** Double-check your output before submitting. ZERO TOLERANCE for these violations.` return { systemInstruction, userContent } }