# Agent Lesson Authoring Guide

This is the machine-oriented guide for creating portable QuickMaths fields and lesson sets. Human authors can use **Lesson studio** inside the app instead: it provides forms, tooltips, a short tutorial, live validation, color pickers, and buttons for every browser-supported feature.

Lesson sets are declarative JSON, with YAML supported for folder authoring and Studio imports. Matplotlib figures are generated during authoring and packaged as images. Media references are validated and their bytes verified before display. Imported plotting scripts, HTML and CSS never run. The browser validates the entire prerequisite graph before installing anything, and an agent may only **stage** a set: a human must click **Install**.

Start with [`lesson-set-example.json`](lesson-set-example.json), or ask an agent to call `get_agent_guide` and `validate_lesson_set`.


### Fields and branches

QuickMaths organizes learning as **Field → Branch → Lesson**. Mathematics is a field; Geometry is a branch within Mathematics. A field owns its theme and can contain many branches. A branch groups lessons in that field; the same branch name in another field is a separate group. Lesson sets can contain several branches.

On the map, choose a **Field**, then a **Branch**, then a **Lesson** to jump to. These selectors narrow the lesson list; the combined map and your saved positions stay intact. In Lesson Studio, select or create the field, then choose an existing branch or type a new branch name for each lesson. Prerequisites can connect lessons across branches and fields.


The shipped curriculum uses these broad branches:

| Field | Branches |
| --- | --- |
| Mathematics | Arithmetic; Algebra; Geometry; Statistics; Probability |
| Geography | Geographic Methods; Cartography and GIS; Physical Geography; Human Geography; Environmental Geography |
| Programming | Programming Fundamentals; Data Structures; Algorithms; Software Engineering |

A focused category such as **Quadratic Equations** is a topic within **Algebra**, rather than a separate branch. Lesson details and Studio retain that topic. Legacy category names in installed lesson sets, imported curricula, old backups, and Studio drafts are converted automatically. Custom branch names outside the documented aliases are preserved.

The canonical map places lessons inside labeled branch bands within each field. Prerequisite connections remain intact across branch and field boundaries. Saved Plan-view coordinates, notes, paths, hidden-node choices, mastery, and unfinished tests are preserved. Switch off **Plan view** to see the canonical grouping; to adopt it for an existing custom layout, open **Plan mode → Plan details → Group by field & branch**. That action resets moved node positions while retaining notes and paths.

Existing lesson files and backups remain compatible: the saved `subject` object and `subjectId` / `subject_id` identifiers describe the field; each lesson's `subdomain` is its broad branch and optional `topic` retains finer subject matter. Keep these stable file keys and lesson IDs when editing older files. The agent tools `list_fields` and `list_branches` expose the hierarchy; `list_subjects` remains a compatibility alias.

## Envelope and field

Schema 2.1 adds media attachments to the fields, themes and prerequisite bridges introduced in 2.0. Existing 1.0 and 2.0 packs remain importable. Use 2.1 for new media packs:

```json
{
  "format": "quickmaths.lesson-set",
  "schema_version": "2.1",
  "id": "PACK_CELL_BIOLOGY",
  "name": "Cell Biology",
  "description": "A first biology curriculum.",
  "author": "Your name",
  "version": "1.0.0",
  "subject": {
    "id": "SUBJECT_BIOLOGY",
    "name": "Biology",
    "short_name": "Bio",
    "icon": "🧬",
    "description": "Life from cells to systems.",
    "theme": {
      "paper": "#eef6f1",
      "paperDeep": "#dcebe2",
      "paperLight": "#ffffff",
      "ink": "#18231d",
      "muted": "#607067",
      "line": "#c7d8ce",
      "primary": "#225c48",
      "primaryAlt": "#33765e",
      "tint": "#bfe2ce",
      "highlight": "#e4ef9b",
      "accent": "#e06b54"
    }
  },
  "track": { "skills": ["CUSTOM_BIO_CELL_001"] },
  "skills": []
}
```

- Pack IDs start with `PACK_`; new skill IDs in the default `add` mode start with `CUSTOM_`; field IDs start with `SUBJECT_`.
- IDs use uppercase letters, numbers, and underscores. Never change a published ID if learner progress may already reference it.
- Use `SUBJECT_MATH` to append lessons to the built-in Mathematics curriculum. Its built-in theme is preserved.
- Reuse an installed custom field ID to append another pack to that field. Include the same field name and theme in the source file so the file stays portable.
- Use a new field ID to create a separate field lane and theme on the combined mastery map.
- Themes accept only the eleven six-digit hex colors shown above. Arbitrary CSS is rejected.
- Schema 1.0 files still load and are migrated into `SUBJECT_MATH`.

Limits: 2 MB per set, 10 installed sets, 50 skills per set, 100 fixed questions per skill, 20 examples, 20 applications, 20 solution steps, 12 tags per field, and 8 multiple-choice options.

## Improving a native QuickMaths lesson

Schema 2.0 also supports a reversible `override` mode for improving a built-in lesson. This is not a new map node: it temporarily replaces the native lesson content under the **same lesson ID**, so completed attempts, mastery, and reviews stay attached. Unfinished tests for affected lessons restart at installation or restoration; this prevents answers from one question-bank version crossing into another. Save pending reflections and finish tests with tutor feedback first. The app protects these records across all profiles until their answers and feedback belong to saved attempts.

In Lesson Studio, choose **Edit a native lesson**, select the lesson, and click **Open editable copy**. The Studio locks the lesson ID, copies every authoring field, validates the result, and labels the install as a native improvement. In Settings, **Restore original** removes the improvement while preserving completed learner progress.

An agent has the same safe pipeline:

1. Call `open_lesson_creator` with `skill_id` when the human wants to edit visibly in Lesson Studio; or author an override file directly.
2. Keep the exact native lesson ID, its original field, and every required problem field.
3. Call `validate_lesson_set`, then `stage_custom_lesson_set`.
4. Ask the human to review the staged improvement and click **Install improvement**. The agent cannot install or restore it silently.

The override envelope differs only in `mode` and its IDs:

```json
{
  "format": "quickmaths.lesson-set",
  "schema_version": "2.0",
  "mode": "override",
  "id": "PACK_IMPROVE_MATH_ARITH_001",
  "name": "Improved integer operations",
  "description": "A clearer native lesson with stronger examples.",
  "author": "Your name",
  "version": "1.0.0",
  "subject": {
    "id": "SUBJECT_MATH",
    "name": "Mathematics",
    "short_name": "Maths",
    "icon": "Σ",
    "description": "The built-in Mathematics curriculum."
  },
  "track": { "skills": ["MATH_ARITH_001"] },
  "skills": [
    {
      "id": "MATH_ARITH_001",
      "name": "Integer operations",
      "domain": "Mathematics",
      "subdomain": "Arithmetic",
      "description": "Revised learner-facing description.",
      "prerequisites": [],
      "unlocks": ["MATH_ARITH_002"],
      "tags": ["integers"],
      "mastery": {
        "passing_score": 0.8,
        "minimum_confidence": 3,
        "max_guessing_allowed": "maybe",
        "review_after_days_if_mastered": 7,
        "review_after_days_if_learning": 2
      },
      "theory": "Revised plain-text theory.",
      "examples": [],
      "applications": [],
      "question_count": 1,
      "problems": [
        {
          "template_id": "INTEGER_ADD_REVISED_001",
          "skill_id": "MATH_ARITH_001",
          "difficulty": "easy",
          "prompt": "Compute -6 + 4.",
          "expected_answer": "-2",
          "answer_type": "integer",
          "grading_method": "exact_numeric",
          "solution_steps": ["The signs differ, so subtract 4 from 6 and keep the negative sign."],
          "mistake_tags": ["signed_addition"],
          "answer_mode": "final_only",
          "work": { "mode": "none" },
          "review_policy": { "work_review": "none", "mastery_requires_review_pass": false }
        }
      ]
    }
  ]
}
```

Guardrails are strict: override mode accepts only IDs from the built-in curriculum, rejects a changed field, rejects two installed improvements targeting the same native lesson, and never changes the total lesson count. New/custom lessons still use `mode: "add"` (or omit `mode`) and must use `CUSTOM_` IDs. To revise an already installed improvement, download its source, restore the original in Settings, then validate and install the replacement. Native improvements apply browser-wide and are not silently embedded into portable curricula; Curriculum Designer blocks export until installed improvements are restored.

Lesson files contain private answer-key fields. Even while helping an author, an agent must not return `expected_answer` or `solution_steps` through learner-facing tool results or tutoring conversation.

## Track and skills

`track.skills` lists every skill in the set exactly once and determines display order. `entry_skills` and `exit_skills` are optional and derived when omitted.

Each skill supports:

```json
{
  "id": "CUSTOM_BIO_CELL_001",
  "name": "Cell structure",
  "domain": "Biology",
  "subdomain": "Cell Biology",
  "topic": "Cells",
  "description": "Identify organelles and connect structure to function.",
  "prerequisites": [],
  "unlocks": [],
  "tags": ["cell", "organelle"],
  "mastery": {
    "passing_score": 0.8,
    "minimum_confidence": 3,
    "max_guessing_allowed": "maybe",
    "review_after_days_if_mastered": 7,
    "review_after_days_if_learning": 2
  },
  "theory": "Plain-text lesson content.",
  "examples": [
    { "prompt": "Example prompt", "solution": "Example result", "explanation": "Why it works" }
  ],
  "applications": [
    { "title": "Medicine", "description": "How the idea travels into another context." }
  ],
  "question_count": 10,
  "problems": []
}
```

`question_count` is the number of questions in one mastery attempt. It must be a whole number from 1 through the number of entries in `problems`; when omitted, QuickMaths uses the complete bank. Supplying a larger bank than `question_count` gives comprehensive retakes fresh variants without making one attempt endless. The Lesson Studio uses the complete bank for new lessons and preserves the original assessment length when improving a native lesson.

Mastery uses the lesson's passing score, minimum confidence, and guessing rule. With `max_guessing_allowed: "no"`, the learner must report no guessing; `"maybe"` accepts either no guessing or uncertainty, but never a definite guess. Review dates use `review_after_days_if_mastered` for proven and mastered results and `review_after_days_if_learning` for learning results, including results resolved by a tutor review.

When editing an existing file, Studio preserves structured prerequisite bridges, extra prompt blocks, rubric weights and IDs, optional proof obligations, trace comparison rules, and Python memory limits. Text cells in authored trace tables use JSON double quotes when their exact type or spacing matters, for example `"001"` or `"a|b"`. Pipes inside JSON strings also remain intact in Python test arguments and expected results.

`unlocks` is optional and may contain only skills from the same pack. QuickMaths also derives unlock relationships from prerequisites, including cross-field links.

## Cross-field prerequisite bridges

A prerequisite may be a globally unique skill ID:

```json
"prerequisites": ["MATH_ARITH_005", "CUSTOM_CHEM_MOLES_001"]
```

Or use an explicit bridge reference, which also verifies that the referenced skill belongs to the field you intended:

```json
"prerequisites": [
  { "subject_id": "SUBJECT_MATH", "skill_id": "MATH_ARITH_005" },
  { "subject_id": "SUBJECT_CHEMISTRY", "skill_id": "CUSTOM_CHEM_MOLES_001" }
]
```

The referenced skill must already be built in, appear in the same file, or belong to an installed pack. Install dependency packs before dependent packs. Missing references, incorrect field references, and cycles anywhere in the combined multi-field graph are rejected.

Each learner chooses graph behavior in the sidebar:

- **Hard path**: unmet prerequisites lock mastery tests. Lessons remain readable.
- **Open path**: every lesson and test is available; prerequisite lines and cross-field bridges become recommended preparation.

The file does not force a learner’s mode. The choice belongs to each learner profile and is included in progress backups.

## Fixed mastery questions

Browser lesson sets contain pre-rendered fixed questions:

```json
{
  "template_id": "CUSTOM_BIO_CELL_Q01",
  "skill_id": "CUSTOM_BIO_CELL_001",
  "difficulty": "medium",
  "prompt": "Which organelle produces most cellular ATP?",
  "expected_answer": "B",
  "answer_type": "choice",
  "grading_method": "multiple_choice",
  "options": [
    { "id": "A", "label": "Golgi apparatus" },
    { "id": "B", "label": "Mitochondrion" }
  ],
  "solution_steps": ["Mitochondria carry out aerobic respiration and generate most ATP."],
  "mistake_tags": ["organelle_function"],
  "answer_mode": "final_only",
  "work": { "mode": "none" },
  "review_policy": { "work_review": "none", "mastery_requires_review_pass": false }
}
```

Supported graders:

- `exact_numeric`
- `numeric_with_tolerance` plus numeric `tolerance`
- `multiple_choice` with 2–8 unique `{ "id", "label" }` options
- `python_program` for a learner-authored pure Python function tested inside a disposable, capability-restricted Worker
- `symbolic_expression`
- `equation_solution`
- `inequality_solution` for equivalent one-variable linear solution sets (including reversed signs and strict versus inclusive boundaries)
- `finite_set` for order-independent exact solution members; duplicates are ignored and empty-set aliases are accepted
- `rational_expression` for an equivalent formula **and** the exact original excluded values, with optional reduced-form enforcement
- `interval_set` for normalized intervals, unions, singletons, empty/all-real sets, or equivalent inequalities
- `exact_text`
- `theorem_conclusion`

Use `accepted_forms` for alternate symbolic, equation, text, or theorem conclusions. `solution_steps` is required, is private answer-key data, and is shown only after submission.

## Answer and shown-work modes

Supported `answer_mode` values are `final_only`, `final_plus_optional_work`, and `final_plus_required_work`.

| Goal | `work.mode` | What QuickMaths does |
| --- | --- | --- |
| Final answer only | `none` | No work field is required. |
| Save an explanation | `capture_only` | Requires/stores text when the answer mode requires work. |
| Check algebraic steps | `procedural_steps` | Checks line count, notation, equivalence/equation consistency, and optional final-line match. |
| Solve a rational equation | `rational_equation_steps` | Shows structured restrictions, algebra steps, candidate classification, and original-equation checks. |
| Build a sign chart | `sign_chart_steps` | Shows structured critical-point, interval-test, sign, selection, endpoint, and final-set fields. |
| Trace program state | `code_trace_steps` | Shows formatted Python plus an authored variable/output table and checks every checkpoint without executing package code. |
| Structure a proof | `proof_obligations` | Shows obligations and strategies, captures the proof, and waits for review. |
| Grade open reasoning | `rubric_check` | Shows rubric criteria, captures work, and waits for review. |

### Typed limit arguments

Use `work.mode: limit_steps` with a `work.limit` object: `variable`, `approach`, `direction` (`left`, `right`, `both`), `original_expression`, and `restrictions` (a list). Set an answer mode with required work. Native templates resolve this metadata with their givens; portable questions store resolved text. The learner submits `structuredWorkJson.limit` with the setup, retained restrictions, a `steps` list beginning with the original expression, `result_kind` (`finite`, `positive_infinity`, `negative_infinity`, `no_common_limit`) and a finite `result_value` when applicable.

The original setup is compared modulo whitespace only, before any cancellation. The engine never treats the function value as the limit or conflates signed infinities with no common limit. Setup/form checks do not validate transformations or prove the declared domain sufficient. This mode forces tutor review, gates mastery on review and disables self-review. Use ordinary proof/rubric review for IVT, continuity hypotheses and uniqueness arguments. See [limit authoring examples](CALCULUS_ENGINE.md#reviewed-limit-work).

### Finite sets, rational expressions, and interval sets

Native YAML uses these answer blocks:

```yaml
answer:
  type: finite_set
  values: ["-2", "5"]
grading:
  method: finite_set
```

The learner may enter `{5, -2}`, `x = -2 or x = 5`, or the same members in another order. Use `values: []` for the empty set. Browser lesson packs store the same information in `answer_metadata.values`; `expected_answer` should be a readable copy such as `{-2, 5}`.

```yaml
answer:
  type: rational_expression
  value: "(x + 4)/(x - 3)"
  excluded_values: ["-2", "3"]
grading:
  method: rational_expression
  require_reduced_form: true
```

This is intentionally stricter than symbolic equivalence: the formula must be equivalent, every original denominator zero must remain excluded, and no extra exclusion may be invented. A canceled factor therefore remains a hole in the domain. In browser lesson packs put `value`, `variable`, and `excluded_values` in `answer_metadata`, and put `require_reduced_form` in `grading_metadata`. The learner sees a separate excluded-values field; they never edit metadata or JSON.

```yaml
answer:
  type: interval_set
  variable: x
  value: "(-inf, -2] U (5, inf)"
grading:
  method: interval_set
```

The interval grader normalizes union order and merges intervals correctly. It accepts `U` or `∪`, `[3,3]` for a singleton, empty/all-real aliases, and equivalent input such as `x <= -2 or x > 5`, `-1 < x <= 4`, or `x != 5`. Infinity must always use an open endpoint. Endpoints may contain exact constants such as fractions, `sqrt(2)`, `pi`, and `e`.

### Rational-equation structured work

Use this mode when the reasoning itself must preserve restrictions and classify candidates:

```yaml
answer_mode: final_plus_required_work
answer:
  type: finite_set
  values: ["4"]
grading:
  method: finite_set
work:
  mode: rational_equation_steps
  prompt: State restrictions, clear denominators, solve, and check every candidate.
  target_variable: x
  original_equation: "1/(x - 1) = 1/3"
  expected_restrictions: ["1"]
  require_restrictions: true
  require_original_equation_check: true
review_policy:
  work_review: auto
```

The learner UI collects:

- original denominator restrictions;
- one algebra step per line;
- candidate values classified as `valid`, `excluded`, `extraneous`, `repeated`, or `non_real`;
- an original-equation check for each valid or extraneous candidate.

QuickMaths compares the submitted restrictions with `expected_restrictions`, checks that every algebra row is an equation, classifies candidates against `original_equation`, and compares candidates marked `valid` with the finite-set final answer. Trusted native YAML may omit `original_equation` and `expected_restrictions` because generation derives both from the rendered prompt; fixed browser/Depot packs must include them when the corresponding checks are required. Lesson Studio provides friendly fields for both. The learner's structured object persists inside the ordinary draft/attempt/backup pipeline as `structuredWorkJson`; authors and learners do not need to write that JSON manually.

### Sign-chart structured work

Use a sign chart when a polynomial or rational inequality is determined by critical points and interval signs:

```yaml
answer_mode: final_plus_required_work
answer:
  type: interval_set
  variable: x
  value: "(-inf, -1) U [2, inf)"
grading:
  method: interval_set
work:
  mode: sign_chart_steps
  target_variable: x
  sign_chart:
    expression_kind: rational
    expression: "(x - 2)/(x + 1)"
    relation: ">="
    expected_factorization: "(x - 2)/(x + 1)"
    reduced_expression: "(x - 2)/(x + 1)"
    require_factorization: false
    critical_points:
      - { value: "-1", kind: undefined, multiplicity: 1, factor: "x + 1" }
      - { value: "2", kind: zero, multiplicity: 1, factor: "x - 2" }
    require_test_values: true
    require_interval_signs: true
    require_endpoint_decisions: true
    require_final_answer_match: true
review_policy:
  work_review: auto
```

`kind` must be `zero`, `undefined`, or `hole`. A zero may be included only for an inclusive relation; a pole or hole is never included. Multiplicity records whether a factor changes sign at that point. The learner editor asks for every critical point, a test value and sign in each interval, which intervals belong in the solution, endpoint decisions, and the final interval set. Interval boundaries are derived and sorted from the critical points. QuickMaths independently computes the sign in each interval, verifies that every learner test value lies strictly inside its row, checks endpoint inclusion, reconstructs the selected set, and compares it with the final authored interval set.

Native templates may place placeholders anywhere inside `work.sign_chart`; the exporter renders that nested metadata recursively. Uploaded packs remain fixed data and never execute arbitrary template expressions.

### Formatted prompt code

Keep `prompt` as the complete plain-text accessibility fallback. Add optional `prompt_blocks` when indentation or line breaks matter:

```json
{
  "prompt": "Trace the program and state what it prints.",
  "prompt_blocks": [
    { "type": "text", "text": "Trace the program and state what it prints." },
    { "type": "code", "language": "python", "text": "x = 2\ny = x + 3\nprint(y)" }
  ]
}
```

Only `text` and `code` blocks are accepted. QuickMaths escapes every character, labels the language, preserves whitespace, and uses horizontal scrolling; it never interprets lesson HTML. A problem may contain at most 12 blocks and 12,000 characters across the blocks. Lesson Studio exposes this as **Formatted code block** beneath the ordinary prompt.

### Structured code traces

`code_trace_steps` checks an authored state model. It displays code but does not execute code from the package:

```json
{
  "answer_mode": "final_plus_required_work",
  "work_required": true,
  "work": {
    "mode": "code_trace_steps",
    "prompt": "Complete every execution checkpoint.",
    "trace_spec": {
      "language": "python",
      "display_code": "x = 2\ny = x + 3\nx = 9\nprint(y)",
      "columns": ["step", "x", "y", "output"],
      "expected_rows": [
        { "step": 1, "x": 2, "y": null, "output": "" },
        { "step": 2, "x": 2, "y": 5, "output": "" },
        { "step": 3, "x": 9, "y": 5, "output": "" },
        { "step": 4, "x": 9, "y": 5, "output": 5 }
      ],
      "comparison": {
        "trim_strings": true,
        "numeric_equivalence": true,
        "blank_equals_null": true
      }
    }
  },
  "review_policy": { "work_review": "auto" }
}
```

`step` is mandatory and stable; other columns are author-chosen simple strings, numbers, booleans, or null. The learner gets ordinary table cells. Submission reports missing rows, duplicate/unexpected step labels, wrong variable values, and wrong output separately. The trace is saved in the same draft, attempt, backup, and tutor-review pipeline as other structured work. In Lesson Studio, enter one column per line and one expected row per line using `|` between cells—authors and learners never have to hand-write the response JSON.

### Sandboxed Python function grading

Use `python_program` only for a deterministic pure-function task:

```json
{
  "prompt": "Implement is_even(number).",
  "expected_answer": "All declared Python tests pass.",
  "answer_type": "code",
  "grading_method": "python_program",
  "program_spec": {
    "runtime": "python_subset_v1",
    "entrypoint": {
      "kind": "function",
      "name": "is_even",
      "parameters": [{ "name": "number", "type": "int" }],
      "return_type": "bool"
    },
    "tests": [
      { "id": "even", "args": [8], "expected_return": true, "visibility": "example" },
      { "id": "odd", "args": [7], "expected_return": false, "visibility": "after_submission" },
      { "id": "zero", "args": [0], "expected_return": true, "visibility": "hidden" }
    ],
    "limits": {
      "wall_time_ms": 1500,
      "step_limit": 20000,
      "memory_mb": 32,
      "stdout_chars": 1000
    },
    "policy": {
      "allowed_builtins": [],
      "imports": [],
      "network": false,
      "storage": false,
      "clock": false,
      "randomness": false
    }
  }
}
```

Supported parameter types are `json`, `bool`, `int`, `float`, `str`, `list`, and `dict`; the return type may also be `none`. Tests contain JSON data only—never expressions, callbacks, source code, URLs, or a package-authored test harness. Include at least one `example` test. `after_submission` results appear only after submission; `hidden` cases never reveal their arguments or expected values in learner-facing results or WebMCP.

The first run loads the self-hosted, integrity-pinned Pyodide 0.28.3 runtime in a module Worker. No executable runtime script or package is fetched from a third-party CDN, and runtime package installation is unavailable. QuickMaths strictly validates the complete payload, parses learner source with a trusted AST supervisor, permits only top-level function definitions and an explicit syntax subset, creates a fresh restricted namespace for every test, and exposes only the authored subset of safe builtins. It rejects imports, classes, decorators, annotations, exception handlers, dynamic evaluation, private/dunder names, unapproved methods, files, process APIs, network, browser APIs, storage, clocks, and randomness. Source and payload bytes, test count, argument depth/size, AST size/depth, repetition and range size, exponent size, integer size, execution steps, aggregate captured output, return size, and wall time are bounded. Every grading run owns one disposable Worker; timeout, cancellation, success, and failure all terminate it, and late messages are ignored. `memory_mb` is a validated forward-compatible contract field, while current browser enforcement relies on the disposable Worker plus structural/data/result bounds rather than claiming a precise per-Worker memory quota.

WebMCP cannot execute learner or agent-authored Python. It may validate or stage a package for visible human review, but the human must install it and the learner must press **Run sandboxed tests** for every run. Learner source and the bounded grade summary persist in the profile, backup, and optional complete-workspace sync; captured stdout is discarded. Runtime-startup failures block assessment submission and never become learner mistakes.

Allowed builtins are: `abs`, `all`, `any`, `bool`, `dict`, `enumerate`, `float`, `int`, `len`, `list`, `max`, `min`, `range`, `round`, `set`, `sorted`, `str`, `sum`, `tuple`, and `zip`. Grant only what the problem needs. The supported value-method allowlist covers ordinary string, list, and dictionary transformations documented in Lesson Studio. Programs that need imports, files, exceptions, classes, command-line input, real time, or randomness remain capture/rubric tasks; do not pretend the subset runs them.

### Kernel-backed formal proof specifications

A question opts into the native proof workspace with `proof_spec`. It bypasses the legacy short-answer grader and requires a fresh complete learner-proof certificate from the pinned browser verifier (or explicit desktop companion) before assessment. Metadata, reference proofs and tutor opinions do not create a badge or a passing grade. Unavailable verification leaves the question pending without penalizing the learner. See [the formal learning guide](FORMAL_LEARNING.md) for setup, demo lessons and the reference-free tutor contract.

```yaml
proof_spec:
  version: "0.1"
  statement:
    declarations: ["x:real"]
    assumptions: ["x != {a}"]
    goal: "x + {a} = {a} + x"
  parameter_contract:
    required_public: ["a"]
  allowed_rules: [ring_identity]
  # Optional loading guidance; blank/absent means automatic inference.
  capabilities: [algebra]
  assessment_policy: {}
  environment:
    backend: lean4
    library: mathlib
```

Generated formal statements resolve from the **same public parameter snapshot as the learner prompt**. A value is public only when it appears as an exact named prompt placeholder such as `{a}`; hidden derived generator state is not available to `proof_spec`. If the formal statement references a hidden value, generation fails rather than proving a theorem different from the displayed exercise. Fixed questions may use a proof specification with no placeholders.

The authoring layer accepts only declarative data: declarations, assumptions, a goal, an optional public-parameter contract, allowed rule names, optional curated capability loading guidance, assessment metadata, a reference-proof candidate, and pinned-environment metadata. Capability IDs are `algebra`, `limits`, `derivatives`, `sequences-series`, and `radicals`. If supplied, the array must include the capabilities inferred from the statement and allowed rules; unknown or duplicate IDs fail closed. A `sqrt` expression or a rule containing `sqrt` or `conjugate` infers `radicals`; for example, `sqrt_derivative` infers both `derivatives` and `radicals`, while `conjugate_limit` infers both `limits` and `radicals`. If omitted, the persisted specification remains unchanged and the loader may infer guidance automatically. This metadata only guides curated capability loading; it does not change rules, environments, certificates or trust checks. It does not accept Lean source, tactics, imports, macros or executable package code. Reference proofs remain candidates until independently checked by the isolated verifier.

Use an empty `assessment_policy` for unrestricted formal assessment, or one of the
supported required methods: induction, cases, contradiction (negation),
universal introduction, implication, existential elimination, or
`derivative_definition`. The derivative-definition policy requires
`derivative_from_limit` to cite one previously proved finite two-sided punctured
limit at `0`, with no extra domain restriction, exact `(f(a+h)-f(a))/h` form,
and a matching derivative target; `h` cannot be free in `f`, `a`, or the result.
For `f(x)=x^2` at `a=3`, prove `((3+h)^2-3^2)/h -> 6` using
`rational_hole_limit` simplified to `h+6` while retaining the kernel-checked
`h != 0` puncture, then cite it with `derivative_from_limit`. This is not
general epsilon-delta or automatic derivative-definition reasoning. The final
root step must use the selected method, and unknown methods or extra fields stay
blocked. See [method and subproof details](FORMAL_LEARNING.md#constructing-scoped-proofs).
Reference candidates must still be checked before publication and never award
learner credit.
The [browser Lean corpus-parity and memory experiment](releases/2026-09-13-browser-lean-parity.md)
does not introduce new authoring fields or Studio controls. Continue declaring
capabilities rather than module names, runtime URLs or worker settings.
The experimental harness remains assessment-ineligible. The production browser
backend enables reference checks and learner certification on supported PC and
Android browsers through the protected formal-runtime route.

### Checked maths steps are not formal proofs

The Advanced Algebra curriculum primarily uses `procedural_steps`. The learner writes one equivalent equation, inequality, or expression per line, and QuickMaths conservatively checks each transition plus the final-line match. Use `line_type: "equation"` for `=` steps and `line_type: "inequality"` for `<`, `<=`, `>`, or `>=` steps; inequality mode checks the complete one-variable solution set, including sign reversals. That workflow can finish automatically.

`proof_obligations` is a different system. A proof question has two deliberately separate judgments:

1. The short final conclusion is graded with the selected final-answer grader and `accepted_forms`.
2. The proof text is required and stored with the exact obligation checklist shown to the learner.
3. QuickMaths checks that a meaningful submission exists, but never treats a correct conclusion or matching keywords as proof validity.
4. A self, human, or WebMCP tutor records `satisfied`, `flawed`, `missing`, or (for optional items) `not_applicable` for every obligation, with an evidence note. Rubrics receive awarded points and a note for every weighted criterion.
5. QuickMaths derives the review score and verdict from those item-level results. Pending review initially freezes mastery; a completed pass/partial/revision/fail resolution applies +12/+3/0/−6 mastery points, and replacing a review replaces that delta instead of stacking it. A required review must pass before the skill can become Proven.

Author obligations as concrete logical milestones—such as “Derives p² = 2q²” and “Explains the contradiction with lowest terms”—rather than vague instructions such as “Shows good reasoning.” Accepted strategies are legitimate routes the learner may take, not phrases they must reproduce.

Procedural example:

```json
{
  "answer_mode": "final_plus_required_work",
  "work": {
    "mode": "procedural_steps",
    "prompt": "Show one equivalent equation per line.",
    "line_type": "equation",
    "target_variable": "x",
    "minimum_steps": 2,
    "require_final_answer_match": true
  },
  "review_policy": {
    "work_review": "auto",
    "mastery_requires_review_pass": false,
    "allow_self_review": true
  }
}
```

Proof example:

```json
{
  "answer_mode": "final_plus_required_work",
  "work": {
    "mode": "proof_obligations",
    "prompt": "Write a concise proof.",
    "proof_policy": {
      "obligations": ["State the claim", "Justify each inference", "Close the argument"],
      "accepted_strategies": ["direct proof", "contradiction"]
    }
  },
  "review_policy": {
    "work_review": "tutor_required",
    "mastery_requires_review_pass": true,
    "allow_self_review": false
  }
}
```

Rubric example:

```json
{
  "work": {
    "mode": "rubric_check",
    "prompt": "Explain your model and conclusion.",
    "rubric": {
      "criteria": [
        { "id": "model", "description": "Chooses a defensible model", "weight": 2 },
        { "id": "reasoning", "description": "Connects evidence to the conclusion", "weight": 1 }
      ]
    }
  },
  "review_policy": {
    "work_review": "self_review",
    "mastery_requires_review_pass": true,
    "allow_self_review": true
  }
}
```

Supported `work_review` values are `none`, `optional`, `auto`, `self_review`, and `tutor_required`. Proof and rubric modes always enter a pending-review state; QuickMaths does not pretend to semantically autograde open reasoning.

## How the original advanced YAML maps to the web format

The repository’s trusted built-in Mathematics YAML is exported as browser-safe runtime templates, preserving fresh generated values and comprehensive scenario coverage without executing arbitrary code. Uploaded lesson sets deliberately use the fixed-data web format below. Every original authoring concern has an explicit browser path:

| Original YAML capability | Web lesson-set path |
| --- | --- |
| Track metadata and graph | `track`, `skills`, `prerequisites`, fields, and bridge references |
| Skill metadata, theory, examples, applications, tags | Same learner-facing fields in each skill |
| Mastery thresholds and review intervals | `mastery` block |
| Final answer block and accepted forms | Flattened `expected_answer`, `answer_type`, grader, tolerance, and `accepted_forms` |
| Fixed tests | Direct `problems` entries |
| Generated tests: variables, derived expressions, constraints, prompt/answer templates, retry limits | Trusted built-in Mathematics templates run through the allowlisted browser generator and can be rerolled/audited in Lesson Studio. Uploaded browser files never execute expressions; author them as explicit fixed `problems`. |
| Random order / question count | Native Mathematics shuffles every authored scenario and draws fresh values. For uploaded sets, set `question_count` and optionally supply a larger fixed bank; QuickMaths rotates through a complete configured set. |
| Explanation templates | Pre-render into literal `solution_steps`. |
| Final-only, optional, or required work | `answer_mode` |
| Capture, procedural, rational-equation, sign-chart, code-trace, proof, and rubric workflows | All eight browser `work.mode` values above |
| Review policy and mastery gate | `review_policy` block |
| Draft skills | Keep them in Lesson studio or outside an installed pack; installed packs accept live skills only. |
| Deprecated/replacement skills | Publish a new stable skill ID and keep the old source file for backup compatibility. Browser packs do not silently redirect progress. |

The security boundary is intentional: variables, derived expressions, constraints, and arbitrary generator/grader code are trusted-author build features, not uploaded runtime features. An agent should generate explicit fixed questions or use the repository’s validated Python export pipeline.

## Lesson media and Matplotlib

Use schema `'2.1'` with a `media` array on any skill, example, application or problem. Each item has a folder-relative `src`, required descriptive `alt`, and optional `caption`, pixel `width` / `height` (1–4096), and `fit: contain` or `cover`. Images, video and audio are inferred from the extension; video/audio support up to three fallback `sources`, and video supports an image `poster`. Keep dimensions and descriptions consistent with the question's values. Media appears in lessons, questions and saved results.

```yaml
media:
  - src: media/triangle.svg
    alt: A right triangle with base 4 units and perpendicular height 3 units.
    caption: Identify the two perpendicular sides.
    width: 800
    height: 500
```

Put attachments in the lesson folder, beside `lesson-set.yaml` or `lesson-set.json`. Studio **Open folder** creates a portable pack; **Attach a file** embeds a small file directly. **Publish → Open lesson folder** uploads larger attachments separately before pinning the manifest to their GitHub revision. Only referenced files are collected. Built JSON contains an `assets` entry for each file: `path`, `mime_type`, `bytes`, `sha256`; portable assets add `data_base64`. Installed hosted packs retain `asset_base_url`, derived from the manifest URL. Preserve these fields in backups and edits. Never send private storage credentials with a media request.

Limits are 1 MB total for portable embedded attachments, 25 MB per hosted file, 100 MB per folder, 60 files per pack, 12 items per section, and 2 MB for the JSON manifest. Hosted media requires its public repository to remain available; it is not copied into every workspace checkpoint. Supported extensions: png/apng/jpg/jpeg/gif/webp/avif/svg/bmp, webm/mp4/m4v/ogv, and mp3/wav/ogg/oga/m4a/flac/opus. Codec support depends on the browser; provide fallback video formats when needed.

Matplotlib runs in the author's environment. Save PNG/SVG with `fig.savefig(...)`, or add declarative `matplotlib_figures` to YAML and run `quickmaths build-lesson lesson-set.yaml --output built-lesson` after `pip install -e ".[media]"`. Add `--portable` to embed small attachments. Import or publish the built output. Arbitrary plotting Python is never run when importing or viewing a lesson. The [complete media reference](LESSON_MEDIA.md) documents drawing layers, format options, safety checks and folder workflows. Start from the [geometry YAML](examples/geometry-media/lesson-set.yaml) or [portable example](lesson-media-example.json). Agents can request this section with `get_lesson_authoring_guide({"section":"media"})`.

### Declarative Cartesian diagrams and mathematical blocks

Attach `diagram` to a native template or resolved portable question. Cartesian data supports independently restricted curves, open/closed endpoints, isolated points, secant segments, excluded inputs, asymptotes and labels. Native bindings can use only names stated directly as `{name}` in `prompt_template`; numeric coordinates can use bounded arithmetic from these public givens. No expected-answer or hidden-derived variables may enter a graph, label or description. Curves use a bounded arithmetic parser with `x`, integer powers, `sqrt`, `abs`, `sin`, `cos`, `exp` and `log`, never JavaScript/Python execution. Trigonometric inputs are radians; `log` means natural logarithm and requires a positive argument. Intervals crossing a pole or an undefined domain are not connected; nonfinite results are omitted. Very large trigonometric arguments (above 1e12 in magnitude) are omitted. Saved visual drafts retain their original question and diagram after template changes.

Use `math_blocks` on a lesson, example, application or problem for piecewise rules, fractions, directed limits, derivations or notation. Every block requires `alt` and `linear_text`; the latter is exposed as copyable text. Existing text fields remain unchanged. Native explanation line breaks are preserved as separate solution steps. See [Cartesian and limit examples](CALCULUS_ENGINE.md) and [math-block schema/examples](MATH_DISPLAY.md).

The native exporter keeps its default 1,000,000-byte budget and now reports bytes, remaining space and per-branch/source attribution. `--media-report` saves a report; `--previous-media-report` adds batch growth. An explicit `--native-media-budget` affects native builds only, not untrusted imported packs. This is an embedded-media capacity bridge, not hosted chunks or offline prefetch.

## Human Lesson Creator

Open **Lesson studio** in the left sidebar. Its optional **Mathematical displays and function graph** panel validates and previews structured display JSON. Lesson, example, application and question math blocks survive source edits and exports; question graphs use resolved numeric coordinates. The native runtime preview rerolls graphs together with question givens. Select **Limit argument · tutor review** for guided setup fields and a mandatory review gate. It can:

- extend an installed field or create a themed field;
- create, remove, and switch between multiple lessons;
- select prerequisites across every field;
- add theory, examples, applications, tags, mastery thresholds, and review timing;
- add fixed questions with every supported grader;
- configure answer modes, capture/procedural/proof/rubric work, and review gates;
- add escaped formatted code blocks, structured trace tables, and declarative sandboxed Python function tests;
- preview the exact answer box, proof checklist, rubric, and review path the learner will see;
- start advanced question types from editable examples with plain-language explanations;
- open an existing JSON file, autosave an author draft locally, validate, download, install, and prepare a public Lesson Depot submission.

It is the preferred route for humans. This guide is the preferred route for agents and advanced source-control workflows.

### Proofs and rubrics in Lesson Studio

There is no special student proof syntax. Choose **Structured proof** under **How the learner answers**, then write one proof obligation per line. Each line becomes a visible checklist item above a required plain-text response box. Accepted proof approaches are suggestions, not exact phrases the learner must type. QuickMaths checks that a response was supplied, saves it with the attempt, and keeps the lesson in Learning until a permitted self, human, or agent review passes it.

Choose **Rubric-reviewed response** for essays, investigations, interpretations, and other open reasoning. Write one observable criterion per line. The learner sees those criteria before answering, and the saved response waits for review in the same way as a proof. The local final-answer grader still checks the separate short answer field; it does not pretend to semantically grade the proof or long response.

## WebMCP workflow

Agents must open QuickMaths inside the ChatGPT or Codex in-app browser; external browser tabs cannot expose WebMCP tools. Reuse the existing in-app tab, then use this sequence:

1. `get_lesson_authoring_guide` with the relevant section (normally `summary`, then `grading_and_work`)
2. `list_subjects`
3. Author a schema 2.0 JSON string
4. `validate_lesson_set`
5. `stage_custom_lesson_set`
6. Tell the human to review the visible preview and click **Install**

`get_lesson_authoring_guide` returns this bundled guide by topic, so an agent does not need a separate network fetch or the entire operating manifest for a small authoring task. Agents cannot call the final install action. They should recommend a full JSON progress backup before curriculum changes and never quote expected answers or solution steps into learner tutoring.

For community lessons, agents can call `search_lesson_depot` to inspect catalog metadata and `stage_depot_lesson` to download, hash-check, validate, and visibly stage a chosen package. `stage_depot_lessons` preflights an ordered group so a later pack may depend on an earlier pack in the same batch, and it rejects aggregate capacity problems before opening the review queue. Neither tool can install anything; the human reviews every item in Settings and clicks **Install** or **Skip** separately.

## Publishing to the Lesson Depot

The public Depot is federated. Authors keep lesson files and a small `quickmaths-registry.json` catalog in their own public GitHub repositories. A machine-readable `[Registry]` Discussion lets an automatic GitHub Action validate and add the feed to global discovery without a maintainer merge. GitHub Discussions provide recommendations, serious-concern flags, comments, and immutable per-version review history. Browsing and installation require no sign-in. To participate inside QuickMaths, a user can authorize the separate least-privilege QuickMaths Community GitHub App; no personal access token is requested, and its user token never enters Workspace Storage checkpoints or backups.

From Lesson Studio, choose **Publish to Lesson Depot** after validation. QuickMaths downloads the author file, copies a detailed Codex publishing prompt, and opens the registry discussion form. The publishing agent should:

1. Create or use the author's own public GitHub repository.
2. Put the lesson under an immutable versioned path, commit it, and record that content commit SHA.
3. Generate `quickmaths-registry.json` with `format: quickmaths.lesson-depot.catalog`, schema `1.0`, a `registry` identity, complete package metadata, the lesson URL pinned to its content commit, and SHA-256 digest.
4. Use a `registry.id` matching `github-owner/repository`, an uppercase 2–24 character namespace, `PACK_NAMESPACE_*` package IDs, and `CUSTOM_NAMESPACE_*` authored skill IDs.
5. Validate the full lesson and prerequisite graph, commit the registry, then pin the submitted registry URL to that second complete 40-character commit SHA. The lesson and registry commits may differ, but both files must belong to the declared repository.
6. Create the `[Registry]` Discussion only after showing the human the exact public files and receiving approval.

The registry builder never executes publisher code. Every valid submission appears immediately as **New**. Community support can promote it to **Community recommended**; serious flags can mark it **Contested** and hide it from ordinary search without erasing it. Users may also bypass global discovery by adding a public GitHub registry URL directly under **Settings → Manage lesson sources**.

Registry metadata and community reactions are untrusted signals. Every installation independently verifies the package hash and always runs the local lesson validator before showing the human confirmation. Federated registry failures are isolated so one publisher cannot take down the official catalog or other feeds. If WebCrypto is unavailable, hash verification fails closed and installation stops. Bounded readers reject or cancel registries above 500 KB and lesson files above 2 MB before parsing. Public author files contain answer keys and solutions by design; do not paste them into learner tutoring conversations.

Portable curriculum imports compare the complete normalized package content, including prerequisites, theory, questions, expected answers, work rules, and review policy. Reusing only a matching package ID or version is not sufficient. Every enabled external package must be embedded in the curriculum so the same file cannot silently resolve to different locally installed content on another device.

## Installation, progress, and backups

1. Download a full progress backup before changing installed content.
2. Use **Settings → Load lesson set**, use Lesson studio, or ask an agent to stage a set.
3. Review the field, author, version, lesson count, question count, and prerequisite links.
4. Confirm installation.
5. Choose the field in the left sidebar.

Installed lesson content, field metadata/themes, per-profile field and path choices, progress, drafts, attempts, reviews, and timers are included in full JSON backups. CSV files are analysis-only.

Import is rejected without changing state when a file has an unsupported version, duplicate IDs, missing or mislabelled prerequisite bridges, a cycle anywhere in the combined graph, mismatched question/skill IDs, unsupported grading/work/review modes, malformed choices/rubrics/proof obligations, executable content, or exceeded safety limits.

Built-in Mathematics lessons use trusted runtime templates shipped with QuickMaths, so each retake draws fresh values while still covering every authored scenario. Uploaded, Depot, and Studio-authored lesson sets are deliberately fixed-data packages: they may contain large validated question banks, but they cannot ship or execute generator code. When editing a native Mathematics lesson, Lesson Studio includes a rerollable author preview and downloadable audit of the original runtime generator before you install a fixed reversible override.

The Statistics and Probability native batches add 21 lessons and 280 authored scenarios. Native authoring may use the trusted distribution helpers `normal_cdf`, `inverse_normal_cdf`, `t_cdf`, `chi_square_cdf`, `chi_square_sf`, and `f_sf` with domain-checked arguments. These helpers are limited to first-party native templates; uploaded and community lesson files remain fixed data and cannot execute helper or generator code. Learner teaching prose should explain the statistical idea rather than expose helper-function source strings.

## Branch reference

Consult [the complete lesson classification](./FIELDS_AND_BRANCHES.md) and [the machine-readable taxonomy](./learning-taxonomy.json). Keep file keys `subject`, `subjectId` / `subject_id`, and `subdomain` compatible. Add optional `topic` for focused subject matter. Prefer an existing broad branch; create another branch only when it describes a distinct area of the field. The map automatically creates its band.

### Monomial root proof generation (September 2026)

Existing supported monomial-times-geometric root tests now use a shorter
kernel-checked proof. No new lesson fields, rules, capabilities, or Studio
settings are required; nonmonomial polynomials retain their existing proof path.
See the [focused update](releases/2026-09-14-monomial-proof-optimization.md).

### Verification time allowance

New formal proof jobs and Studio reference checks allow up to **60 seconds**
per verification. On phones a proof can take tens of seconds; initial runtime
setup/download can take longer. The proof workspace displays this guidance.
Saved requests and certificates keep their original policy for exact replay;
this update does not silently rewrite old evidence or enable browser grading.

The reusable engineering workflow is documented in
[Formal proof optimization procedure](FORMAL_PROOF_OPTIMIZATION_PROCEDURE.md).

### PC and Android browser proof checking

Chrome/Edge on PC and Chrome on Android can check supported formal proofs locally.
Start a formal question to open the protected proof workspace; on first use it
reopens the saved test and enables isolation. Select **Start my proof** again
after the reload. Initial setup downloads shared verified assets (hundreds of
megabytes); use Wi-Fi. Cached environments are reused. Each proof check allows
up to a minute; initial setup can take longer. Your draft survives a failed check.

Only a fresh Lean certificate for the exact submitted proof can award formal
credit. Author reference checks remain separate from learner submissions. iPhone
and iPad browser support is deferred; drafts can still be saved. Existing imported
formal lessons use the same backend when their declared capabilities are supported.
The local desktop companion remains available through explicit client options.

## Native compatibility and reviewed capstones

Native source validation accepts both structured legacy proof strategies and the web format: `proof_policy.accepted_strategies` as descriptive strings with a separate `obligations` list. String obligations receive stable positional IDs such as `obligation_1`; use explicit IDs when later edits may reorder obligations. Native rubric validation accepts `weight` as an alias for `points`; values must remain positive. These fields describe review criteria, not automatic semantic proof grading.

Ordinary expression answers support `sqrt`, `abs`, `sin`, `cos`, `exp` and natural `log` (`ln` is a browser alias). Comparison is bounded numerical checking, not a theorem certificate. Use explicit multiplication for ambiguous multi-letter products. Formal generated statements may resolve only prompt-visible public parameters; invalid formal generation fails instead of falling back to ordinary grading.

Existing drafts preserve complete resolved snapshots. Changes to generation defaults or scenario order apply to new draws, not already saved answers. See [native compatibility details](NATIVE_COMPATIBILITY_FIXES.md).

## Native lesson standards audit (September 21, 2026)

The shipped catalog contains 160 lessons; 18 native Mathematics lessons now include a formal exercise. Use formal tasks for precise statements supported by the current rules, alongside ordinary practice and explicit tutor review for explanations. Do not convert a conceptual capstone into answer-string grading or claim that an empty assessment policy enforces a requested method.

For `sqrt_square_nonnegative`, author the cited guard as `0 <= x`. Difference-quotient cancellation retains `h != 0`; its algebraic identity alone is not a derivative proof. A punctured limit is separate from the assigned function value, and IVT existence does not establish uniqueness. The native composition, difference-quotient, radicals, systems, limits and continuity lessons provide current examples.

Run `node scripts/audit_lesson_standards.mjs` for the catalog inventory. Run `python scripts/check_native_formal_references.py --skill MATH_CALC_001` with the configured pinned Lean environment to check published reference steps through the real kernel. Reference verification is authoring validation, never learner credit. The historical `MATH_FORMAL_002` example retains unsupported policies and is explicitly marked as historical; do not use it as a current assessment template.

## Foundation-batch assessment contract

The twenty-lesson foundation expansion supplies examples of elementary reasoning, compact reproducible instructional SVGs, structured calculations and public-parameter Cartesian graphs. Each native lesson has twenty distinct templates and question_count twenty, so its review-required capstone appears every time. Keep scenario counts distinct from assessment lengths; if you add another template, deliberately preserve every required question rather than relying on rotation.

Known native Mathematics IDs in theory text are rendered as internal lesson links after escaping the text; unknown IDs remain text. These references are recommendations, not prerequisite locks. Native Studio copies retain the diagrams, mathematical displays and review rules. Portable exports contain resolved questions. Significance/precision checks must assess notation or reasoning explicitly: numeric equality alone cannot distinguish 2.4 from 2.40. Do not use ordinary floating powers to assert real odd roots of negative numbers.

Run the focused foundation source/oracle tests and docs/foundations-roadmap.test.js after content changes. The latter checks one hundred generated assessments per lesson, every-attempt capstones, review boundaries, original-draft restoration, Studio copies and media integrity. See FOUNDATIONS_ROADMAP.md and the release report for the exact scope.

## Derivatives Batch 6 authoring examples

See [Derivatives Batch 6](DERIVATIVES_BATCH6.md) for MATH_CALC_003–006: each has twenty ordinary scenarios plus one formal question and `question_count: 21`. Preserve the conceptual capstone and formal exercise on every attempt. `derivative_definition` requires the exact supported closing rule and cited limit; polynomial, quotient and recursive derivative exercises use their existing allowed rules. A reference certificate validates authored content only and must never become learner credit.

Twelve assessment scenarios use public-parameter Cartesian graphs, while eight static teaching figures illustrate secants, corners, rules, changing area, holes and composition. Preserve excluded inputs even after cancellation; distinguish a formula's derivative from a cancelled function's extension. Run the focused Batch 6 tests and `scripts/check_derivatives_batch6_references.py --output <report.json>` using the pinned formal environment before publication. Preflight-only output is insufficient.
