# Lesson / Experiment Pipeline

Each lesson is a small, repeatable unit that teaches one system-identification
question and leaves behind enough evidence for another engineer to reproduce
it. The pipeline is deliberately a contract first. Shared Python abstractions
should be extracted only after a second lesson proves that a boundary is truly
repeated. The [lesson delivery policy](lesson_delivery_policy.md) assigns the
main explanation and usable evidence to static HTML, treats teaching clips as
support for named questions and Marimo WASM as optional, and requires a local
reference path for computational labs. See the [per-lesson matrix](course/status.md#evidence-media-matrix)
for the actual reading, evidence and media state.

## The lesson contract

Every lesson declares these items before implementation:

| Contract item | Required question |
|---|---|
| Learning question | What should a beginner be able to explain after the lesson? |
| Plant boundary | What system is being identified, and what is outside it? |
| Oracle | Which hidden truth generates the data? |
| Observations | Which signals does the estimator receive? |
| Student model | Which structure and parameters may be fitted? |
| Experiment split | What is used for fitting, and what is held out? |
| Execution policy | Is this an instant calculation, interactive wait, or background job? |
| Learner control | Which one or two safe settings can be changed? |
| Evidence | Which curves, metrics, and diagnostics answer the question? |
| Limits | Which real effects are intentionally absent? |

When Oracle and Student parameter spaces differ, the lesson contract must also
declare which parameters are shared, Oracle-only, or Student-only. Shared
parameters can support recovery claims; unmatched parameters require behavior
and residual evidence instead.

The contract prevents a lesson from silently changing its model, data boundary,
or validation meaning while its narrative remains the same.

Knowledge lessons cite existing lab evidence or clearly labeled conceptual
illustrations; they do not require an independent fitting implementation.
Computational labs use the three visibility levels and minimum evidence package
in [curriculum design D03](lessons/course-design.md#d03). Validation used for
model selection requires a separate untouched split for final claims.

## The shared flow

```text
frame question
  -> freeze versioned configuration
  -> generate Oracle fit and validation observations
  -> fit the declared Student using fit observations only
  -> evaluate baseline and identified models on fit and held-out data
  -> inspect diagnostics and failure conditions
  -> explain the evidence in a lesson
  -> package a notebook, headless command, report, and checks
```

The computation has one source of truth: an importable Python module. The
lesson text explains the reasoning, Marimo and Jupyter are learner-facing
adapters, and the static report records one reproducible run.
None of those presentation surfaces should reimplement simulation or fitting.

## Execution policy

Lessons keep calculation separate from result exploration. A completed
`ExperimentRun` is the boundary between them: fitting produces the run, while
plots, metric selection, joint selection, timelines, and camera controls read
it without silently fitting again.

Choose one execution policy for every learner control that changes the
experiment:

| Policy | Expected wait | Interaction |
|---|---:|---|
| Instant | under about 200 ms | recompute reactively after a control changes |
| Interactive | about 0.2-5 s | show running, success, and failure state in the page |
| Job | longer or remotely scheduled | submit work, return a job ID, show progress and cancellation, then load the completed run |

Controls that only change presentation never submit computation. Changing from
fit to validation, selecting a joint, scrubbing time, or switching a residual
view should reuse the same run. A complex Oracle dataset should normally be
prepared and cached once; fitting jobs consume its reference rather than
regenerating the Oracle on every page interaction.

For a machine leg or whole robot, the eventual flow is:

```text
prepare_experiment(spec) -> immutable dataset reference
submit_fit(dataset reference, Student, estimator) -> job ID
observe job -> progress / failure / completed ExperimentRun
select views from ExperimentRun -> plots, rollouts, metrics, report
```

This is a behavioral contract, not yet a shared queue API. Implement the job
boundary when a concrete actuator or leg lesson first exceeds the interactive
budget, so its retry, cancellation, cache, and worker requirements come from a
real workload.

## Artifact contract

A runnable computational lesson produces:

```text
docs/lessons/<track>/<lesson>.md       narrative and exercises
synthetic/<lesson>.py                  shared numerical implementation
synthetic/<lesson>_config.json         frozen example configuration
reports/<lesson>/report.md             explained run result
reports/<lesson>/report.png            static visual evidence
reports/<lesson>/metrics.json          machine-readable metrics and provenance
tests/test_<lesson>.py                 focused numerical and contract checks
```

The local notebook or command is required for computational labs. A guided
Marimo app and its WASM export are added when the lesson passes the browser
execution gates; they are optional adapters around the shared implementation,
not prerequisites for understanding the lesson.

The report is not a replacement for the lesson. It answers “what happened in
this run”; the lesson answers “why did we run it, how should I read it, and
what should I try next?”.

## Acceptance gates

An experiment can move to the next lesson only when it has:

1. **Numerical evidence:** the declared estimator runs, the fit/validation
   split is preserved, and the result meets the lesson's recovery or behavior
   criteria.
2. **Reproduction evidence:** a clean reproduction command regenerates the
   report in the declared CPU/GPU and backend environment. Record resources and
   runtime budgets; the visual entry point shares implementation/configuration.
   Small CPU lessons retain that path; large lessons need not be CPU-only.
3. **Learning evidence:** a reader can identify the baseline mismatch, explain
   the held-out result, change one exposed setting, and name the main limits.
4. **Scope evidence:** the report states what is absent, so later model
   complexity is introduced because a failure requires it.

## Presentation surfaces

The static lesson is the primary narrative surface; teaching clips support named questions when available. Jupyter
remains an inspectable local surface. The guided Marimo course calls the same
numerical implementation but separates two actions: changing an Initial-model
parameter updates its orange preview immediately, while **Run identification**
explicitly starts fitting and refreshes the blue result. Fit and Validation
remain visible side by side. A WASM export may provide this bounded interaction
on GitHub Pages; heavy fitting, native simulator work, and large datasets stay
on the local path. The static course overview owns the K/L/H learning map;
generated reports remain immutable run artifacts.

## L0 mapping

The L0 set uses the contract as follows:

| Lesson | Question | Evidence |
|---|---|---|
| Orientation | What are plant, input, output, and parameter? | model boundary and units |
| Physics to data | How do `J` and `b` shape motion? | True system versus Initial-model curves |
| Fit to validation | Did fitting recover useful behavior? | parameter recovery and held-out metrics |
| Assumptions | Where does this result stop applying? | omitted effects and next-step delay lesson |

The next model-mismatch lesson should use the same headings and artifact layout.
Only after that comparison should repeated data structures be promoted into a
shared `ExperimentRun` or `run_experiment(...)` API.
