{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://schemas.fablepool.org/v1/hint-solution.schema.json", "title": "FablePool Hint and Solution structures", "description": "Reusable Hint and Solution definitions embedded in ProblemVersion documents. This file has no top-level document type; consumers reference its $defs.", "$defs": { "Hint": { "type": "object", "required": ["order", "content"], "additionalProperties": false, "properties": { "id": { "description": "Stable id within the problem family, preserved across versions so attempt records ('hintsUsed') remain meaningful after edits.", "type": "string", "pattern": "^[a-zA-Z0-9_-]{1,64}$" }, "order": { "description": "1-based reveal order. Hints must be revealed sequentially.", "type": "integer", "minimum": 1, "maximum": 8 }, "title": { "description": "Short label shown on the collapsed hint, e.g. 'Think about parity'. Avoid spoilers.", "type": "string", "maxLength": 120 }, "content": { "$ref": "problem.schema.json#/$defs/RichContent" }, "scorePenalty": { "description": "Fraction of the attempt's max score deducted when this hint is revealed (cumulative across hints, clamped to 0.8 total by the grader).", "type": "number", "minimum": 0, "maximum": 0.5, "default": 0.1 }, "revealCondition": { "description": "Optional gating before the hint becomes available. Both conditions are OR-ed; absent means available immediately.", "type": "object", "additionalProperties": false, "properties": { "afterIncorrectAttempts": { "type": "integer", "minimum": 1, "maximum": 10 }, "afterSeconds": { "type": "integer", "minimum": 10, "maximum": 3600 } } } } }, "Solution": { "type": "object", "required": ["kind", "content"], "additionalProperties": false, "properties": { "id": { "type": "string", "pattern": "^[a-zA-Z0-9_-]{1,64}$" }, "kind": { "description": "'official' solutions ship with the reviewed version; 'community' solutions are alternate approaches merged in through the same review workflow.", "type": "string", "enum": ["official", "community"] }, "approachTitle": { "description": "Names the technique, e.g. 'Approach 1: Invariants' — shown as a tab when multiple solutions exist.", "type": "string", "maxLength": 150 }, "content": { "$ref": "problem.schema.json#/$defs/RichContent" }, "authors": { "description": "Solution-specific attribution when it differs from the problem's authors (e.g. community-contributed alternate approach).", "type": "array", "maxItems": 5, "items": { "type": "object", "required": ["name"], "additionalProperties": false, "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 120 }, "userId": { "type": "string", "format": "uuid" } } } }, "revealPolicy": { "description": "When learners may view this solution. 'after_correct' is the default; 'after_attempts' opens after N incorrect tries; 'always' suits worked examples in lessons.", "type": "object", "additionalProperties": false, "properties": { "mode": { "type": "string", "enum": ["after_correct", "after_attempts", "always"], "default": "after_correct" }, "attempts": { "type": "integer", "minimum": 1, "maximum": 10 } } } } } } }