{
  "schema_version": 2,
  "id": "atlas:release:0.1-foundations-candidate",
  "title": "Ephemerent Atlas Foundations field trial",
  "version": "0.1-foundations-candidate",
  "status": "field_trial",
  "license": "CC BY 4.0",
  "language": "en",
  "created_at": "2026-08-12T12:00:00Z",
  "manifest_sha256": "388fe857d481957bbc45f5f9fe9de52a1d1d8154a604c4c835a5e373c955f0f0",
  "media_catalog_id": "atlas:media:vector-meridian:0.1-candidate",
  "media_manifest_sha256": "596e196dfc6f2a2af3f9b05e2bd9b8bf13c45a7eac4750692fdd05d0131db769",
  "lessons": [
    {
      "id": "lesson:quantities-ratios-and-scale:v2",
      "slug": "quantities-ratios-and-scale",
      "concept_id": "concept:quantities-ratios-and-scale",
      "pathway": "mathematics",
      "title": "Quantities, ratios, and scale",
      "deck": "Learn to read a number as a measured relationship, preserve that relationship through conversion, and use units to expose impossible answers.",
      "study_time_status": "awaiting_observation",
      "objectives": [
        {
          "id": "objective:scale:interpret-quantity",
          "description": "Distinguish a bare number from a quantity and identify the dimension and unit carried by a quantity."
        },
        {
          "id": "objective:scale:apply-ratio",
          "description": "Use a unit rate or scale factor to move between representations without reversing the relationship."
        },
        {
          "id": "objective:scale:check-dimensions",
          "description": "Test a calculation for dimensional consistency and repair a mismatched conversion."
        }
      ],
      "prerequisite_ids": [],
      "misconception_ids": [
        "misconception:scale:units-decoration",
        "misconception:scale:reverse-factor"
      ],
      "movements": [
        {
          "id": "movement:scale:numbers-need-nouns",
          "label": "Orient",
          "title": "A number cannot tell you what was measured",
          "blocks": [
            {
              "kind": "prose",
              "id": "block:scale:opening",
              "markdown": "A room is 4.8. A medicine dose is 4.8. A journey lasts 4.8. The numeral is identical, but the claims are not. Each becomes usable only when a unit names the comparison: 4.8 metres, 4.8 milligrams, or 4.8 hours.\n\nA quantity joins a numerical magnitude to a unit. The unit is not a label pasted on after the arithmetic. It determines which comparisons and operations make sense. Length can be added to length. Dividing length by time creates a rate. Adding length to time does not create a physical quantity at all.\n",
              "provenance": [
                {
                  "source_id": "source:openstax-prealgebra-measurement",
                  "locator": "Section 7.5, Convert Units of Measurement; dimensional-analysis conversion factors",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.97,
                  "content_hash": "8026c9453449709ca4ba222a2e3f7bdf8cb4ecb02884954a8b030941fb58e5e0"
                }
              ]
            },
            {
              "kind": "figure",
              "id": "block:scale:quantity-figure",
              "figure_kind": "quantity_line",
              "title": "One length, three numerical descriptions",
              "caption": "Figure 1. The endpoints remain fixed while the numerical magnitude changes with the chosen unit.",
              "alt": "A horizontal line of the same physical length is labeled 2 metres, 200 centimetres, and 2000 millimetres on three aligned scales.",
              "long_description": "Three aligned number lines share the same left and right endpoints. The first runs from 0 to 2 metres, the second from 0 to 200 centimetres, and the third from 0 to 2000 millimetres. This shows that changing units changes the numeral and unit together while preserving the represented length.",
              "units": [
                "m",
                "cm",
                "mm"
              ],
              "data": {
                "centimetres": "200",
                "metres": "2",
                "millimetres": "2000"
              },
              "provenance": [
                {
                  "source_id": "source:openstax-prealgebra-measurement",
                  "locator": "Section 7.5, Convert Units of Measurement; dimensional-analysis conversion factors",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.97,
                  "content_hash": "104b4764a52c38412be8234e9e06f46b7b8f31c7523618fad0380577358e18ed"
                }
              ]
            },
            {
              "kind": "misconception",
              "id": "block:scale:units-misconception",
              "misconception_id": "misconception:scale:units-decoration",
              "claim": "Units can be added after the calculation, once the numerical answer is known.",
              "diagnosis": "This loses the relationships that tell you which operations and conversion factors are valid. It can turn a calculator output into a confidently mislabeled claim.",
              "repair": "Write the unit beside every measured number and carry it through each multiplication or division. Treat cancellation as part of the reasoning.",
              "provenance": [
                {
                  "source_id": "source:openstax-prealgebra-measurement",
                  "locator": "Section 7.5, Convert Units of Measurement; dimensional-analysis conversion factors",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.97,
                  "content_hash": "d90d7b8fe87a46ba4ca11d569a9bd993e9c9d28b75af3f143dd2be93c099caac"
                }
              ]
            },
            {
              "kind": "assessment_set",
              "id": "block:scale:quantity-check",
              "assessment_ids": [
                "assessment:scale:quantity-kind"
              ]
            }
          ]
        },
        {
          "id": "movement:scale:ratios-preserve",
          "label": "Model",
          "title": "A ratio compares; a scale factor transforms",
          "blocks": [
            {
              "kind": "prose",
              "id": "block:scale:ratio-explanation",
              "markdown": "A ratio compares two quantities in a stated order. If a map uses 1 centimetre for every 25 metres in the landscape, the map-to-real ratio is 1 cm to 25 m. Reversing it describes a different conversion.\n\nA unit rate names how much of one quantity corresponds to one unit of another. A scale factor tells you what multiplication carries one representation into the other. The reliable question is not “multiply or divide?” It is “which factor has the units I need after cancellation?”\n",
              "provenance": [
                {
                  "source_id": "source:openstax-prealgebra-ratios",
                  "locator": "Section 5.6, Use Ratios and Rates; ratio order and unit rates",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.97,
                  "content_hash": "b518b98adfdcb247435f4e634be2e3a7da531ca98c86879797315a90593daf6e"
                }
              ]
            },
            {
              "kind": "equation",
              "id": "block:scale:conversion-equation",
              "expression": "real length = model length × (24 real cm / 1 model cm)",
              "spoken": "Real length equals model length multiplied by twenty-four real centimetres per one model centimetre.",
              "explanation": "The model-centimetre unit cancels. The remaining real-centimetre unit names the result and makes the direction of conversion inspectable.",
              "provenance": [
                {
                  "source_id": "source:openstax-prealgebra-ratios",
                  "locator": "Section 5.6, Use Ratios and Rates; ratio order and unit rates",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.97,
                  "content_hash": "ad1fda06099b7dd381f6e007c4e3febf443f302db954ae7203dcd27c59e25a13"
                }
              ]
            },
            {
              "kind": "worked_example",
              "id": "block:scale:model-car",
              "title": "Worked example — recover a model car's full length",
              "problem": "A model car is 18 cm long and built at 1 to 24 scale. Find the full-size length in metres.",
              "steps": [
                "Interpret 1 to 24 as 1 model centimetre corresponding to 24 real centimetres.",
                "Multiply 18 model cm by 24 real cm per 1 model cm. The model-centimetre units cancel.",
                "The product is 432 real cm.",
                "Convert with 1 m per 100 cm, giving 432 cm times 1 m per 100 cm, or 4.32 m.",
                "Check magnitude: a full car should be much longer than the model, so 4.32 m is plausible and 0.75 cm is not."
              ],
              "conclusion": "The full-size car is 4.32 m long. The unit chain records why multiplication, then division by 100, was appropriate.",
              "provenance": [
                {
                  "source_id": "source:openstax-prealgebra-scale",
                  "locator": "Section 9.3, Similar Triangles; similar figures as scale models with equal corresponding-side ratios",
                  "method": "derived",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "23c7541b9a411ac5fe69d6542cc52ef4f60da24ea7beba7e7a53920cfad17089"
                }
              ]
            },
            {
              "kind": "misconception",
              "id": "block:scale:factor-direction",
              "misconception_id": "misconception:scale:reverse-factor",
              "claim": "A 1 to 24 scale always means divide the given number by 24.",
              "diagnosis": "The operation depends on the direction of travel. Dividing a real length by 24 finds its model length; multiplying a model length by 24 finds its real length.",
              "repair": "Write the factor with named units. Choose its orientation so the starting unit cancels and the requested unit remains.",
              "provenance": [
                {
                  "source_id": "source:openstax-prealgebra-scale",
                  "locator": "Section 9.3, Similar Triangles; similar figures as scale models with equal corresponding-side ratios",
                  "method": "derived",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "b6a0a1bf6c9edf0ab7423097dcdf8e9db17e941418047d878e67de259f907650"
                }
              ]
            },
            {
              "kind": "assessment_set",
              "id": "block:scale:model-check",
              "assessment_ids": [
                "assessment:scale:model-length"
              ]
            }
          ]
        },
        {
          "id": "movement:scale:cancel-on-purpose",
          "label": "Formalize",
          "title": "Make the units cancel on purpose",
          "blocks": [
            {
              "kind": "prose",
              "id": "block:scale:dimension-explanation",
              "markdown": "A dimension describes the kind of quantity, such as length, time, or mass. A unit is a chosen standard for expressing that dimension, such as metre, second, or kilogram. Metres and centimetres are different units of the same dimension, so conversion can connect them. Metres and seconds are different dimensions, so no conversion factor can make their sum meaningful.\n\nDimensional consistency is a necessary check, not a proof that an answer is correct. An equation with mismatched dimensions must be wrong. An equation with matching dimensions may still use the wrong numerical factor or model.\n",
              "provenance": [
                {
                  "source_id": "source:openstax-prealgebra-measurement",
                  "locator": "Section 7.5, Convert Units of Measurement; dimensional-analysis conversion factors",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.97,
                  "content_hash": "067380222af897e6151e1d1df73c3a38da1854cc9e81dad84ff903b42647c345"
                }
              ]
            },
            {
              "kind": "worked_example",
              "id": "block:scale:walking-rate",
              "title": "Worked example — convert a walking rate without losing time",
              "problem": "A walker travels 1.5 kilometres in 18 minutes. Express the average rate in metres per second.",
              "steps": [
                "Start with the measured relationship 1.5 km per 18 min, not two disconnected numbers.",
                "Multiply by 1000 m per 1 km. Kilometres cancel, giving 1500 m per 18 min.",
                "Multiply by 1 min per 60 s. Minutes cancel from numerator and denominator in the arranged factor.",
                "Compute 1500 divided by 1080, which is approximately 1.39.",
                "Check scale: ordinary walking is on the order of metres per second, so 1.39 m/s is plausible; 83.3 m/s would signal a missing minute-to-second conversion."
              ],
              "conclusion": "The average rate is about 1.39 m/s. Every conversion factor equals one, so the represented rate stays fixed while its description changes.",
              "provenance": [
                {
                  "source_id": "source:openstax-prealgebra-measurement",
                  "locator": "Section 7.5, Convert Units of Measurement; dimensional-analysis conversion factors",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.97,
                  "content_hash": "4f9dcff6851cb88faf66032639bd1302247310483ce117e18e87d9a20cfd0890"
                }
              ]
            },
            {
              "kind": "table",
              "id": "block:scale:operation-table",
              "caption": "Dimensional questions to ask before accepting an operation",
              "columns": [
                "Operation",
                "Required relationship",
                "Example",
                "Diagnostic"
              ],
              "rows": [
                [
                  "Addition",
                  "Same dimension",
                  "2 m plus 30 cm",
                  "Convert to a common unit first"
                ],
                [
                  "Multiplication",
                  "Any dimensions with a stated interpretation",
                  "3 m times 4 m",
                  "The result is area in square metres"
                ],
                [
                  "Division",
                  "A meaningful comparison or rate",
                  "120 km divided by 2 h",
                  "The result is speed in kilometres per hour"
                ],
                [
                  "Equality",
                  "Same dimensions on both sides",
                  "distance equals speed times time",
                  "Units must reduce to the same dimension"
                ]
              ],
              "provenance": [
                {
                  "source_id": "source:openstax-prealgebra-measurement",
                  "locator": "Section 7.5, Convert Units of Measurement; dimensional-analysis conversion factors",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.97,
                  "content_hash": "2a827511a8b8ee0622d9e5e4c290dfb3a3c7ee8e5d4431fed7727c5cc719fcd1"
                }
              ]
            },
            {
              "kind": "assessment_set",
              "id": "block:scale:dimension-check",
              "assessment_ids": [
                "assessment:scale:unit-order"
              ]
            }
          ]
        },
        {
          "id": "movement:scale:bench",
          "label": "Experiment",
          "title": "Hold the object still while its representation changes",
          "blocks": [
            {
              "kind": "lab_activity",
              "id": "block:scale:lab",
              "simulation_id": "simulation:scale-bench-v2",
              "prediction_prompt": "A drawing is 12 cm long. Before moving the scale control from 1 to 10 toward 1 to 25, predict what happens to the represented real length and name the unit that should remain after the calculation.",
              "observation_prompt": "Compare the double number line, the ruler marks, and the dimensional check. Which marks changed, and which represented relationship stayed invariant?",
              "invariant": "Real length divided by model length equals the selected scale denominator when both lengths use the same unit.",
              "static_account": "At 1 to 10, a 12 cm drawing represents 120 cm or 1.2 m. At 1 to 25, the same 12 cm drawing represents 300 cm or 3 m. The drawing stays 12 cm; only the represented real length changes with the factor.",
              "provenance": [
                {
                  "source_id": "source:openstax-prealgebra-scale",
                  "locator": "Section 9.3, Similar Triangles; similar figures as scale models with equal corresponding-side ratios",
                  "method": "derived",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "59241aadc635cbfafa0fcb34edf287541559f795d1f61617d89fc0d9d8020398"
                }
              ]
            },
            {
              "kind": "callout",
              "id": "block:scale:lab-habit",
              "label": "Laboratory habit",
              "body": "Predict in words and units before calculating. If the displayed value moves opposite your prediction, inspect the direction of the factor before touching the arithmetic.",
              "provenance": [
                {
                  "source_id": "source:atlas-method-v2",
                  "locator": "Authoring contract v2, prediction-first laboratory pattern",
                  "method": "human",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 1,
                  "content_hash": "caf087798cef22308456c912de9f80f007b0a21c182459ecd311e7da61a41b6a"
                }
              ]
            }
          ]
        },
        {
          "id": "movement:scale:transfer",
          "label": "Transfer",
          "title": "Audit a blueprint before trusting the answer",
          "blocks": [
            {
              "kind": "prose",
              "id": "block:scale:transfer-setup",
              "markdown": "Scale reasoning matters when the representation and the built object cannot be compared directly. A blueprint, dosage chart, map, recipe, or scientific model compresses one relationship into another. The safety habit is the same: state what each unit refers to, orient the factor, cancel visibly, and test the magnitude.\n\nThe final task asks you to combine all three objectives. It is deliberately a different form from the immediate checks, so a correct response can contribute transfer evidence rather than repeating the same click.\n",
              "provenance": [
                {
                  "source_id": "source:openstax-prealgebra-scale",
                  "locator": "Section 9.3, Similar Triangles; similar figures as scale models with equal corresponding-side ratios",
                  "method": "derived",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "2596c44e4477ed8ef657354260e6c5dc17001bd8ce9cfb56f1057cea118b6e9f"
                }
              ]
            },
            {
              "kind": "assessment_set",
              "id": "block:scale:transfer-check",
              "assessment_ids": [
                "assessment:scale:transfer"
              ]
            }
          ]
        }
      ],
      "assessment_ids": [
        "assessment:scale:quantity-kind",
        "assessment:scale:model-length",
        "assessment:scale:unit-order",
        "assessment:scale:transfer"
      ],
      "simulation_id": "simulation:scale-bench-v2",
      "source_ids": [
        "source:openstax-prealgebra-ratios",
        "source:openstax-prealgebra-measurement",
        "source:openstax-prealgebra-scale"
      ],
      "review_ids": []
    },
    {
      "id": "lesson:procedures-and-state:v2",
      "slug": "procedures-and-state",
      "concept_id": "concept:procedures-and-state",
      "pathway": "computing",
      "title": "Procedures and state",
      "deck": "Read a small program as a sequence of inspectable state changes, predict a loop before running it, and separate rebinding from mutation.",
      "study_time_status": "awaiting_observation",
      "objectives": [
        {
          "id": "objective:procedures:trace-state",
          "description": "Record the relevant state before and after each assignment in a short Python procedure."
        },
        {
          "id": "objective:procedures:predict-loop",
          "description": "Use a loop invariant to predict the result of an accumulation procedure."
        },
        {
          "id": "objective:procedures:distinguish-mutation",
          "description": "Distinguish rebinding a local name from mutating an object shared with the caller."
        }
      ],
      "prerequisite_ids": [],
      "misconception_ids": [
        "misconception:procedures:equals",
        "misconception:procedures:loop-at-once",
        "misconception:procedures:assignment-copies"
      ],
      "movements": [
        {
          "id": "movement:procedures:slow-time",
          "label": "Orient",
          "title": "Debugging begins by slowing time down",
          "blocks": [
            {
              "kind": "prose",
              "id": "block:procedures:opening",
              "markdown": "A program does not execute as a paragraph. It advances through instructions, and each instruction sees a particular state. When the result surprises you, the useful question is not “What does this code look like it means?” but “What values were available immediately before this instruction, and what changed immediately after it?”\n\nFor this lesson, state means the bindings and object contents relevant to the next step. A trace is a compact record of those states. It turns execution from an invisible event into a sequence you can inspect, challenge, and reproduce.\n",
              "provenance": [
                {
                  "source_id": "source:python-reference-simple",
                  "locator": "Section 7.2, Assignment statements; assignment evaluates an expression then assigns the result to targets",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "3cf1e55f58fb67ab67506b8fa167cdab9b0128e51df21ce631f5227f1506aef9"
                }
              ]
            },
            {
              "kind": "code",
              "id": "block:procedures:first-code",
              "language": "python",
              "code": "width = 4\nwidth = width + 3\narea = width * 2\n",
              "caption": "Three statements, read in execution order.",
              "provenance": [
                {
                  "source_id": "source:python-reference-simple",
                  "locator": "Section 7.2, Assignment statements; assignment evaluates an expression then assigns the result to targets",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "cebec55059c13b35a3d1cc30d8d29e60b252311356beaeb1abf0c36b641e9cfe"
                }
              ]
            },
            {
              "kind": "figure",
              "id": "block:procedures:first-trace",
              "figure_kind": "state_trace",
              "title": "State changes one instruction at a time",
              "caption": "Figure 1. Each row records the state after one statement; the highlighted cell is the binding changed by that statement.",
              "alt": "A three-row trace table shows width becoming 4, then 7, and area becoming 14 while width remains 7.",
              "long_description": "Row one executes width equals 4 and records width as 4. Row two evaluates the old width plus 3, then rebinds width to 7. Row three evaluates width times 2 and binds area to 14; width remains 7. The rows make evaluation order explicit.",
              "units": [
                "execution step",
                "integer value"
              ],
              "data": {
                "columns": "statement|width|area",
                "row_1": "width = 4|4|not bound",
                "row_2": "width = width + 3|7|not bound",
                "row_3": "area = width * 2|7|14"
              },
              "provenance": [
                {
                  "source_id": "source:python-reference-simple",
                  "locator": "Section 7.2, Assignment statements; assignment evaluates an expression then assigns the result to targets",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "2fa1865df6c57958a6c7eb322a180ef9f89482f878f950e4c02344efef9f3528"
                }
              ]
            },
            {
              "kind": "misconception",
              "id": "block:procedures:equals-misconception",
              "misconception_id": "misconception:procedures:equals",
              "claim": "The statement width = width + 3 says that a mathematical equality is always true.",
              "diagnosis": "In an assignment statement, the expression on the right is evaluated using the current state; only then is the target name on the left rebound. It describes a transition, not a timeless equation.",
              "repair": "Read it aloud as “evaluate old width plus three, then bind width to that result.” Record the before and after values on separate trace rows.",
              "provenance": [
                {
                  "source_id": "source:python-reference-simple",
                  "locator": "Section 7.2, Assignment statements; assignment evaluates an expression then assigns the result to targets",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "0eea67baeb8d5b097fe64abb9c648b30d5fd07ac45b2bcb41f4bae2c7c338b7c"
                }
              ]
            },
            {
              "kind": "assessment_set",
              "id": "block:procedures:first-check",
              "assessment_ids": [
                "assessment:procedures:assignment-trace"
              ]
            }
          ]
        },
        {
          "id": "movement:procedures:loops",
          "label": "Model",
          "title": "A loop is repeated small work, not one mysterious leap",
          "blocks": [
            {
              "kind": "prose",
              "id": "block:procedures:loop-explanation",
              "markdown": "Python's for statement visits items from an iterable in order. On each visit, the loop target is bound to the next item and the body runs. You can therefore explain a loop by repeating the same local questions: What item is current? What is the accumulator before the body? What is it afterward?\n\nAn invariant is a statement that remains true at a chosen point in every iteration. For an accumulating sum, a useful invariant is: after the body finishes, total equals the sum of every item visited so far. The invariant is stronger than a guessed final answer because it explains every intermediate row.\n",
              "provenance": [
                {
                  "source_id": "source:python-tutorial-control",
                  "locator": "Section 4.2, for Statements; iteration over sequence items in order",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.97,
                  "content_hash": "7fedb32750089ff41b4549785261672491820482d4ef53cacb5d64d67c156769"
                }
              ]
            },
            {
              "kind": "code",
              "id": "block:procedures:sum-code",
              "language": "python",
              "code": "total = 0\nfor item in [2, 4, 6]:\n    total = total + item\n",
              "caption": "A three-iteration accumulation procedure.",
              "provenance": [
                {
                  "source_id": "source:python-tutorial-control",
                  "locator": "Section 4.2, for Statements; iteration over sequence items in order",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.97,
                  "content_hash": "b4f661f6ac1568fbe15a3738145e5f2ee0856ab9da4e98228789c47eb6a3c160"
                }
              ]
            },
            {
              "kind": "worked_example",
              "id": "block:procedures:sum-example",
              "title": "Worked example — trace an accumulator",
              "problem": "Explain the final value of total without running the code.",
              "steps": [
                "Before the loop, total is 0 and no item has been visited. The invariant holds because the sum of no visited items is 0.",
                "First visit: item is 2. Evaluate old total plus item, which is 0 plus 2, then rebind total to 2.",
                "Second visit: item is 4. Evaluate 2 plus 4, then rebind total to 6.",
                "Third visit: item is 6. Evaluate 6 plus 6, then rebind total to 12.",
                "The iterable is exhausted. The invariant says total is the sum of 2, 4, and 6, so the final value is 12."
              ],
              "conclusion": "The trace 0, 2, 6, 12 explains both the final answer and the work performed at each step.",
              "provenance": [
                {
                  "source_id": "source:python-tutorial-control",
                  "locator": "Section 4.2, for Statements; iteration over sequence items in order",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.97,
                  "content_hash": "debab5a491370fe33eec82abdbb82d568572a35b0eae434bc66eb3c264fedb52"
                }
              ]
            },
            {
              "kind": "misconception",
              "id": "block:procedures:loop-misconception",
              "misconception_id": "misconception:procedures:loop-at-once",
              "claim": "The loop somehow sees the whole list and jumps directly to 12.",
              "diagnosis": "That story cannot predict intermediate states, explain an early exit, or locate the iteration where a defect appears.",
              "repair": "Expand the loop into one row per visited item. Check the invariant after every body execution.",
              "provenance": [
                {
                  "source_id": "source:python-tutorial-control",
                  "locator": "Section 4.2, for Statements; iteration over sequence items in order",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.97,
                  "content_hash": "a15e1aae24d467d2ed3012e81c42183cdf1a1d271cac7f233d98864a3c880894"
                }
              ]
            },
            {
              "kind": "assessment_set",
              "id": "block:procedures:loop-check",
              "assessment_ids": [
                "assessment:procedures:loop-invariant"
              ]
            }
          ]
        },
        {
          "id": "movement:procedures:function-boundary",
          "label": "Formalize",
          "title": "Function calls create a boundary, not a magical box",
          "blocks": [
            {
              "kind": "prose",
              "id": "block:procedures:function-explanation",
              "markdown": "A function definition gives a procedure a name and parameters. Calling the function binds its parameter names to the supplied argument objects, executes the body, and returns a result. The local names belong to the call, but a mutable argument object may still be shared with the caller.\n\nThis distinction matters. Rebinding a local parameter changes which object that local name refers to. Mutating a shared list changes the contents of the object itself, so the caller can observe the change. A state trace should record both bindings and the contents of mutable objects when either can change.\n",
              "provenance": [
                {
                  "source_id": "source:python-tutorial-control",
                  "locator": "Section 4.8, Defining Functions; local symbol table and arguments as object references",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "c90cb7bf2ebb4770fb53b415de6074a53384e450872a76e493f71493ccdb9b35"
                }
              ]
            },
            {
              "kind": "code",
              "id": "block:procedures:function-code",
              "language": "python",
              "code": "def add_tax(prices):\n    taxed = []\n    for price in prices:\n        taxed.append(price * 1.05)\n    return taxed\n\noriginal = [10, 20]\nresult = add_tax(original)\n",
              "caption": "The function builds a new list; it does not append to the argument list.",
              "provenance": [
                {
                  "source_id": "source:python-tutorial-control",
                  "locator": "Section 4.8, Defining Functions; local symbol table and arguments as object references",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "6483eec3a12c4e26b5feea89480178b1c2cbcc25dde6a9e5b7a8099a1f8bc95e"
                }
              ]
            },
            {
              "kind": "worked_example",
              "id": "block:procedures:function-example",
              "title": "Worked example — prove that the input list stays unchanged",
              "problem": "Trace the call add_tax(original) and explain why original remains [10, 20].",
              "steps": [
                "At the call boundary, the local name prices refers to the same list object as original.",
                "The statement taxed = [] creates a new empty list and binds only the local name taxed to it.",
                "Each loop iteration appends a computed value to the new taxed list. No statement targets prices or the original list object.",
                "The function returns the new list [10.5, 21.0], which is bound to result by the caller.",
                "The object referred to by original was read but never mutated, so its contents remain [10, 20]."
              ],
              "conclusion": "Shared access does not imply mutation. The operations performed on each object determine what the caller observes.",
              "provenance": [
                {
                  "source_id": "source:python-tutorial-control",
                  "locator": "Section 4.8, Defining Functions; local symbol table and arguments as object references",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "0cb0b593406f8f7a63bf5448b9ddf99d7a61df9d5698e51f19f40010131dff02"
                }
              ]
            },
            {
              "kind": "misconception",
              "id": "block:procedures:copy-misconception",
              "misconception_id": "misconception:procedures:assignment-copies",
              "claim": "Assigning prices = original automatically creates an independent copy of the list.",
              "diagnosis": "Assignment binds another name to the same object; it does not by itself copy that object's contents.",
              "repair": "Draw names and objects separately. Two arrows may point to one list. Create an explicit copy or a new result list when independence is required.",
              "provenance": [
                {
                  "source_id": "source:python-tutorial-control",
                  "locator": "Section 4.8, Defining Functions; local symbol table and arguments as object references",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "f8a5db48a0425855557a87a47889fa12cae7303faf89f5e2869794dee4a5d95d"
                }
              ]
            },
            {
              "kind": "assessment_set",
              "id": "block:procedures:mutation-check",
              "assessment_ids": [
                "assessment:procedures:mutation-selection"
              ]
            }
          ]
        },
        {
          "id": "movement:procedures:trace-bench",
          "label": "Experiment",
          "title": "Predict the next row before revealing it",
          "blocks": [
            {
              "kind": "lab_activity",
              "id": "block:procedures:lab",
              "simulation_id": "simulation:procedure-trace-v2",
              "prediction_prompt": "With loop length 3 and input step 2, predict the current item and total after the second iteration. State the invariant in your own words before revealing the row.",
              "observation_prompt": "Change the input step while holding loop length fixed. Which cells change, and why does the invariant remain true?",
              "invariant": "After each body execution, total equals the sum of every item visited so far.",
              "static_account": "With items 2, 4, and 6, the before and after totals are 0 to 2, 2 to 6, and 6 to 12. Increasing the input step changes each item and total, but each after-total still equals the visited sum.",
              "provenance": [
                {
                  "source_id": "source:python-tutorial-control",
                  "locator": "Section 4.2, for Statements; iteration over sequence items in order",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.97,
                  "content_hash": "ba882cfedaf2cc642fb3a26d93af3eeed62fa959e19a49760cb08d1b21eb4cd2"
                }
              ]
            },
            {
              "kind": "table",
              "id": "block:procedures:static-table",
              "caption": "Static trace for loop length 3 and input step 2",
              "columns": [
                "Iteration",
                "Current item",
                "Total before",
                "Total after",
                "Invariant check"
              ],
              "rows": [
                [
                  "1",
                  "2",
                  "0",
                  "2",
                  "2 equals sum of visited items"
                ],
                [
                  "2",
                  "4",
                  "2",
                  "6",
                  "6 equals sum of visited items"
                ],
                [
                  "3",
                  "6",
                  "6",
                  "12",
                  "12 equals sum of visited items"
                ]
              ],
              "provenance": [
                {
                  "source_id": "source:python-tutorial-control",
                  "locator": "Section 4.2, for Statements; iteration over sequence items in order",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.97,
                  "content_hash": "ccdbf96ce4becf76cee55cd2de671158600218827dcda18f665f007e6d558bd0"
                }
              ]
            }
          ]
        },
        {
          "id": "movement:procedures:transfer",
          "label": "Transfer",
          "title": "Trace a conversion function without executing it",
          "blocks": [
            {
              "kind": "prose",
              "id": "block:procedures:transfer-setup",
              "markdown": "A trace is most useful when it predicts code you have not run. In the transfer task, a function converts temperatures into a new list. You must record the loop's state and preserve the distinction between the input object and the result object.\n\nThe assessment uses a structured state trace rather than another multiple-choice prompt. That change of form matters: demonstrated evidence should show that the same idea survives a new representation.\n",
              "provenance": [
                {
                  "source_id": "source:python-tutorial-control",
                  "locator": "Section 4.8, Defining Functions; local symbol table and arguments as object references",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "2ad31f0cc98d26fd6ca4e11a1876ea2a7402919920a5bca1a2fd67ba9657cb4f"
                }
              ]
            },
            {
              "kind": "assessment_set",
              "id": "block:procedures:transfer-check",
              "assessment_ids": [
                "assessment:procedures:transfer-trace"
              ]
            }
          ]
        }
      ],
      "assessment_ids": [
        "assessment:procedures:assignment-trace",
        "assessment:procedures:loop-invariant",
        "assessment:procedures:mutation-selection",
        "assessment:procedures:transfer-trace"
      ],
      "simulation_id": "simulation:procedure-trace-v2",
      "source_ids": [
        "source:python-tutorial-control",
        "source:python-reference-simple"
      ],
      "review_ids": []
    },
    {
      "id": "lesson:measurement-and-uncertainty:v2",
      "slug": "measurement-and-uncertainty",
      "concept_id": "concept:measurement-and-uncertainty",
      "pathway": "physics",
      "title": "Measurement and uncertainty",
      "deck": "Turn an instrument reading into an honest claim by naming the method, separating scatter from offset, and reporting only the precision the evidence supports.",
      "study_time_status": "awaiting_observation",
      "objectives": [
        {
          "id": "objective:measurement:report-result",
          "description": "Report a measured value with a unit, an uncertainty, and the convention used to estimate that uncertainty."
        },
        {
          "id": "objective:measurement:diagnose-error",
          "description": "Distinguish limited resolution and trial-to-trial scatter from a shared offset that repetition alone cannot reveal."
        },
        {
          "id": "objective:measurement:compare-precision-accuracy",
          "description": "Use repeated values and a reference value to compare precision and accuracy without treating them as synonyms."
        }
      ],
      "prerequisite_ids": [],
      "misconception_ids": [
        "misconception:measurement:display-certainty",
        "misconception:measurement:repeat-removes-bias",
        "misconception:measurement:precision-is-accuracy"
      ],
      "movements": [
        {
          "id": "movement:measurement:procedure",
          "label": "Orient",
          "title": "A reading becomes evidence only when the procedure travels with it",
          "blocks": [
            {
              "kind": "prose",
              "id": "block:measurement:opening",
              "markdown": "“The period is 2.4” is not yet a physical result. Which event started the timer? Which event stopped it? Was one swing or ten swings timed? What unit did the display use? What was the instrument's smallest step? Could another observer repeat the same procedure?\n\nMeasurement connects a phenomenon to a comparison standard through an operation. The reported number is therefore a claim about the phenomenon, the instrument, and the method together. Leaving out the unit or uncertainty does not make the measurement cleaner; it hides part of what is known.\n",
              "provenance": [
                {
                  "source_id": "source:openstax-physics-units",
                  "locator": "Section 1.2, Units and Standards; physical quantities expressed using standardized units",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.97,
                  "content_hash": "6a4cf575500751443e5dd715abe870c59a99588906df909e2cfff0155e530678"
                }
              ]
            },
            {
              "kind": "equation",
              "id": "block:measurement:report-equation",
              "expression": "x = x̂ ± u, with unit and uncertainty convention stated",
              "spoken": "The measured quantity x is reported as the best estimate x hat, plus or minus uncertainty u, together with a unit and the rule used to obtain u.",
              "explanation": "The interval communicates the measurement's supported precision. The uncertainty estimate is not universal; its meaning depends on the stated method and confidence convention.",
              "provenance": [
                {
                  "source_id": "source:openstax-physics-uncertainty",
                  "locator": "Section 1.6, Accuracy, Precision, Uncertainty, and Discrepancy; reporting a measured value as A plus or minus delta A",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "396077a79b696df9eb541a10a95e001b388fe097e6de913b2aa46e3d36a71a65"
                }
              ]
            },
            {
              "kind": "misconception",
              "id": "block:measurement:display-misconception",
              "misconception_id": "misconception:measurement:display-certainty",
              "claim": "If a calculator or digital instrument prints six decimal places, all six digits are established by the measurement.",
              "diagnosis": "Display digits can exceed the resolution and stability of the measuring process. Arithmetic can propagate digits without creating information.",
              "repair": "Identify the instrument resolution and uncertainty before rounding the final report. Keep guard digits during calculation, then report precision supported by the inputs.",
              "provenance": [
                {
                  "source_id": "source:openstax-physics-uncertainty",
                  "locator": "Section 1.6, Accuracy, Precision, Uncertainty, and Discrepancy; reporting a measured value as A plus or minus delta A",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "04b235a977362481e0fb4b5be35d052b27e91d5967063bdf6117e76dce95b43e"
                }
              ]
            },
            {
              "kind": "assessment_set",
              "id": "block:measurement:report-check",
              "assessment_ids": [
                "assessment:measurement:report"
              ]
            }
          ]
        },
        {
          "id": "movement:measurement:scatter-offset",
          "label": "Model",
          "title": "Repetition shows scatter; a reference exposes offset",
          "blocks": [
            {
              "kind": "prose",
              "id": "block:measurement:scatter-explanation",
              "markdown": "Repeated trials can reveal how much a procedure varies. A tight cluster is precise; a broad cluster is less precise. But the cluster's centre may still be displaced from an accepted reference. That displacement is a discrepancy and can arise from a shared calibration offset or another systematic effect.\n\nRepetition alone cannot diagnose every shared offset because the same shift can affect every trial. To test accuracy, compare with a defensible reference, reverse the method when possible, or use an independent instrument or calibration procedure.\n",
              "provenance": [
                {
                  "source_id": "source:openstax-physics-uncertainty",
                  "locator": "Section 1.6, Accuracy, Precision, Uncertainty, and Discrepancy; reporting a measured value as A plus or minus delta A",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "e066740d4d785a5ca9a856cd12f802da73ac1ebc95664065c91389b04f620858"
                }
              ]
            },
            {
              "kind": "figure",
              "id": "block:measurement:cluster-figure",
              "figure_kind": "measurement_clusters",
              "title": "Precision and accuracy answer different questions",
              "caption": "Figure 1. Horizontal position shows measured length; each mark is one trial. The vertical rule is the accepted reference.",
              "alt": "Three horizontal dot plots show a tight cluster around the reference, a broad cluster around the reference, and a tight cluster shifted to the right of the reference.",
              "long_description": "Plot A has closely packed measurements centred on 10.0 centimetres and is both precise and accurate. Plot B has measurements spread from 9.6 to 10.4 centimetres but centred near 10.0, so it is less precise while its centre is accurate. Plot C has closely packed measurements near 10.5 centimetres, so it is precise but discrepant from the 10.0-centimetre reference.",
              "units": [
                "cm",
                "trial"
              ],
              "data": {
                "accurate_precise": "9.98|10.00|10.01|10.02|9.99",
                "accurate_scattered": "9.60|9.85|10.00|10.15|10.40",
                "precise_offset": "10.48|10.50|10.51|10.52|10.49",
                "reference_cm": "10.0"
              },
              "provenance": [
                {
                  "source_id": "source:openstax-physics-uncertainty",
                  "locator": "Section 1.6, Accuracy, Precision, Uncertainty, and Discrepancy; reporting a measured value as A plus or minus delta A",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "4330f5d1b334dfd8e6363fe931239e78c61199745a50be172ab379f059bd19d0"
                }
              ]
            },
            {
              "kind": "table",
              "id": "block:measurement:diagnostic-table",
              "caption": "What each diagnostic can and cannot reveal",
              "columns": [
                "Diagnostic",
                "Evidence it provides",
                "Blind spot"
              ],
              "rows": [
                [
                  "Read the smallest display or scale step",
                  "Instrument resolution",
                  "Does not expose calibration offset"
                ],
                [
                  "Repeat the same procedure",
                  "Trial-to-trial scatter and repeatability",
                  "A shared shift may remain hidden"
                ],
                [
                  "Measure a trusted reference",
                  "Discrepancy from the reference",
                  "Reference itself has uncertainty"
                ],
                [
                  "Change observer or method",
                  "Sensitivity to procedural choices",
                  "New method may add different effects"
                ]
              ],
              "provenance": [
                {
                  "source_id": "source:openstax-physics-uncertainty",
                  "locator": "Section 1.6, Accuracy, Precision, Uncertainty, and Discrepancy; reporting a measured value as A plus or minus delta A",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "832baf978cace5e3e0418dbbcea73ae9318319200553652707a9be6fa074d2b0"
                }
              ]
            },
            {
              "kind": "misconception",
              "id": "block:measurement:repeat-misconception",
              "misconception_id": "misconception:measurement:repeat-removes-bias",
              "claim": "Taking more measurements automatically removes every measurement error.",
              "diagnosis": "More trials can stabilize an estimate of a centre and characterize scatter, but the same offset can shift every trial in the same direction.",
              "repair": "Pair repetition with calibration or an independent reference. Report the remaining limitation instead of averaging it out in words.",
              "provenance": [
                {
                  "source_id": "source:openstax-physics-uncertainty",
                  "locator": "Section 1.6, Accuracy, Precision, Uncertainty, and Discrepancy; reporting a measured value as A plus or minus delta A",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "8095b249b9f34a3c8bbd0af45c95b8c662601cb7dbf9b79d5347b00542d96f44"
                }
              ]
            },
            {
              "kind": "assessment_set",
              "id": "block:measurement:error-check",
              "assessment_ids": [
                "assessment:measurement:error-sources",
                "assessment:measurement:cluster"
              ]
            }
          ]
        },
        {
          "id": "movement:measurement:compute",
          "label": "Formalize",
          "title": "Compute a summary without pretending it is the whole experiment",
          "blocks": [
            {
              "kind": "worked_example",
              "id": "block:measurement:timing-example",
              "title": "Worked example — report repeated timing trials",
              "problem": "Five period measurements are 2.38 s, 2.42 s, 2.39 s, 2.41 s, and 2.40 s. Use the mean as the best estimate and half the range as a simple uncertainty convention.",
              "steps": [
                "Add the five values to obtain 12.00 s, then divide by 5. The mean is 2.400 s.",
                "Find the range: 2.42 s minus 2.38 s equals 0.04 s.",
                "Half the range is 0.02 s. State explicitly that half-range, not a standard deviation or confidence interval, is the chosen convention.",
                "Round the best estimate to the same decimal place as the uncertainty.",
                "Report 2.40 plus or minus 0.02 s using the stated repeated-timing procedure."
              ],
              "conclusion": "The result is 2.40 ± 0.02 s, where 0.02 s is half the observed range. It summarizes repeatability but does not rule out a shared timer offset.",
              "provenance": [
                {
                  "source_id": "source:openstax-physics-uncertainty",
                  "locator": "Section 1.6, Accuracy, Precision, Uncertainty, and Discrepancy; reporting a measured value as A plus or minus delta A",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "5bfcf1f27ea635fb068d4ff1c02b7d445e915a515655adae5557ec2fd33523e7"
                }
              ]
            },
            {
              "kind": "equation",
              "id": "block:measurement:percent-equation",
              "expression": "percent uncertainty = (u / |x̂|) × 100 percent",
              "spoken": "Percent uncertainty equals the uncertainty divided by the absolute value of the best estimate, multiplied by one hundred percent.",
              "explanation": "The numerator and denominator must use the same unit, so the ratio is dimensionless. Percent uncertainty compares uncertainty relative to the measured magnitude.",
              "provenance": [
                {
                  "source_id": "source:openstax-physics-uncertainty",
                  "locator": "Section 1.6, Accuracy, Precision, Uncertainty, and Discrepancy; reporting a measured value as A plus or minus delta A",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "b8c606d911e943f9382a7612d12aec57ad33f71abde18312fd0988f8b5e2134f"
                }
              ]
            },
            {
              "kind": "worked_example",
              "id": "block:measurement:percent-example",
              "title": "Worked example — decide whether a stopwatch can separate two results",
              "problem": "A stopwatch has stated uncertainty ±0.05 s. Two sprint times are 12.04 s and 12.07 s. Can the display alone support the claim that the second runner was 0.03 s slower?",
              "steps": [
                "Compute the displayed difference: 12.07 s minus 12.04 s equals 0.03 s.",
                "Compare that difference with the stated uncertainty scale of 0.05 s for each timing.",
                "The difference is smaller than even one timing's stated uncertainty, so the intervals overlap substantially.",
                "Extra decimal places in the display do not reduce the stated uncertainty."
              ],
              "conclusion": "These readings do not support a confident ordering at a 0.03 s separation with this uncertainty. The limitation belongs in the conclusion.",
              "provenance": [
                {
                  "source_id": "source:openstax-physics-uncertainty",
                  "locator": "Section 1.6, Check Your Understanding 1.8; comparing close sprint times with a stopwatch uncertainty",
                  "method": "derived",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.98,
                  "content_hash": "c5becd8c28b4376da55719d2105932453afd16cfa0a48a8624cefd5cfe5967a8"
                }
              ]
            },
            {
              "kind": "misconception",
              "id": "block:measurement:precision-misconception",
              "misconception_id": "misconception:measurement:precision-is-accuracy",
              "claim": "Precise measurements must be accurate because they agree closely with each other.",
              "diagnosis": "Agreement among trials measures repeatability. A shared offset can create a tight cluster far from the accepted reference.",
              "repair": "Use the spread to discuss precision and the discrepancy from a reference to discuss accuracy. Name both when the reference is available.",
              "provenance": [
                {
                  "source_id": "source:openstax-physics-uncertainty",
                  "locator": "Section 1.6, Accuracy, Precision, Uncertainty, and Discrepancy; reporting a measured value as A plus or minus delta A",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "3ee5b635c1dccf64f882e933f44ecd36f50f9896d2a739f5387609fd3e475cba"
                }
              ]
            }
          ]
        },
        {
          "id": "movement:measurement:bench",
          "label": "Experiment",
          "title": "Hide an offset inside a convincing cluster",
          "blocks": [
            {
              "kind": "lab_activity",
              "id": "block:measurement:lab",
              "simulation_id": "simulation:measurement-bench-v2",
              "prediction_prompt": "Hold scatter fixed and increase the hidden zero offset. Predict what happens to the cluster width, cluster centre, precision, and accuracy before revealing the reference.",
              "observation_prompt": "Run repeated trials, then reveal the reference. Which feature diagnosed scatter and which comparison exposed the shared offset?",
              "invariant": "Repetition changes the estimate of the cluster and its scatter; a constant zero offset shifts every trial and is only identifiable against a reference or independent calibration.",
              "static_account": "For a true value of 10.0 cm, low scatter with zero offset produces a tight cluster near 10.0 cm. The same scatter with a +0.5 cm offset produces an equally tight cluster near 10.5 cm. Precision is similar; accuracy is not.",
              "provenance": [
                {
                  "source_id": "source:openstax-physics-uncertainty",
                  "locator": "Section 1.6, Accuracy, Precision, Uncertainty, and Discrepancy; reporting a measured value as A plus or minus delta A",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "c6843475dea5360f295a2c0dd56b50f905bfebd4e4a29ec0f52cb631ac08dc47"
                }
              ]
            },
            {
              "kind": "callout",
              "id": "block:measurement:lab-habit",
              "label": "Laboratory habit",
              "body": "Never use “human error” as a terminal explanation. Name the operation, instrument, observer choice, environmental variable, or model assumption that could change the result, then propose a diagnostic.",
              "provenance": [
                {
                  "source_id": "source:atlas-method-v2",
                  "locator": "Authoring contract v2, diagnostic laboratory feedback",
                  "method": "human",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 1,
                  "content_hash": "41c60c3f338eb1780e0af40f0d76fa8615145c0020dc7d36e7d2903682df78a3"
                }
              ]
            }
          ]
        },
        {
          "id": "movement:measurement:transfer",
          "label": "Transfer",
          "title": "Design a comparison that can fail honestly",
          "blocks": [
            {
              "kind": "prose",
              "id": "block:measurement:transfer-setup",
              "markdown": "The transfer task asks you to compare two pendulum periods. A useful design identifies the start and stop events, times multiple cycles to reduce reaction-time influence, repeats trials, states a summary and uncertainty convention, and checks for at least one systematic effect such as timer calibration or inconsistent release angle.\n\nA strong conclusion is conditional. It says what the measurements distinguish at the achieved uncertainty and what remains unresolved. That is not weakness; it is the boundary that makes the claim trustworthy.\n",
              "provenance": [
                {
                  "source_id": "source:openstax-physics-uncertainty",
                  "locator": "Section 1.6, Accuracy, Precision, Uncertainty, and Discrepancy; reporting a measured value as A plus or minus delta A",
                  "method": "ai_drafted",
                  "recorded_at": "2026-08-12T12:00:00Z",
                  "confidence": 0.96,
                  "content_hash": "5b6fbdf50103980ed8f1fff819c822f7a0fdfd73961e6b4b9ec2481c64951e82"
                }
              ]
            },
            {
              "kind": "assessment_set",
              "id": "block:measurement:transfer-check",
              "assessment_ids": [
                "assessment:measurement:transfer"
              ]
            }
          ]
        }
      ],
      "assessment_ids": [
        "assessment:measurement:report",
        "assessment:measurement:error-sources",
        "assessment:measurement:cluster",
        "assessment:measurement:transfer"
      ],
      "simulation_id": "simulation:measurement-bench-v2",
      "source_ids": [
        "source:openstax-physics-units",
        "source:openstax-physics-uncertainty"
      ],
      "review_ids": []
    }
  ],
  "assessments": [
    {
      "id": "assessment:scale:quantity-kind",
      "lesson_id": "lesson:quantities-ratios-and-scale:v2",
      "objective_ids": [
        "objective:scale:interpret-quantity"
      ],
      "evidence_kind": "retrieval",
      "item_form": "multiple_selection",
      "prompt": "Which expressions are quantities with enough information to interpret a measurement? Select every valid expression.",
      "response_specification": "Select all expressions that pair a magnitude with a unit.",
      "options": [
        {
          "id": "a",
          "label": "12"
        },
        {
          "id": "b",
          "label": "12 cm"
        },
        {
          "id": "c",
          "label": "3.2 seconds"
        },
        {
          "id": "d",
          "label": "fast"
        }
      ],
      "hints": [
        "Look for both a numerical magnitude and a named comparison standard."
      ],
      "answer": {
        "kind": "multiple_selection",
        "option_ids": [
          "b",
          "c"
        ]
      },
      "feedback_correct": "Yes. Both selections pair a magnitude with a unit; the other choices do not state a measurable quantity completely.",
      "feedback_incorrect": "A measurable quantity needs both a numerical magnitude and a unit. Check whether each selected expression supplies both.",
      "misconception_ids": [
        "misconception:scale:units-decoration"
      ],
      "provenance": [
        {
          "source_id": "source:openstax-prealgebra-measurement",
          "locator": "Section 7.5, Convert Units of Measurement; dimensional-analysis conversion factors",
          "method": "ai_drafted",
          "recorded_at": "2026-08-12T12:00:00Z",
          "confidence": 0.97,
          "content_hash": "7b3740756e5e99e263d30caf800aac0cb784edd751fa316d11851de5387b2509"
        }
      ],
      "evaluator_version": "atlas-eval-v2"
    },
    {
      "id": "assessment:scale:model-length",
      "lesson_id": "lesson:quantities-ratios-and-scale:v2",
      "objective_ids": [
        "objective:scale:apply-ratio"
      ],
      "evidence_kind": "retrieval",
      "item_form": "numeric",
      "prompt": "A 7.5 cm drawing uses a 1 to 40 scale. What real length does it represent in metres?",
      "response_specification": "Enter a number and a unit.",
      "options": [],
      "hints": [
        "Multiply by the real-to-model factor, then convert centimetres to metres."
      ],
      "answer": {
        "kind": "numeric",
        "value": 3,
        "tolerance": 0.001,
        "accepted_units": [
          "m",
          "metre",
          "metres",
          "meter",
          "meters"
        ]
      },
      "feedback_correct": "Correct. 7.5 cm times 40 is 300 cm, which is 3 m.",
      "feedback_incorrect": "Write 40 real centimetres per model centimetre so the model unit cancels, then convert the resulting centimetres to metres.",
      "misconception_ids": [
        "misconception:scale:reverse-factor"
      ],
      "provenance": [
        {
          "source_id": "source:openstax-prealgebra-scale",
          "locator": "Section 9.3, Similar Triangles; similar figures as scale models with equal corresponding-side ratios",
          "method": "derived",
          "recorded_at": "2026-08-12T12:00:00Z",
          "confidence": 0.96,
          "content_hash": "58f3618a11d10bdb01d31708dd11f8f459af59250d9346c2b2df5345571a50d4"
        }
      ],
      "evaluator_version": "atlas-eval-v2"
    },
    {
      "id": "assessment:scale:unit-order",
      "lesson_id": "lesson:quantities-ratios-and-scale:v2",
      "objective_ids": [
        "objective:scale:check-dimensions"
      ],
      "evidence_kind": "retrieval",
      "item_form": "ordering",
      "prompt": "Put the conversion steps for 2.4 kilometres to metres in a defensible order.",
      "response_specification": "Arrange all steps from the starting quantity to the checked result.",
      "options": [
        {
          "id": "check",
          "label": "Check that 2400 m is the same length and a plausible magnitude"
        },
        {
          "id": "factor",
          "label": "Multiply by 1000 m per 1 km"
        },
        {
          "id": "state",
          "label": "Write 2.4 km as the starting quantity"
        },
        {
          "id": "cancel",
          "label": "Cancel km and identify m as the remaining unit"
        }
      ],
      "hints": [
        "State the quantity before choosing a factor; check only after a unit remains."
      ],
      "answer": {
        "kind": "ordering",
        "ordered_ids": [
          "state",
          "factor",
          "cancel",
          "check"
        ]
      },
      "feedback_correct": "Correct. The order makes the conversion direction and the final plausibility check visible.",
      "feedback_incorrect": "Begin with the measured quantity, choose a factor that cancels its unit, identify the remaining unit, then check the result.",
      "misconception_ids": [
        "misconception:scale:units-decoration"
      ],
      "provenance": [
        {
          "source_id": "source:openstax-prealgebra-measurement",
          "locator": "Section 7.5, Convert Units of Measurement; dimensional-analysis conversion factors",
          "method": "ai_drafted",
          "recorded_at": "2026-08-12T12:00:00Z",
          "confidence": 0.97,
          "content_hash": "8a5ae456b59b2280dc27205d77b3b7569ebc4dce493b5c3a34c8419ee4a6d510"
        }
      ],
      "evaluator_version": "atlas-eval-v2"
    },
    {
      "id": "assessment:scale:transfer",
      "lesson_id": "lesson:quantities-ratios-and-scale:v2",
      "objective_ids": [
        "objective:scale:interpret-quantity",
        "objective:scale:apply-ratio",
        "objective:scale:check-dimensions"
      ],
      "evidence_kind": "transfer",
      "item_form": "numeric",
      "prompt": "On a 1 to 50 blueprint, a doorway is 4.2 cm wide. The safety requirement is at least 2.0 m. What real width does the drawing specify?",
      "response_specification": "Enter the real width as a number with a metre unit. The feedback will then ask you to compare it with the requirement.",
      "options": [],
      "hints": [],
      "answer": {
        "kind": "numeric",
        "value": 2.1,
        "tolerance": 0.001,
        "accepted_units": [
          "m",
          "metre",
          "metres",
          "meter",
          "meters"
        ]
      },
      "feedback_correct": "Correct. 4.2 cm times 50 is 210 cm, or 2.1 m, so the drawing clears the stated minimum by 0.1 m.",
      "feedback_incorrect": "Orient the scale as 50 real centimetres per blueprint centimetre, cancel blueprint centimetres, then convert the result to metres.",
      "misconception_ids": [
        "misconception:scale:units-decoration",
        "misconception:scale:reverse-factor"
      ],
      "provenance": [
        {
          "source_id": "source:openstax-prealgebra-scale",
          "locator": "Section 9.3, Similar Triangles; similar figures as scale models with equal corresponding-side ratios",
          "method": "derived",
          "recorded_at": "2026-08-12T12:00:00Z",
          "confidence": 0.96,
          "content_hash": "d8b99cd078a9f8cdcb80fb1835730d2f0fb3013537159902fc186de005faec93"
        }
      ],
      "evaluator_version": "atlas-eval-v2"
    },
    {
      "id": "assessment:procedures:assignment-trace",
      "lesson_id": "lesson:procedures-and-state:v2",
      "objective_ids": [
        "objective:procedures:trace-state"
      ],
      "evidence_kind": "retrieval",
      "item_form": "ordering",
      "prompt": "Arrange the reasoning for x = x + 2 when the current x is 5.",
      "response_specification": "Put all four operations in execution order.",
      "options": [
        {
          "id": "bind",
          "label": "Rebind x to 7"
        },
        {
          "id": "read",
          "label": "Read the current value 5 through the name x"
        },
        {
          "id": "record",
          "label": "Record the after-state x equals 7"
        },
        {
          "id": "evaluate",
          "label": "Evaluate 5 plus 2 to obtain 7"
        }
      ],
      "hints": [
        "The right-hand expression must be evaluated before the target is updated."
      ],
      "answer": {
        "kind": "ordering",
        "ordered_ids": [
          "read",
          "evaluate",
          "bind",
          "record"
        ]
      },
      "feedback_correct": "Correct. The old binding is read before the new binding is installed.",
      "feedback_incorrect": "Evaluate the right-hand expression using the before-state, then update the target and record the after-state.",
      "misconception_ids": [
        "misconception:procedures:equals"
      ],
      "provenance": [
        {
          "source_id": "source:python-reference-simple",
          "locator": "Section 7.2, Assignment statements; assignment evaluates an expression then assigns the result to targets",
          "method": "ai_drafted",
          "recorded_at": "2026-08-12T12:00:00Z",
          "confidence": 0.96,
          "content_hash": "ccbb3472634ef1ce8636a31b0ac53a7cf66be9e3fce9ce9b484a471ffb1db301"
        }
      ],
      "evaluator_version": "atlas-eval-v2"
    },
    {
      "id": "assessment:procedures:loop-invariant",
      "lesson_id": "lesson:procedures-and-state:v2",
      "objective_ids": [
        "objective:procedures:predict-loop"
      ],
      "evidence_kind": "retrieval",
      "item_form": "numeric",
      "prompt": "A loop starts total at 0 and visits [3, 6, 9], adding each item to total. What is total after the second iteration?",
      "response_specification": "Enter the integer value. Use the unit “integer”.",
      "options": [],
      "hints": [
        "After two iterations, only 3 and 6 have been visited."
      ],
      "answer": {
        "kind": "numeric",
        "value": 9,
        "tolerance": 0,
        "accepted_units": [
          "integer"
        ]
      },
      "feedback_correct": "Correct. After two visits, the invariant gives total as 3 plus 6, which is 9.",
      "feedback_incorrect": "Trace only the first two items. The third item has not been visited yet.",
      "misconception_ids": [
        "misconception:procedures:loop-at-once"
      ],
      "provenance": [
        {
          "source_id": "source:python-tutorial-control",
          "locator": "Section 4.2, for Statements; iteration over sequence items in order",
          "method": "ai_drafted",
          "recorded_at": "2026-08-12T12:00:00Z",
          "confidence": 0.97,
          "content_hash": "9830646dc051dd8198851ed6355b15a76cc2165233287bdbf0e8b629f5cc3fc7"
        }
      ],
      "evaluator_version": "atlas-eval-v2"
    },
    {
      "id": "assessment:procedures:mutation-selection",
      "lesson_id": "lesson:procedures-and-state:v2",
      "objective_ids": [
        "objective:procedures:distinguish-mutation"
      ],
      "evidence_kind": "retrieval",
      "item_form": "multiple_selection",
      "prompt": "Which statements would mutate the existing list bound to items? Select every statement that changes that object's contents.",
      "response_specification": "Select all mutating operations.",
      "options": [
        {
          "id": "append",
          "label": "items.append(4)"
        },
        {
          "id": "rebind",
          "label": "items = [1, 2, 3, 4]"
        },
        {
          "id": "assign_element",
          "label": "items[0] = 9"
        },
        {
          "id": "length",
          "label": "count = len(items)"
        }
      ],
      "hints": [
        "Separate changing a name's binding from changing the object already reached through that name."
      ],
      "answer": {
        "kind": "multiple_selection",
        "option_ids": [
          "append",
          "assign_element"
        ]
      },
      "feedback_correct": "Correct. append and indexed assignment change the existing list object; rebinding and reading its length do not.",
      "feedback_incorrect": "Ask whether the operation changes the current list object or merely changes a name or reads a value.",
      "misconception_ids": [
        "misconception:procedures:assignment-copies"
      ],
      "provenance": [
        {
          "source_id": "source:python-tutorial-control",
          "locator": "Section 4.8, Defining Functions; local symbol table and arguments as object references",
          "method": "ai_drafted",
          "recorded_at": "2026-08-12T12:00:00Z",
          "confidence": 0.96,
          "content_hash": "a67b9ef955f664aab95e6a3bc26b1b8bbf75029db14fa385e7f31de6f45015e8"
        }
      ],
      "evaluator_version": "atlas-eval-v2"
    },
    {
      "id": "assessment:procedures:transfer-trace",
      "lesson_id": "lesson:procedures-and-state:v2",
      "objective_ids": [
        "objective:procedures:trace-state",
        "objective:procedures:predict-loop",
        "objective:procedures:distinguish-mutation"
      ],
      "evidence_kind": "transfer",
      "item_form": "state_trace",
      "prompt": "A function starts converted as an empty list, visits temperatures [0, 10], and appends c times 9 divided by 5 plus 32. Submit the two after-iteration states.",
      "response_specification": "For each row, provide c and converted using the exact displayed values.",
      "options": [],
      "hints": [],
      "answer": {
        "kind": "state_trace",
        "states": [
          {
            "c": "0",
            "converted": "[32.0]"
          },
          {
            "c": "10",
            "converted": "[32.0, 50.0]"
          }
        ]
      },
      "feedback_correct": "Correct. Each row records the current item and the new result-list contents; the input list remains unchanged.",
      "feedback_incorrect": "Start with an empty result list. Visit 0 first, append 32.0, then visit 10 and append 50.0 without replacing the earlier element.",
      "misconception_ids": [
        "misconception:procedures:loop-at-once",
        "misconception:procedures:assignment-copies"
      ],
      "provenance": [
        {
          "source_id": "source:python-tutorial-control",
          "locator": "Section 4.8, Defining Functions; local symbol table and arguments as object references",
          "method": "ai_drafted",
          "recorded_at": "2026-08-12T12:00:00Z",
          "confidence": 0.96,
          "content_hash": "5da70465d49c3a2d756a12babb7e29a59810622e83616e375b46de5da28e4aa5"
        }
      ],
      "evaluator_version": "atlas-eval-v2"
    },
    {
      "id": "assessment:measurement:report",
      "lesson_id": "lesson:measurement-and-uncertainty:v2",
      "objective_ids": [
        "objective:measurement:report-result"
      ],
      "evidence_kind": "retrieval",
      "item_form": "numeric",
      "prompt": "Five length readings span from 14.2 cm to 14.6 cm. Using half the range as the uncertainty convention, what uncertainty should be reported?",
      "response_specification": "Enter the uncertainty magnitude and a centimetre unit.",
      "options": [],
      "hints": [
        "Find the range first, then divide it by two."
      ],
      "answer": {
        "kind": "numeric",
        "value": 0.2,
        "tolerance": 0.001,
        "accepted_units": [
          "cm",
          "centimetre",
          "centimetres",
          "centimeter",
          "centimeters"
        ]
      },
      "feedback_correct": "Correct. The range is 0.4 cm, so the half-range uncertainty is 0.2 cm.",
      "feedback_incorrect": "Subtract the smallest reading from the largest, then take half. Keep the centimetre unit.",
      "misconception_ids": [
        "misconception:measurement:display-certainty"
      ],
      "provenance": [
        {
          "source_id": "source:openstax-physics-uncertainty",
          "locator": "Section 1.6, Accuracy, Precision, Uncertainty, and Discrepancy; reporting a measured value as A plus or minus delta A",
          "method": "ai_drafted",
          "recorded_at": "2026-08-12T12:00:00Z",
          "confidence": 0.96,
          "content_hash": "874dd983aac817f3ae621b4417e35e31d525beb87f0e8d28c52ec9ab091e55d3"
        }
      ],
      "evaluator_version": "atlas-eval-v2"
    },
    {
      "id": "assessment:measurement:error-sources",
      "lesson_id": "lesson:measurement-and-uncertainty:v2",
      "objective_ids": [
        "objective:measurement:diagnose-error"
      ],
      "evidence_kind": "retrieval",
      "item_form": "multiple_selection",
      "prompt": "Which diagnostics can reveal a constant +0.5 cm zero offset? Select every defensible choice.",
      "response_specification": "Select methods that compare the instrument or procedure with independent information.",
      "options": [
        {
          "id": "repeat",
          "label": "Repeat the same unknown length many times with the same instrument"
        },
        {
          "id": "reference",
          "label": "Measure a certified reference length"
        },
        {
          "id": "second",
          "label": "Compare with an independently calibrated instrument"
        },
        {
          "id": "average",
          "label": "Average more readings from the unchanged setup"
        }
      ],
      "hints": [
        "A constant offset shifts every repeated value together."
      ],
      "answer": {
        "kind": "multiple_selection",
        "option_ids": [
          "reference",
          "second"
        ]
      },
      "feedback_correct": "Correct. An external reference or independent calibration can expose a shared offset; repetition alone mainly characterizes scatter.",
      "feedback_incorrect": "Look for a comparison that does not share the same possible offset. More values from the unchanged setup can remain tightly shifted.",
      "misconception_ids": [
        "misconception:measurement:repeat-removes-bias"
      ],
      "provenance": [
        {
          "source_id": "source:openstax-physics-uncertainty",
          "locator": "Section 1.6, Accuracy, Precision, Uncertainty, and Discrepancy; reporting a measured value as A plus or minus delta A",
          "method": "ai_drafted",
          "recorded_at": "2026-08-12T12:00:00Z",
          "confidence": 0.96,
          "content_hash": "c6e0a8bd14448cce9e4738de96be3f800f03f9418fd69d5ada5f4faebca27184"
        }
      ],
      "evaluator_version": "atlas-eval-v2"
    },
    {
      "id": "assessment:measurement:cluster",
      "lesson_id": "lesson:measurement-and-uncertainty:v2",
      "objective_ids": [
        "objective:measurement:compare-precision-accuracy"
      ],
      "evidence_kind": "retrieval",
      "item_form": "exact_text",
      "prompt": "A tight cluster of measurements lies far from an accepted reference. Which two-word description fits it?",
      "response_specification": "Enter the description using the words precise and inaccurate.",
      "options": [],
      "hints": [
        "Tight spread describes precision; displacement from the reference describes accuracy."
      ],
      "answer": {
        "kind": "exact_text",
        "accepted": [
          "precise but inaccurate",
          "precise and inaccurate"
        ],
        "case_sensitive": false
      },
      "feedback_correct": "Correct. The trials agree closely with each other but not with the reference.",
      "feedback_incorrect": "Describe the spread first and the discrepancy from the reference second.",
      "misconception_ids": [
        "misconception:measurement:precision-is-accuracy"
      ],
      "provenance": [
        {
          "source_id": "source:openstax-physics-uncertainty",
          "locator": "Section 1.6, Accuracy, Precision, Uncertainty, and Discrepancy; reporting a measured value as A plus or minus delta A",
          "method": "ai_drafted",
          "recorded_at": "2026-08-12T12:00:00Z",
          "confidence": 0.96,
          "content_hash": "edb6d2a7dead2060c882f1a04d4e753ea7acef192eafe6e70444b7b39c055f1e"
        }
      ],
      "evaluator_version": "atlas-eval-v2"
    },
    {
      "id": "assessment:measurement:transfer",
      "lesson_id": "lesson:measurement-and-uncertainty:v2",
      "objective_ids": [
        "objective:measurement:report-result",
        "objective:measurement:diagnose-error",
        "objective:measurement:compare-precision-accuracy"
      ],
      "evidence_kind": "transfer",
      "item_form": "multiple_selection",
      "prompt": "You compare two pendulums whose mean periods differ by 0.03 s, while each repeated result is reported with ±0.05 s uncertainty. Which conclusions are supported? Select every defensible statement.",
      "response_specification": "Select statements that respect the reported uncertainty and identify a useful next step.",
      "options": [
        {
          "id": "ordered",
          "label": "The data conclusively prove which pendulum is slower"
        },
        {
          "id": "unresolved",
          "label": "The achieved uncertainty does not clearly resolve the 0.03 s difference"
        },
        {
          "id": "improve",
          "label": "A more precise timing procedure or more cycles per timing could be investigated"
        },
        {
          "id": "digits",
          "label": "Printing more decimal places would by itself settle the comparison"
        }
      ],
      "hints": [],
      "answer": {
        "kind": "multiple_selection",
        "option_ids": [
          "unresolved",
          "improve"
        ]
      },
      "feedback_correct": "Correct. The measured separation is smaller than the uncertainty scale, so the ordering remains unresolved and the procedure needs stronger resolving power.",
      "feedback_incorrect": "Compare the 0.03 s separation with the ±0.05 s uncertainties. Display digits do not create resolving power.",
      "misconception_ids": [
        "misconception:measurement:display-certainty",
        "misconception:measurement:precision-is-accuracy"
      ],
      "provenance": [
        {
          "source_id": "source:openstax-physics-uncertainty",
          "locator": "Section 1.6, Accuracy, Precision, Uncertainty, and Discrepancy; reporting a measured value as A plus or minus delta A",
          "method": "ai_drafted",
          "recorded_at": "2026-08-12T12:00:00Z",
          "confidence": 0.96,
          "content_hash": "12e1411bf0a9b901d77669cf7692992703c5bbab8c8936472c9d5e51ccd8deba"
        }
      ],
      "evaluator_version": "atlas-eval-v2"
    }
  ],
  "simulations": [
    {
      "id": "simulation:scale-bench-v2",
      "slug": "scale-bench",
      "lesson_id": "lesson:quantities-ratios-and-scale:v2",
      "concept_ids": [],
      "title": "Scale bench",
      "model_kind": "scale",
      "controls": [
        {
          "id": "model_length_cm",
          "label": "Drawing length",
          "unit": "cm",
          "minimum": 2,
          "maximum": 30,
          "step": 0.5,
          "default": 12
        },
        {
          "id": "scale_denominator",
          "label": "Scale denominator",
          "unit": "real cm per drawing cm",
          "minimum": 5,
          "maximum": 50,
          "step": 1,
          "default": 10
        }
      ],
      "outputs": [
        "real_length_cm",
        "real_length_m",
        "dimensional_check"
      ],
      "invariant": "Real length divided by model length equals the selected scale denominator when both lengths use the same unit.",
      "prediction_prompt": "Predict the real length and direction of change before moving either control.",
      "observation_prompt": "Explain the result using the double number line and the cancelled-unit expression.",
      "static_states": [
        {
          "label": "Compact scale",
          "values": {
            "model_length_cm": "12",
            "real_length_cm": "120",
            "real_length_m": "1.2",
            "scale_denominator": "10"
          },
          "explanation": "Twelve drawing centimetres at 1 to 10 represent one hundred twenty real centimetres, or 1.2 metres."
        },
        {
          "label": "Expanded scale",
          "values": {
            "model_length_cm": "12",
            "real_length_cm": "300",
            "real_length_m": "3",
            "scale_denominator": "25"
          },
          "explanation": "With drawing length held fixed, increasing the denominator increases the represented real length proportionally."
        }
      ],
      "provenance": [
        {
          "source_id": "source:openstax-prealgebra-scale",
          "locator": "Section 9.3, Similar Triangles; similar figures as scale models with equal corresponding-side ratios",
          "method": "derived",
          "recorded_at": "2026-08-12T12:00:00Z",
          "confidence": 0.96,
          "content_hash": "a99654976ce7b0238b29c262eed4671d7af328b0cba116f31f5f2f98dfeb2ced"
        }
      ],
      "review_ids": []
    },
    {
      "id": "simulation:procedure-trace-v2",
      "slug": "python-trace",
      "lesson_id": "lesson:procedures-and-state:v2",
      "concept_ids": [],
      "title": "Procedure trace",
      "model_kind": "procedure_trace",
      "controls": [
        {
          "id": "loop_length",
          "label": "Number of iterations",
          "unit": "iterations",
          "minimum": 1,
          "maximum": 8,
          "step": 1,
          "default": 3
        },
        {
          "id": "input_step",
          "label": "Item step",
          "unit": "integer value",
          "minimum": 1,
          "maximum": 6,
          "step": 1,
          "default": 2
        }
      ],
      "outputs": [
        "current_item",
        "total_before",
        "total_after",
        "invariant_check"
      ],
      "invariant": "After each body execution, total equals the sum of every item visited so far.",
      "prediction_prompt": "Predict the highlighted next state before revealing the row.",
      "observation_prompt": "Explain exactly which binding changed and which earlier values remained available.",
      "static_states": [
        {
          "label": "Second row",
          "values": {
            "current_item": "4",
            "iteration": "2",
            "total_after": "6",
            "total_before": "2"
          },
          "explanation": "The second row uses the total left by the first row, adds the current item 4, and records 6."
        },
        {
          "label": "Final row",
          "values": {
            "current_item": "6",
            "iteration": "3",
            "total_after": "12",
            "total_before": "6"
          },
          "explanation": "The final state equals the sum of all three visited items, 2 plus 4 plus 6."
        }
      ],
      "provenance": [
        {
          "source_id": "source:python-tutorial-control",
          "locator": "Section 4.2, for Statements; iteration over sequence items in order",
          "method": "ai_drafted",
          "recorded_at": "2026-08-12T12:00:00Z",
          "confidence": 0.97,
          "content_hash": "26e806e7498f46f9d1d8c9f8e4a2a12d83f4d995d5f08015bf7bfbddde2552cf"
        }
      ],
      "review_ids": []
    },
    {
      "id": "simulation:measurement-bench-v2",
      "slug": "measurement-bench",
      "lesson_id": "lesson:measurement-and-uncertainty:v2",
      "concept_ids": [],
      "title": "Measurement bench",
      "model_kind": "measurement",
      "controls": [
        {
          "id": "trials",
          "label": "Repeated trials",
          "unit": "trials",
          "minimum": 3,
          "maximum": 24,
          "step": 1,
          "default": 8
        },
        {
          "id": "resolution",
          "label": "Instrument resolution",
          "unit": "cm",
          "minimum": 0.01,
          "maximum": 0.5,
          "step": 0.01,
          "default": 0.1
        },
        {
          "id": "scatter",
          "label": "Trial scatter",
          "unit": "cm",
          "minimum": 0,
          "maximum": 1,
          "step": 0.05,
          "default": 0.25
        },
        {
          "id": "zero_offset",
          "label": "Hidden zero offset",
          "unit": "cm",
          "minimum": -1,
          "maximum": 1,
          "step": 0.1,
          "default": 0
        }
      ],
      "outputs": [
        "trial_values",
        "mean",
        "range",
        "reference",
        "discrepancy"
      ],
      "invariant": "Repetition reveals scatter; only comparison with a reference or independent calibration reveals a constant shared offset.",
      "prediction_prompt": "Predict cluster width and centre before generating the deterministic trial sequence.",
      "observation_prompt": "Compare the cluster with the reference and name what repetition did and did not reveal.",
      "static_states": [
        {
          "label": "Precise and accurate",
          "values": {
            "cluster_centre_cm": "10.0",
            "scatter_cm": "0.1",
            "true_value_cm": "10.0",
            "zero_offset_cm": "0.0"
          },
          "explanation": "The values cluster tightly near the reference."
        },
        {
          "label": "Precise but inaccurate",
          "values": {
            "cluster_centre_cm": "10.5",
            "scatter_cm": "0.1",
            "true_value_cm": "10.0",
            "zero_offset_cm": "0.5"
          },
          "explanation": "The values remain tightly clustered but share a displacement from the reference."
        }
      ],
      "provenance": [
        {
          "source_id": "source:openstax-physics-uncertainty",
          "locator": "Section 1.6, Accuracy, Precision, Uncertainty, and Discrepancy; reporting a measured value as A plus or minus delta A",
          "method": "ai_drafted",
          "recorded_at": "2026-08-12T12:00:00Z",
          "confidence": 0.96,
          "content_hash": "5a2b291dd1d06f623df7de7382c44c10e33e3547e7cc4dee1b7e10d43e797430"
        }
      ],
      "review_ids": []
    },
    {
      "id": "simulation:robust-selection-v1",
      "slug": "robust-selection",
      "concept_ids": [
        "concept:quantities-ratios-and-scale",
        "concept:probability-as-evidence",
        "concept:learning-systems-and-evaluation"
      ],
      "title": "When the average hides a rare failure",
      "model_kind": "robust_risk",
      "controls": [
        {
          "id": "rare_share_percent",
          "label": "Code prevalence",
          "unit": "percent",
          "minimum": 1,
          "maximum": 30,
          "step": 1,
          "default": 5
        }
      ],
      "outputs": [
        "fragile_mean",
        "robust_mean",
        "fragile_worst",
        "robust_worst",
        "break_even_share"
      ],
      "invariant": "Worst-environment risk ignores prevalence; a weighted mean can prefer the fragile subset below the crossover.",
      "prediction_prompt": "At five percent code prevalence, which candidate will prevalence-weighted mean risk select, and which candidate will worst-environment risk select? Explain why before revealing the bench.",
      "observation_prompt": "Move code prevalence across the crossover. Explain why the mean selector flips while the worst-environment selector does not.",
      "static_states": [
        {
          "label": "Rare room discounted",
          "values": {
            "code_prevalence": "5%",
            "fragile_mean": "0.1294",
            "mean_selects": "Frequency-first",
            "robust_mean": "0.1579",
            "worst_selects": "Robust capacity"
          },
          "explanation": "The frequency-first candidate's large code loss is multiplied by a small prevalence, so its weighted mean remains lower even though its worst-room loss is 0.84."
        },
        {
          "label": "Mean-risk crossover",
          "values": {
            "code_prevalence": "9.5238%",
            "mean_risk": "0.163238",
            "mean_selects": "Tie",
            "worst_selects": "Robust capacity"
          },
          "explanation": "At the crossover the candidates have the same prevalence-weighted mean risk. Their worst-environment risks remain 0.84 and 0.27."
        },
        {
          "label": "Rare room carries enough weight",
          "values": {
            "code_prevalence": "15%",
            "fragile_mean": "0.2042",
            "mean_selects": "Robust capacity",
            "robust_mean": "0.1697",
            "worst_selects": "Robust capacity"
          },
          "explanation": "Once code receives enough prevalence, its large frequency-first loss outweighs that candidate's small advantage in common environments."
        }
      ],
      "provenance": [
        {
          "source_id": "source:drfsp-paper-v3",
          "locator": "Conceptual robust-risk scenario derived from the paper's normalized mean and worst-environment objectives; values are illustrative and not experimental measurements.",
          "method": "derived",
          "recorded_at": "2026-08-22T00:00:00Z",
          "confidence": 1,
          "content_hash": "c2d32e965475914d9f1ef0b0c740f55c4af19f01a19340a9a7e1b4554dcfaf5c"
        },
        {
          "source_id": "source:atlas-method-v2",
          "locator": "Prediction-first laboratory contract; practice interactions do not constitute mastery evidence.",
          "method": "human",
          "recorded_at": "2026-08-22T00:00:00Z",
          "confidence": 1,
          "content_hash": "c2d32e965475914d9f1ef0b0c740f55c4af19f01a19340a9a7e1b4554dcfaf5c"
        }
      ],
      "review_ids": []
    }
  ],
  "projects": [
    {
      "id": "project:climate-signal-ledger-v2",
      "slug": "climate-signal-ledger",
      "pathway": "mathematics",
      "title": "Climate signal ledger",
      "summary": "Build a reproducible notebook that separates long-term change, short-term variation, sampling choices, and uncertainty in one public temperature record.",
      "objective_ids": [
        "objective:change-and-accumulation",
        "objective:probability-as-evidence",
        "objective:measurement:diagnose-error"
      ],
      "prerequisite_ids": [
        "concept:change-and-accumulation",
        "concept:probability-as-evidence"
      ],
      "milestones": [
        "Choose one bounded public series and preserve the original file plus its data dictionary.",
        "Plot raw observations before selecting a summary or smoothing method.",
        "Compare at least two defensible summaries and explain what each hides.",
        "Audit units, missing values, time windows, and measurement limitations."
      ],
      "deliverables": [
        "A runnable notebook with a one-command or one-cell reproduction path.",
        "A source ledger linking every external claim to a precise locator.",
        "A short interpretation that separates observation, calculation, and inference."
      ],
      "source_ids": [
        "source:atlas-method-v2",
        "source:openstax-physics-uncertainty"
      ],
      "evidence_requirements": [
        "Preserve one failed or misleading analysis and explain why it failed.",
        "Include a transfer explanation for a reader who has not seen the lesson sequence."
      ],
      "reproducibility_checks": [
        "A clean run recreates every table and figure from the preserved input.",
        "Reported summary values match machine-checked fixtures within stated tolerance."
      ],
      "checkpoint_ids": [
        "checkpoint:climate:source",
        "checkpoint:climate:raw-view",
        "checkpoint:climate:comparison",
        "checkpoint:climate:audit"
      ],
      "review_ids": []
    },
    {
      "id": "project:small-search-engine-v2",
      "slug": "small-search-engine",
      "pathway": "computing",
      "title": "A small search engine",
      "summary": "Build and test an index over a local text collection, then explain the invariants and cost tradeoffs that make retrieval trustworthy.",
      "objective_ids": [
        "objective:procedures:trace-state",
        "objective:data-and-invariants",
        "objective:algorithms-and-cost"
      ],
      "prerequisite_ids": [
        "concept:procedures-and-state",
        "concept:data-and-invariants",
        "concept:algorithms-and-cost"
      ],
      "milestones": [
        "Specify tokenization, normalization, and document identity before writing the index.",
        "Build an inverted index and trace one document through every state transition.",
        "Add queries with zero, one, and many matches plus malformed input cases.",
        "Measure build and query behavior as the corpus grows."
      ],
      "deliverables": [
        "Source code, a tiny licensed corpus, and a deterministic test command.",
        "A state/invariant table for ingestion and query phases.",
        "A cost note distinguishing measured behavior from asymptotic expectation."
      ],
      "source_ids": [
        "source:atlas-method-v2",
        "source:python-tutorial-control"
      ],
      "evidence_requirements": [
        "At least one unhinted trace and one transfer explanation.",
        "A failure fixture that would catch an index/document mismatch."
      ],
      "reproducibility_checks": [
        "Rebuilding from the same corpus produces the same normalized index.",
        "Every returned document can be traced to an inspectable posting entry."
      ],
      "checkpoint_ids": [
        "checkpoint:search:contract",
        "checkpoint:search:index",
        "checkpoint:search:queries",
        "checkpoint:search:cost"
      ],
      "review_ids": []
    },
    {
      "id": "project:orbital-model-audit-v2",
      "slug": "orbital-model-audit",
      "pathway": "physics",
      "title": "Orbital model audit",
      "summary": "Construct a computational orbit model and make its numerical error, conservation behavior, and domain of validity visible.",
      "objective_ids": [
        "objective:measurement:report-result",
        "objective:motion-and-interaction",
        "objective:energy-and-conservation"
      ],
      "prerequisite_ids": [
        "concept:motion-and-interaction",
        "concept:energy-and-conservation",
        "concept:fields-models-and-limits"
      ],
      "milestones": [
        "Define the system, units, initial conditions, force law, and timestep.",
        "Reproduce one orbit and plot position, speed, and energy against time.",
        "Halve the timestep twice and compare trajectory and energy drift.",
        "State one physical regime and one numerical regime where the model fails."
      ],
      "deliverables": [
        "Runnable model code and a machine-readable parameter file.",
        "An energy ledger and timestep convergence figure with visible units.",
        "A validity note that distinguishes approximation from coding error."
      ],
      "source_ids": [
        "source:atlas-method-v2",
        "source:openstax-physics-units",
        "source:openstax-physics-uncertainty"
      ],
      "evidence_requirements": [
        "Preserve a coarse-timestep run that visibly fails the audit.",
        "Explain how the evidence changes one modeling decision."
      ],
      "reproducibility_checks": [
        "A clean run recreates the accepted trajectory and diagnostic figures.",
        "Halving the timestep changes the reported error in the documented direction."
      ],
      "checkpoint_ids": [
        "checkpoint:orbit:contract",
        "checkpoint:orbit:first-run",
        "checkpoint:orbit:convergence",
        "checkpoint:orbit:limits"
      ],
      "review_ids": []
    }
  ],
  "sources": [
    {
      "id": "source:atlas-method-v2",
      "title": "Ephemerent Atlas curriculum and evidence method",
      "publisher": "Ephemerent Education",
      "url": "https://atlas.ephemerent.com/policies/ai-use",
      "license": "CC BY 4.0",
      "accessed_at": "2026-08-12"
    },
    {
      "id": "source:openstax-prealgebra-ratios",
      "title": "Prealgebra 2e, section 5.6 — Ratios and Rate",
      "publisher": "OpenStax",
      "url": "https://openstax.org/books/prealgebra-2e/pages/5-6-ratios-and-rate",
      "license": "CC BY 4.0",
      "accessed_at": "2026-08-12"
    },
    {
      "id": "source:openstax-prealgebra-measurement",
      "title": "Prealgebra 2e, section 7.5 — Systems of Measurement",
      "publisher": "OpenStax",
      "url": "https://openstax.org/books/prealgebra-2e/pages/7-5-systems-of-measurement",
      "license": "CC BY 4.0",
      "accessed_at": "2026-08-12"
    },
    {
      "id": "source:openstax-prealgebra-scale",
      "title": "Prealgebra 2e, section 9.3 — Similar Figures",
      "publisher": "OpenStax",
      "url": "https://openstax.org/books/prealgebra-2e/pages/9-3-use-properties-of-angles-triangles-and-the-pythagorean-theorem",
      "license": "CC BY 4.0",
      "accessed_at": "2026-08-12"
    },
    {
      "id": "source:python-tutorial-control",
      "title": "The Python Tutorial, sections 4.2 and 4.8 — for statements and defining functions",
      "publisher": "Python Software Foundation",
      "url": "https://docs.python.org/3/tutorial/controlflow.html",
      "license": "PSF License Version 2",
      "accessed_at": "2026-08-12"
    },
    {
      "id": "source:python-reference-simple",
      "title": "Python Language Reference, section 7 — Simple statements",
      "publisher": "Python Software Foundation",
      "url": "https://docs.python.org/3/reference/simple_stmts.html",
      "license": "PSF License Version 2",
      "accessed_at": "2026-08-12"
    },
    {
      "id": "source:openstax-physics-units",
      "title": "University Physics Volume 1, section 1.2 — Units and Standards",
      "publisher": "OpenStax",
      "url": "https://openstax.org/books/university-physics-volume-1/pages/1-2-units-and-standards",
      "license": "CC BY 4.0",
      "accessed_at": "2026-08-12"
    },
    {
      "id": "source:openstax-physics-uncertainty",
      "title": "University Physics Volume 1, section 1.6 — Significant Figures",
      "publisher": "OpenStax",
      "url": "https://openstax.org/books/university-physics-volume-1/pages/1-6-significant-figures",
      "license": "CC BY 4.0",
      "accessed_at": "2026-08-12"
    },
    {
      "id": "source:drfsp-paper-v3",
      "title": "Distribution-Robust Functional Subset Projection for Structured Neural Network Width Compression",
      "publisher": "Ephemerent Research",
      "url": "https://ephemerent.com/journal/article/distribution-robust-functional-subset-projection-for-structured-neural-network-width-compression",
      "license": "CC BY 4.0",
      "accessed_at": "2026-08-22"
    }
  ],
  "generation_records": [
    {
      "artifact_id": "lesson:quantities-ratios-and-scale:v2",
      "model_identifier": "Codex runtime — exact model identifier unavailable to repository",
      "authoring_contract_version": "atlas-authoring-v2",
      "source_pack_hash": "954b734b1fcbef0e18b56a8ea520f21e38f4fd813acfcc63cc814a9c93d8ea28",
      "trace_hash": "e0a560452b0462f2ecc1f90ec6bdf25c596f1ebc3555e4a4b680bc7c26c0d7e5",
      "generated_at": "2026-08-12T12:00:00Z"
    },
    {
      "artifact_id": "lesson:procedures-and-state:v2",
      "model_identifier": "Codex runtime — exact model identifier unavailable to repository",
      "authoring_contract_version": "atlas-authoring-v2",
      "source_pack_hash": "90687a2025891a6c0d53101f5264ad2b5940a5ee3f707ef28d68c0f42d247d84",
      "trace_hash": "2dc8288f36b15a43d12f3da5868c95e8c89d7404df326fdff4ff0f7f1125642e",
      "generated_at": "2026-08-12T12:00:00Z"
    },
    {
      "artifact_id": "lesson:measurement-and-uncertainty:v2",
      "model_identifier": "Codex runtime — exact model identifier unavailable to repository",
      "authoring_contract_version": "atlas-authoring-v2",
      "source_pack_hash": "652d7b1a2125fb89af3aa207ca90bc5ed37332aa6e35ce851fb54ddc6f8d6b4d",
      "trace_hash": "1e04c0db3901bb33bda7596ebfe40fc093d22db9a7392b75022f25b904ba10e8",
      "generated_at": "2026-08-12T12:00:00Z"
    }
  ],
  "reviews": [],
  "benchmarking_studies": [
    {
      "id": "study:foundations-quality-gate",
      "study_id": "foundations-depth-first-gate",
      "cohort_id": "pilot-internal-2026-q3",
      "retention_horizon_days": 0,
      "scoring_rule": "manual precision gate before any new lesson conversion",
      "evidence_artifact": "content/quality/foundations-50-sample.json",
      "decision_context": "Stops further bulk conversion until independent scoring reaches >= 0.90 precision on claims/entities/relations for this candidate graph manifest."
    },
    {
      "id": "study:foundations-benchmark-readiness",
      "study_id": "foundations-v2-comparative-gate",
      "cohort_id": "design-partner-designset",
      "retention_horizon_days": 14,
      "scoring_rule": ">=0.95 precision on stratified 200-item sample before release transition",
      "evidence_artifact": "content/quality/foundations-200-sample.json",
      "decision_context": "Prerequisite for any superiority narrative around foundations learning outcomes or learning-to-learn evidence."
    }
  ],
  "release_decision_context": "Field-trial candidate only. We do not assert superiority or retention outcomes until the 50/200-item quality gates and learner/institution studies clear on the same sealed commit."
}
