{
  "registryVersion": "1.0.0",
  "generatedFrom": "apps/web/src/roadmap-data.ts (generated by scripts/generate-studies.py from apps/api/helioforge/studies.py)",
  "generatedAtSourceRevisionDate": "2026-10-29",
  "status": "source inventory; not a deployment receipt or independent scientific validation",
  "identities": {
    "application": {
      "browserReleaseVersion": "2.0.0",
      "source": "scripts/build-domain.mjs sets the browser release version on the generated app.json; this source identity does not prove deployment."
    },
    "apiPackage": {
      "name": "helioforge-api",
      "version": "2.0.0",
      "source": "apps/api/pyproject.toml"
    },
    "catalogue": {
      "hybrid": {
        "version": "0.2.0",
        "source": "apps/api/helioforge/data/hybrid_catalog.json",
        "architectureCount": 36,
        "scenarioCount": 24,
        "lessonCount": 36
      },
      "studies": {
        "declaredVersion": null,
        "versionStatus": "U — no independent semantic study-catalogue version is declared; content digest below identifies this catalogue snapshot.",
        "studyCount": 59,
        "workspaceCount": 12,
        "sha256": "9c89b475d9e929f1cad0792dc955082cc30bf87be305be175e22a6e9a9c5495f"
      }
    },
    "data": {
      "hybridPresets": {
        "schema": "helioforge.hybrid-bundle.v1",
        "version": "hybrid-presets-v1",
        "catalogueVersion": "0.2.0",
        "caseCount": 285,
        "sha256": "c4a0861cdc02016bb40a64ff363c9c7bee16bb053804096207e72c060b342400"
      },
      "studyExamples": {
        "kind": "synthetic_or_user_supplied_unverified",
        "version": null,
        "versionStatus": "U — examples have no named dataset version; the fixture digest identifies their current generated evidence hashes.",
        "sha256": "d53241ccab2357c73309dc5bc57d1cd3e6941ab1a3d2c51c2b68df5f82243077"
      }
    },
    "models": {
      "terminal": "terminal-greedy-hourly-v1",
      "hybrid": "hybrid-greedy-hourly-v1",
      "storage": "btm-milp-v1",
      "studies": "roadmap-screen-v1",
      "studyCatalogue": [
        "roadmap-screen-v1"
      ]
    },
    "schemas": {
      "terminal": "helioforge.terminal.v1",
      "hybridPresets": "helioforge.hybrid-bundle.v1",
      "studyResultVersion": null,
      "studyResultVersionStatus": "U — studies.py currently adds study/model_version/input provenance but no standalone result schema_version."
    }
  },
  "deliveryModes": [
    {
      "id": "bundled",
      "name": "Bundled synthetic examples",
      "description": "Bundled synthetic examples are precomputed outputs; the public browser edition does not calculate new study-model results from edited inputs.",
      "newCalculations": false
    },
    {
      "id": "terminal",
      "name": "Bounded browser Terminal",
      "description": "The Terminal runs new deterministic, bounded 24-hour energy-balance cases in the browser; it uses a greedy policy and is not an optimizer.",
      "newCalculations": true,
      "modelVersion": "terminal-greedy-hourly-v1",
      "horizon": "24 one-hour steps"
    },
    {
      "id": "python",
      "name": "Advanced local Python",
      "description": "The separate local Python application runs new calculations for the advanced study models and optimization.",
      "newCalculations": true,
      "studyCount": 59,
      "workspaceCount": 12,
      "modelVersion": "roadmap-screen-v1"
    },
    {
      "id": "external",
      "name": "Opt-in local external providers",
      "description": "Optional local external providers are separate, opt-in integrations; credentials and explicit consent are required where applicable. The public static preview makes no provider calls.",
      "newCalculations": null,
      "providerExecutionStatus": "not verified by this documentation change"
    }
  ],
  "taskStarts": [
    {
      "id": "examples",
      "label": "Explore a bundled example",
      "description": "Open the public preview and inspect a labeled synthetic result.",
      "href": "/run/#hybrid",
      "mode": "bundled"
    },
    {
      "id": "terminal",
      "label": "Calculate a bounded Terminal case",
      "description": "Open Scenario compare and use the Parametric energy workbench.",
      "href": "/run/#compare",
      "mode": "terminal"
    },
    {
      "id": "local-python",
      "label": "Set up advanced local calculations",
      "description": "Use the source setup guide to run the Python application on your machine.",
      "href": "https://github.com/mohammadrezwankhan/helioforge-energy-lab/blob/main/START_HERE.md",
      "mode": "python"
    }
  ],
  "workspaces": [
    {
      "id": "hybrid",
      "name": "Hybrid systems lab",
      "appRoute": "#hybrid",
      "studies": [
        "hybrid-resonance",
        "hybrid-clipping",
        "hybrid-inertia",
        "hybrid-powerflow",
        "hybrid-switching",
        "hybrid-controls"
      ],
      "delivery": {
        "publicBrowser": "bundled synthetic examples",
        "newCalculations": "local Python application",
        "boundedTerminal": false
      },
      "contractUrl": "/models/#workspace-hybrid"
    },
    {
      "id": "lessons",
      "name": "Learning studio",
      "appRoute": "#lessons",
      "studies": [
        "lessons-duck",
        "lessons-blackstart",
        "lessons-hydrogen",
        "lessons-energization",
        "lessons-hydrogenaux"
      ],
      "delivery": {
        "publicBrowser": "bundled synthetic examples",
        "newCalculations": "local Python application",
        "boundedTerminal": false
      },
      "contractUrl": "/models/#workspace-lessons"
    },
    {
      "id": "compare",
      "name": "Scenario compare",
      "appRoute": "#compare",
      "studies": [
        "compare-pareto",
        "compare-dunkelflaute",
        "compare-thermal",
        "compare-thermalphysics",
        "compare-reliability"
      ],
      "delivery": {
        "publicBrowser": "bundled synthetic examples",
        "newCalculations": "local Python application",
        "boundedTerminal": false
      },
      "contractUrl": "/models/#workspace-compare"
    },
    {
      "id": "overview",
      "name": "Command centre",
      "appRoute": "#overview",
      "studies": [
        "overview-weather",
        "overview-merit",
        "overview-carbon",
        "overview-risk"
      ],
      "delivery": {
        "publicBrowser": "bundled synthetic examples",
        "newCalculations": "local Python application",
        "boundedTerminal": false
      },
      "contractUrl": "/models/#workspace-overview"
    },
    {
      "id": "storage",
      "name": "Storage & flexibility",
      "appRoute": "#storage",
      "studies": [
        "storage-degradation",
        "storage-stacking",
        "storage-synergy",
        "storage-annual",
        "storage-response",
        "storage-agingfit",
        "storage-annualopt"
      ],
      "delivery": {
        "publicBrowser": "bundled synthetic examples",
        "newCalculations": "local Python application",
        "boundedTerminal": false
      },
      "contractUrl": "/models/#workspace-storage"
    },
    {
      "id": "pv",
      "name": "PV economics",
      "appRoute": "#pv",
      "studies": [
        "pv-backtracking",
        "pv-agrivoltaic",
        "pv-albedo",
        "pv-sensitivity",
        "pv-reserve",
        "pv-raytrace",
        "pv-setbacks"
      ],
      "delivery": {
        "publicBrowser": "bundled synthetic examples",
        "newCalculations": "local Python application",
        "boundedTerminal": false
      },
      "contractUrl": "/models/#workspace-pv"
    },
    {
      "id": "research",
      "name": "Research pillars",
      "appRoute": "#research",
      "studies": [
        "research-vibration",
        "research-epd",
        "research-scr",
        "research-bearing",
        "research-eigenmodes"
      ],
      "delivery": {
        "publicBrowser": "bundled synthetic examples",
        "newCalculations": "local Python application",
        "boundedTerminal": false
      },
      "contractUrl": "/models/#workspace-research"
    },
    {
      "id": "markets",
      "name": "Market intelligence",
      "appRoute": "#markets",
      "studies": [
        "markets-capture",
        "markets-negative",
        "markets-interconnection",
        "markets-network"
      ],
      "delivery": {
        "publicBrowser": "bundled synthetic examples",
        "newCalculations": "local Python application",
        "boundedTerminal": false
      },
      "contractUrl": "/models/#workspace-markets"
    },
    {
      "id": "forecast",
      "name": "Forecast studio",
      "appRoute": "#forecast",
      "studies": [
        "forecast-regimes",
        "forecast-commodities",
        "forecast-tail",
        "forecast-cointegration",
        "forecast-backtest"
      ],
      "delivery": {
        "publicBrowser": "bundled synthetic examples",
        "newCalculations": "local Python application",
        "boundedTerminal": false
      },
      "contractUrl": "/models/#workspace-forecast"
    },
    {
      "id": "investment",
      "name": "Investment desk",
      "appRoute": "#investment",
      "studies": [
        "investment-sculpt",
        "investment-tranches",
        "investment-waterfall",
        "investment-contracts"
      ],
      "delivery": {
        "publicBrowser": "bundled synthetic examples",
        "newCalculations": "local Python application",
        "boundedTerminal": false
      },
      "contractUrl": "/models/#workspace-investment"
    },
    {
      "id": "pilots",
      "name": "Construction & pilots",
      "appRoute": "#pilots",
      "studies": [
        "pilots-photogrammetry",
        "pilots-dts",
        "pilots-offgas",
        "pilots-registration"
      ],
      "delivery": {
        "publicBrowser": "bundled synthetic examples",
        "newCalculations": "local Python application",
        "boundedTerminal": false
      },
      "contractUrl": "/models/#workspace-pilots"
    },
    {
      "id": "council",
      "name": "Agent council",
      "appRoute": "#council",
      "studies": [
        "council-debate",
        "council-evidence",
        "council-consensus"
      ],
      "delivery": {
        "publicBrowser": "bundled synthetic examples",
        "newCalculations": "local Python application",
        "boundedTerminal": false
      },
      "contractUrl": "/models/#workspace-council"
    }
  ],
  "studies": [
    {
      "id": "hybrid-resonance",
      "title": "Coupled oscillation modes",
      "summary": "Inspect poles and a normalized response of two coupled damped modes.",
      "workspace": {
        "id": "hybrid",
        "name": "Hybrid systems lab",
        "appRoute": "#hybrid"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "resonance",
        "equations": [
          "q'' + C q' + K q = u",
          "det(s²I + sC + K) = 0",
          "H(jω) = (K − ω²I + jωC)⁻¹"
        ],
        "limitations": [
          "Two-mode lumped oscillator proxy; parameters are illustrative, not measured inverter/network impedances.",
          "Pole screening is not EMT simulation, SSR diagnosis, protection coordination or stability certification."
        ],
        "sources": [
          {
            "title": "Tutorial on Sub-synchronous Resonance Protection Applied to Inverter Based Renewables (IEEE)",
            "url": "https://ieeexplore.ieee.org/document/9429840/"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "mode_1_hz",
            "label": "Mode 1 frequency",
            "default": 12,
            "min": 1,
            "max": 80,
            "step": 1,
            "unit": "Hz"
          },
          {
            "name": "mode_2_hz",
            "label": "Mode 2 frequency",
            "default": 24,
            "min": 1,
            "max": 100,
            "step": 1,
            "unit": "Hz"
          },
          {
            "name": "damping_1",
            "label": "Mode 1 damping ratio",
            "default": 0.04,
            "min": 0.01,
            "max": 0.45,
            "step": 0.01,
            "unit": "fraction"
          },
          {
            "name": "damping_2",
            "label": "Mode 2 damping ratio",
            "default": 0.07,
            "min": 0.01,
            "max": 0.45,
            "step": 0.01,
            "unit": "fraction"
          },
          {
            "name": "coupling_fraction",
            "label": "Coupling fraction",
            "default": 0.15,
            "min": 0,
            "max": 0.9,
            "step": 0.01,
            "unit": "fraction"
          }
        ],
        "jsonDataFields": []
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "42938f1b89cacfef9b64a8aee1cf84dcae30fbdb8c03cea50ab621aae2596df7",
        "evidenceSha256": "71b1d4b8286a9a41ad75590b7a75ba7a306ddd5e6f9ab7920970b499abc83095",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-resonance\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-resonance\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "At zero coupling, modal frequencies equal the two configured natural frequencies and both modes are stable.",
          "testIds": [
            {
              "node_id": "tests/test_engineering_studies.py::test_resonance_zero_coupling_retains_two_uncoupled_natural_modes",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "mode_1_hz",
              "unit": "Hz",
              "minimum": 1,
              "maximum": 80,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "mode_2_hz",
              "unit": "Hz",
              "minimum": 1,
              "maximum": 100,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "damping_1",
              "unit": "fraction",
              "minimum": 0.01,
              "maximum": 0.45,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "damping_2",
              "unit": "fraction",
              "minimum": 0.01,
              "maximum": 0.45,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "coupling_fraction",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 0.9,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [],
          "strictValidationReference": "apps/api/helioforge/engines/engineering_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "42938f1b89cacfef9b64a8aee1cf84dcae30fbdb8c03cea50ab621aae2596df7",
          "evidenceSha256": "71b1d4b8286a9a41ad75590b7a75ba7a306ddd5e6f9ab7920970b499abc83095",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-resonance\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-resonance\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "At zero coupling, modal frequencies equal the two configured natural frequencies and both modes are stable.",
            "testIds": [
              {
                "node_id": "tests/test_engineering_studies.py::test_resonance_zero_coupling_retains_two_uncoupled_natural_modes",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "mode_1_hz below declared minimum 1",
            "mode_1_hz above declared maximum 80",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "mode_1_hz=1 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Oscillatory pole count",
                  "value": 2,
                  "unit": "poles"
                },
                {
                  "label": "Minimum modal damping ratio",
                  "value": 0.04052355811086105,
                  "unit": "fraction"
                },
                {
                  "label": "Peak normalized response frequency",
                  "value": 1.108421052631579,
                  "unit": "Hz"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "e82fe70f49cb191416f337ad4b9865dc241f44a4ee112db2ed463efbcc840a26"
          }
        },
        "limitations": [
          "Two-mode lumped oscillator proxy; parameters are illustrative, not measured inverter/network impedances.",
          "Pole screening is not EMT simulation, SSR diagnosis, protection coordination or stability certification."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/engineering_studies.py",
          "tests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "hybrid-resonance",
            "workspace": "hybrid",
            "title": "Coupled oscillation modes",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_engineering_studies.py::test_resonance_zero_coupling_retains_two_uncoupled_natural_modes",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "At zero coupling, modal frequencies equal the two configured natural frequencies and both modes are stable.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Oscillatory pole count",
                        "value": 2,
                        "unit": "poles"
                      },
                      {
                        "label": "Minimum modal damping ratio",
                        "value": 0.039999999999999994,
                        "unit": "fraction"
                      },
                      {
                        "label": "Peak normalized response frequency",
                        "value": 12.201052631578946,
                        "unit": "Hz"
                      }
                    ],
                    "scalar_details": {
                      "mode_1_hz": 12,
                      "mode_2_hz": 24
                    },
                    "table_row_count": 0,
                    "result_sha256": "58e89a5e791cfc24d25642d594e16b581bda573ffed166d43a147f05f2a062bd"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Oscillatory pole count",
                    "value": 2,
                    "unit": "poles"
                  },
                  {
                    "label": "Minimum modal damping ratio",
                    "value": 0.04159205048957105,
                    "unit": "fraction"
                  },
                  {
                    "label": "Peak normalized response frequency",
                    "value": 11.696842105263157,
                    "unit": "Hz"
                  }
                ],
                "default_result_sha256": "e82fe70f49cb191416f337ad4b9865dc241f44a4ee112db2ed463efbcc840a26"
              },
              "units": "frequency / damping",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 3
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "mode_1_hz=1 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Oscillatory pole count",
                    "value": 2,
                    "unit": "poles"
                  },
                  {
                    "label": "Minimum modal damping ratio",
                    "value": 0.04052355811086105,
                    "unit": "fraction"
                  },
                  {
                    "label": "Peak normalized response frequency",
                    "value": 1.108421052631579,
                    "unit": "Hz"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "e82fe70f49cb191416f337ad4b9865dc241f44a4ee112db2ed463efbcc840a26"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/engineering_studies.py",
              "engine_sha256": "3cebc8222e67c45db07c0e288098f53735a4991e773e1cac13751372c9fec642",
              "test_file": "apps/api/tests/test_engineering_studies.py",
              "test_sha256": "240899798522fe3061c8aa817f59eeb91bf66ea1907e8b41163b16371913644b",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Two-mode lumped oscillator proxy; parameters are illustrative, not measured inverter/network impedances.",
              "Pole screening is not EMT simulation, SSR diagnosis, protection coordination or stability certification.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-hybrid-resonance"
    },
    {
      "id": "hybrid-clipping",
      "title": "DC clipping recovery",
      "summary": "Bound DC-coupled battery charging to PV inverter clipping, battery power and energy headroom.",
      "workspace": {
        "id": "hybrid",
        "name": "Hybrid systems lab",
        "appRoute": "#hybrid"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "clipping",
        "equations": [
          "Pdc,inverter limit = Pac,rated / ηinv",
          "Pclip = max(0, Ppv − Pdc,inverter limit)",
          "Pcharge = min(Pclip, Pcharge,max, (Emax − E0)/(ηcharge Δt))",
          "E1 = E0 + ηcharge Pcharge Δt"
        ],
        "limitations": [
          "Single interval, DC-side energy balance; no battery discharge, thermal limits, voltage-window or detailed inverter topology.",
          "Recovered energy is a physical screening quantity, not a dispatch forecast or project value."
        ],
        "sources": [
          {
            "title": "NREL System Advisor Model: PV and battery system models",
            "url": "https://sam.nrel.gov/photovoltaic.html"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "pv_dc_kw",
            "label": "Available PV DC",
            "default": 1400,
            "min": 0,
            "max": 100000,
            "step": 10,
            "unit": "kW"
          },
          {
            "name": "inverter_ac_kw",
            "label": "Inverter AC rating",
            "default": 1000,
            "min": 1,
            "max": 100000,
            "step": 10,
            "unit": "kW"
          },
          {
            "name": "inverter_efficiency",
            "label": "Inverter efficiency",
            "default": 0.98,
            "min": 0.01,
            "max": 1,
            "step": 0.01,
            "unit": "fraction"
          },
          {
            "name": "battery_capacity_kwh",
            "label": "Battery capacity",
            "default": 500,
            "min": 0,
            "max": 100000,
            "step": 10,
            "unit": "kWh"
          },
          {
            "name": "battery_initial_kwh",
            "label": "Initial battery energy",
            "default": 100,
            "min": 0,
            "max": 100000,
            "step": 10,
            "unit": "kWh"
          },
          {
            "name": "battery_charge_limit_kw",
            "label": "Battery charge limit",
            "default": 300,
            "min": 0,
            "max": 100000,
            "step": 10,
            "unit": "kW"
          }
        ],
        "jsonDataFields": []
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "2a6d7f71fc38b9a55d1cb9c9f8548eb89df74e9fc11242677bad3e16d49aa13a",
        "evidenceSha256": "3c1d95ca4a34e544e67df0ca8a41951c27be80ad3c95c034caf19508a0d95375",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-clipping\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-clipping\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "For the hand case, 2 kWh is recovered; unrecovered clipped DC is 400 - 2/0.96 kWh and the DC ledger residual is zero.",
          "testIds": [
            {
              "node_id": "tests/test_engineering_studies.py::test_clipping_recovery_is_limited_by_power_and_soc_headroom",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "pv_dc_kw",
              "unit": "kW",
              "minimum": 0,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "inverter_ac_kw",
              "unit": "kW",
              "minimum": 1,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "inverter_efficiency",
              "unit": "fraction",
              "minimum": 0.01,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "battery_capacity_kwh",
              "unit": "kWh",
              "minimum": 0,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "battery_initial_kwh",
              "unit": "kWh",
              "minimum": 0,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "battery_charge_limit_kw",
              "unit": "kW",
              "minimum": 0,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [],
          "strictValidationReference": "apps/api/helioforge/engines/engineering_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "2a6d7f71fc38b9a55d1cb9c9f8548eb89df74e9fc11242677bad3e16d49aa13a",
          "evidenceSha256": "3c1d95ca4a34e544e67df0ca8a41951c27be80ad3c95c034caf19508a0d95375",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-clipping\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-clipping\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "For the hand case, 2 kWh is recovered; unrecovered clipped DC is 400 - 2/0.96 kWh and the DC ledger residual is zero.",
            "testIds": [
              {
                "node_id": "tests/test_engineering_studies.py::test_clipping_recovery_is_limited_by_power_and_soc_headroom",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "pv_dc_kw below declared minimum 0",
            "pv_dc_kw above declared maximum 100000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "pv_dc_kw=0 (min bound)",
              "metric_snapshot": [
                {
                  "label": "DC-to-AC inverter loading ratio",
                  "value": 0,
                  "unit": "kWp/kWac"
                },
                {
                  "label": "PV direct AC energy",
                  "value": 0,
                  "unit": "kWh"
                },
                {
                  "label": "Clipped DC energy available",
                  "value": 0,
                  "unit": "kWh DC"
                },
                {
                  "label": "Clipped DC energy recovered",
                  "value": 0,
                  "unit": "kWh stored"
                },
                {
                  "label": "Unrecovered clipped DC energy",
                  "value": 0,
                  "unit": "kWh DC"
                },
                {
                  "label": "Ending battery energy",
                  "value": 100,
                  "unit": "kWh"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "e406c74a51d1ff4af871721fed7c4af511b2af6a266a8a610425741258ccc242"
          }
        },
        "limitations": [
          "Single interval, DC-side energy balance; no battery discharge, thermal limits, voltage-window or detailed inverter topology.",
          "Recovered energy is a physical screening quantity, not a dispatch forecast or project value."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/engineering_studies.py",
          "tests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "hybrid-clipping",
            "workspace": "hybrid",
            "title": "DC clipping recovery",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_engineering_studies.py::test_clipping_recovery_is_limited_by_power_and_soc_headroom",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "For the hand case, 2 kWh is recovered; unrecovered clipped DC is 400 - 2/0.96 kWh and the DC ledger residual is zero.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "DC-to-AC inverter loading ratio",
                        "value": 1.5,
                        "unit": "kWp/kWac"
                      },
                      {
                        "label": "PV direct AC energy",
                        "value": 800,
                        "unit": "kWh"
                      },
                      {
                        "label": "Clipped DC energy available",
                        "value": 400,
                        "unit": "kWh DC"
                      },
                      {
                        "label": "Clipped DC energy recovered",
                        "value": 2,
                        "unit": "kWh stored"
                      },
                      {
                        "label": "Unrecovered clipped DC energy",
                        "value": 397.9166666666667,
                        "unit": "kWh DC"
                      },
                      {
                        "label": "Ending battery energy",
                        "value": 100,
                        "unit": "kWh"
                      }
                    ],
                    "scalar_details": {
                      "inverter_dc_limit_kw": 800,
                      "inverter_dc_input_kw": 800,
                      "battery_charge_dc_kw": 2.0833333333333335,
                      "battery_stored_energy_gain_kwh": 2,
                      "dc_balance_residual_kwh": 0,
                      "fixed_interval_hours": 1,
                      "fixed_charge_efficiency": 0.96
                    },
                    "table_row_count": 4,
                    "result_sha256": "8b8a83b3281856cf9808df9c8c6330b711ce53de42bb9b6d82b5640109d87244"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "DC-to-AC inverter loading ratio",
                    "value": 1.4,
                    "unit": "kWp/kWac"
                  },
                  {
                    "label": "PV direct AC energy",
                    "value": 1000,
                    "unit": "kWh"
                  },
                  {
                    "label": "Clipped DC energy available",
                    "value": 379.59183673469386,
                    "unit": "kWh DC"
                  },
                  {
                    "label": "Clipped DC energy recovered",
                    "value": 288,
                    "unit": "kWh stored"
                  },
                  {
                    "label": "Unrecovered clipped DC energy",
                    "value": 79.59183673469386,
                    "unit": "kWh DC"
                  },
                  {
                    "label": "Ending battery energy",
                    "value": 388,
                    "unit": "kWh"
                  }
                ],
                "default_result_sha256": "e406c74a51d1ff4af871721fed7c4af511b2af6a266a8a610425741258ccc242"
              },
              "units": "kWh DC / kWh stored",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 6
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "pv_dc_kw=0 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "DC-to-AC inverter loading ratio",
                    "value": 0,
                    "unit": "kWp/kWac"
                  },
                  {
                    "label": "PV direct AC energy",
                    "value": 0,
                    "unit": "kWh"
                  },
                  {
                    "label": "Clipped DC energy available",
                    "value": 0,
                    "unit": "kWh DC"
                  },
                  {
                    "label": "Clipped DC energy recovered",
                    "value": 0,
                    "unit": "kWh stored"
                  },
                  {
                    "label": "Unrecovered clipped DC energy",
                    "value": 0,
                    "unit": "kWh DC"
                  },
                  {
                    "label": "Ending battery energy",
                    "value": 100,
                    "unit": "kWh"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "e406c74a51d1ff4af871721fed7c4af511b2af6a266a8a610425741258ccc242"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/engineering_studies.py",
              "engine_sha256": "3cebc8222e67c45db07c0e288098f53735a4991e773e1cac13751372c9fec642",
              "test_file": "apps/api/tests/test_engineering_studies.py",
              "test_sha256": "240899798522fe3061c8aa817f59eeb91bf66ea1907e8b41163b16371913644b",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Single interval, DC-side energy balance; no battery discharge, thermal limits, voltage-window or detailed inverter topology.",
              "Recovered energy is a physical screening quantity, not a dispatch forecast or project value.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-hybrid-clipping"
    },
    {
      "id": "hybrid-inertia",
      "title": "Synthetic inertia and RoCoF",
      "summary": "Integrate an aggregate swing model after a generation trip with bounded droop and derivative response.",
      "workspace": {
        "id": "hybrid",
        "name": "Hybrid systems lab",
        "appRoute": "#hybrid"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "frequency",
        "equations": [
          "df/dt = f0/(2 H Sbase) · (Pgov + Psyn − ΔPtrip − D Δf)",
          "Psyn = clip(−Msynthetic · df/dt, 0, Psynthetic,max)",
          "dPgov/dt = (clip(Kdroop · max(−Δf,0),0,Preserve) − Pgov)/τ"
        ],
        "limitations": [
          "Reduced-order aggregate frequency response; fixed trip and simplified governor/derivative service.",
          "Not an EMT simulator, protection study, grid-code assessment or certified inertia requirement."
        ],
        "sources": [
          {
            "title": "Fast Frequency Response Concepts and Bulk Power System Reliability Needs (NREL)",
            "url": "https://www.nlr.gov/grid/ieee-standard-1547/bulk-power-reliability-needs"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "trip_mw",
            "label": "Generation trip",
            "default": 1000,
            "min": 1,
            "max": 20000,
            "step": 50,
            "unit": "MW"
          },
          {
            "name": "system_base_mw",
            "label": "System base",
            "default": 100000,
            "min": 1000,
            "max": 2000000,
            "step": 1000,
            "unit": "MW"
          },
          {
            "name": "inertia_seconds",
            "label": "Aggregate inertia",
            "default": 4,
            "min": 0.1,
            "max": 30,
            "step": 0.1,
            "unit": "s"
          },
          {
            "name": "damping_mw_per_hz",
            "label": "Load damping",
            "default": 4000,
            "min": 0,
            "max": 500000,
            "step": 100,
            "unit": "MW/Hz"
          },
          {
            "name": "synthetic_inertia_mw_s_per_hz",
            "label": "Synthetic inertia gain",
            "default": 10000,
            "min": 0,
            "max": 200000,
            "step": 500,
            "unit": "MW·s/Hz"
          },
          {
            "name": "synthetic_inertia_limit_mw",
            "label": "Synthetic response limit",
            "default": 1000,
            "min": 0,
            "max": 100000,
            "step": 50,
            "unit": "MW"
          }
        ],
        "jsonDataFields": []
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "316334f9753f6d095fcf9abada759d47c2e6e2142af2c4384eb2c53e2f60a9ce",
        "evidenceSha256": "a58003de3f6d9d0c968ebb446cf31e90df0360136da53ccfeb946c79f2fb0a11",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-inertia\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-inertia\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Synthetic inertia improves initial RoCoF relative to zero inertia; frequency remains within the tested 40 Hz floor and RoCoF below 10 Hz/s.",
          "testIds": [
            {
              "node_id": "tests/test_engineering_studies.py::test_synthetic_inertia_reduces_initial_rocof_and_stays_bounded",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "trip_mw",
              "unit": "MW",
              "minimum": 1,
              "maximum": 20000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "system_base_mw",
              "unit": "MW",
              "minimum": 1000,
              "maximum": 2000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "inertia_seconds",
              "unit": "s",
              "minimum": 0.1,
              "maximum": 30,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "damping_mw_per_hz",
              "unit": "MW/Hz",
              "minimum": 0,
              "maximum": 500000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "synthetic_inertia_mw_s_per_hz",
              "unit": "MW·s/Hz",
              "minimum": 0,
              "maximum": 200000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "synthetic_inertia_limit_mw",
              "unit": "MW",
              "minimum": 0,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [],
          "strictValidationReference": "apps/api/helioforge/engines/engineering_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "316334f9753f6d095fcf9abada759d47c2e6e2142af2c4384eb2c53e2f60a9ce",
          "evidenceSha256": "a58003de3f6d9d0c968ebb446cf31e90df0360136da53ccfeb946c79f2fb0a11",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-inertia\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-inertia\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Synthetic inertia improves initial RoCoF relative to zero inertia; frequency remains within the tested 40 Hz floor and RoCoF below 10 Hz/s.",
            "testIds": [
              {
                "node_id": "tests/test_engineering_studies.py::test_synthetic_inertia_reduces_initial_rocof_and_stays_bounded",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "trip_mw below declared minimum 1",
            "trip_mw above declared maximum 20000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "trip_mw=1 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Initial RoCoF",
                  "value": -0.00003846153846153846,
                  "unit": "Hz/s"
                },
                {
                  "label": "Maximum absolute RoCoF",
                  "value": 0.00003846153846153846,
                  "unit": "Hz/s"
                },
                {
                  "label": "Minimum frequency",
                  "value": 49.999889032567594,
                  "unit": "Hz"
                },
                {
                  "label": "Frequency nadir deviation",
                  "value": 0.00011096743240557316,
                  "unit": "Hz"
                },
                {
                  "label": "Peak synthetic inertia response",
                  "value": 0.38461538461538464,
                  "unit": "MW"
                },
                {
                  "label": "Peak governor response",
                  "value": 0.5544234222137353,
                  "unit": "MW"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "dcffbe143e6bc15a8b17498308dbc7fcd5816f1a5f7091623b00c3bd1a44f70a"
          }
        },
        "limitations": [
          "Reduced-order aggregate frequency response; fixed trip and simplified governor/derivative service.",
          "Not an EMT simulator, protection study, grid-code assessment or certified inertia requirement."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/engineering_studies.py",
          "tests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "hybrid-inertia",
            "workspace": "hybrid",
            "title": "Synthetic inertia and RoCoF",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_engineering_studies.py::test_synthetic_inertia_reduces_initial_rocof_and_stays_bounded",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Synthetic inertia improves initial RoCoF relative to zero inertia; frequency remains within the tested 40 Hz floor and RoCoF below 10 Hz/s.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Initial RoCoF",
                        "value": -0.0625,
                        "unit": "Hz/s"
                      },
                      {
                        "label": "Maximum absolute RoCoF",
                        "value": 0.0625,
                        "unit": "Hz/s"
                      },
                      {
                        "label": "Minimum frequency",
                        "value": 49.88851312731623,
                        "unit": "Hz"
                      },
                      {
                        "label": "Frequency nadir deviation",
                        "value": 0.11148687268376989,
                        "unit": "Hz"
                      },
                      {
                        "label": "Peak synthetic inertia response",
                        "value": 0,
                        "unit": "MW"
                      },
                      {
                        "label": "Peak governor response",
                        "value": 556.593021596699,
                        "unit": "MW"
                      }
                    ],
                    "scalar_details": {
                      "integration": "fixed-step RK4",
                      "step_seconds": 0.02,
                      "horizon_seconds": 15,
                      "nominal_frequency_hz": 50,
                      "fixed_governor_droop_mw_per_hz": 5000,
                      "fixed_governor_reserve_mw": 1500,
                      "fixed_governor_time_constant_seconds": 0.8
                    },
                    "table_row_count": 0,
                    "result_sha256": "1c82ed6e5f0618c5b3b2ff2009966f1872e1e2c19d7e0c5409eac8dc0a2f607b"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Initial RoCoF",
                        "value": -0.038461538461538464,
                        "unit": "Hz/s"
                      },
                      {
                        "label": "Maximum absolute RoCoF",
                        "value": 0.038461538461538464,
                        "unit": "Hz/s"
                      },
                      {
                        "label": "Minimum frequency",
                        "value": 49.889032567591116,
                        "unit": "Hz"
                      },
                      {
                        "label": "Frequency nadir deviation",
                        "value": 0.11096743240888429,
                        "unit": "Hz"
                      },
                      {
                        "label": "Peak synthetic inertia response",
                        "value": 384.61538461538464,
                        "unit": "MW"
                      },
                      {
                        "label": "Peak governor response",
                        "value": 554.4234222137354,
                        "unit": "MW"
                      }
                    ],
                    "scalar_details": {
                      "integration": "fixed-step RK4",
                      "step_seconds": 0.02,
                      "horizon_seconds": 15,
                      "nominal_frequency_hz": 50,
                      "fixed_governor_droop_mw_per_hz": 5000,
                      "fixed_governor_reserve_mw": 1500,
                      "fixed_governor_time_constant_seconds": 0.8
                    },
                    "table_row_count": 0,
                    "result_sha256": "5d08b108232faea3e85c3298f783915f0be33263c95e9f7c51d9aed81dfdc445"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Initial RoCoF",
                    "value": -0.038461538461538464,
                    "unit": "Hz/s"
                  },
                  {
                    "label": "Maximum absolute RoCoF",
                    "value": 0.038461538461538464,
                    "unit": "Hz/s"
                  },
                  {
                    "label": "Minimum frequency",
                    "value": 49.889032567591116,
                    "unit": "Hz"
                  },
                  {
                    "label": "Frequency nadir deviation",
                    "value": 0.11096743240888429,
                    "unit": "Hz"
                  },
                  {
                    "label": "Peak synthetic inertia response",
                    "value": 384.61538461538464,
                    "unit": "MW"
                  },
                  {
                    "label": "Peak governor response",
                    "value": 554.4234222137354,
                    "unit": "MW"
                  }
                ],
                "default_result_sha256": "dcffbe143e6bc15a8b17498308dbc7fcd5816f1a5f7091623b00c3bd1a44f70a"
              },
              "units": "Hz / Hz/s",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 6
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "trip_mw=1 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Initial RoCoF",
                    "value": -0.00003846153846153846,
                    "unit": "Hz/s"
                  },
                  {
                    "label": "Maximum absolute RoCoF",
                    "value": 0.00003846153846153846,
                    "unit": "Hz/s"
                  },
                  {
                    "label": "Minimum frequency",
                    "value": 49.999889032567594,
                    "unit": "Hz"
                  },
                  {
                    "label": "Frequency nadir deviation",
                    "value": 0.00011096743240557316,
                    "unit": "Hz"
                  },
                  {
                    "label": "Peak synthetic inertia response",
                    "value": 0.38461538461538464,
                    "unit": "MW"
                  },
                  {
                    "label": "Peak governor response",
                    "value": 0.5544234222137353,
                    "unit": "MW"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "dcffbe143e6bc15a8b17498308dbc7fcd5816f1a5f7091623b00c3bd1a44f70a"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/engineering_studies.py",
              "engine_sha256": "3cebc8222e67c45db07c0e288098f53735a4991e773e1cac13751372c9fec642",
              "test_file": "apps/api/tests/test_engineering_studies.py",
              "test_sha256": "240899798522fe3061c8aa817f59eeb91bf66ea1907e8b41163b16371913644b",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Reduced-order aggregate frequency response; fixed trip and simplified governor/derivative service.",
              "Not an EMT simulator, protection study, grid-code assessment or certified inertia requirement.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-hybrid-inertia"
    },
    {
      "id": "storage-degradation",
      "title": "Separate battery aging mechanisms",
      "summary": "Compare uncalibrated calendar SEI, low-temperature plating and cycling-fatigue contributions.",
      "workspace": {
        "id": "storage",
        "name": "Storage & flexibility",
        "appRoute": "#storage"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "degradation",
        "equations": [
          "LSEI = kSEI √t · 2^((T−25°C)/10°C)",
          "Lplating = kplate · t · (C-rate/2)² · max(0,(15°C−T)/25°C)",
          "Lfatigue = kfatigue · EFC · (0.5 + 0.5 DOD)"
        ],
        "limitations": [
          "Mechanism terms are independently parameterized, additive screening proxies and intentionally uncalibrated.",
          "No cell chemistry, SOC history, charge protocol, coupled electrochemistry, uncertainty or warranty prediction."
        ],
        "sources": [
          {
            "title": "Coupling Lithium Plating with SEI Formation in a Pseudo-3D Model (Journal of The Electrochemical Society)",
            "url": "https://doi.org/10.1149/1945-7111/ac716a"
          },
          {
            "title": "A Generic Cycle Life Model Based on Fatigue Theory and Equivalent Cycle Counting (IEEE OJIES)",
            "url": "https://doi.org/10.1109/OJIES.2020.3015396"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "years",
            "label": "Elapsed time",
            "default": 8,
            "min": 0,
            "max": 30,
            "step": 1,
            "unit": "years"
          },
          {
            "name": "temperature_c",
            "label": "Mean temperature",
            "default": 25,
            "min": -20,
            "max": 60,
            "step": 1,
            "unit": "°C"
          },
          {
            "name": "charge_c_rate",
            "label": "Charge C-rate",
            "default": 1,
            "min": 0,
            "max": 5,
            "step": 0.1,
            "unit": "C"
          },
          {
            "name": "equivalent_full_cycles",
            "label": "Equivalent full cycles",
            "default": 2400,
            "min": 0,
            "max": 100000,
            "step": 100,
            "unit": "cycles"
          },
          {
            "name": "depth_of_discharge_fraction",
            "label": "Depth of discharge",
            "default": 0.8,
            "min": 0,
            "max": 1,
            "step": 0.01,
            "unit": "fraction"
          }
        ],
        "jsonDataFields": []
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "0e327f6bfce98f41564a501979eafb300224c6c0e8e6216fdb2082de033c5543",
        "evidenceSha256": "54ccb7e438d36c05ca7ba98dc9310bbc98f5492218d77ec6786b8f78d723cad8",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-degradation\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-degradation\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "SEI, plating and fatigue remain separate; hotter, colder/faster-charge and higher-cycle cases increase their corresponding loss terms.",
          "testIds": [
            {
              "node_id": "tests/test_engineering_studies.py::test_degradation_contributions_are_separate_and_increase_with_stress",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "years",
              "unit": "years",
              "minimum": 0,
              "maximum": 30,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "temperature_c",
              "unit": "°C",
              "minimum": -20,
              "maximum": 60,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "charge_c_rate",
              "unit": "C",
              "minimum": 0,
              "maximum": 5,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "equivalent_full_cycles",
              "unit": "cycles",
              "minimum": 0,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "depth_of_discharge_fraction",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [],
          "strictValidationReference": "apps/api/helioforge/engines/engineering_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "0e327f6bfce98f41564a501979eafb300224c6c0e8e6216fdb2082de033c5543",
          "evidenceSha256": "54ccb7e438d36c05ca7ba98dc9310bbc98f5492218d77ec6786b8f78d723cad8",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-degradation\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-degradation\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "SEI, plating and fatigue remain separate; hotter, colder/faster-charge and higher-cycle cases increase their corresponding loss terms.",
            "testIds": [
              {
                "node_id": "tests/test_engineering_studies.py::test_degradation_contributions_are_separate_and_increase_with_stress",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "years below declared minimum 0",
            "years above declared maximum 30",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "years=0 (min bound)",
              "metric_snapshot": [
                {
                  "label": "SEI calendar contribution",
                  "value": 0,
                  "unit": "% capacity"
                },
                {
                  "label": "Lithium-plating contribution",
                  "value": 0,
                  "unit": "% capacity"
                },
                {
                  "label": "Cycling-fatigue contribution",
                  "value": 0.432,
                  "unit": "% capacity"
                },
                {
                  "label": "Total modeled capacity loss",
                  "value": 0.432,
                  "unit": "% capacity"
                },
                {
                  "label": "Remaining normalized capacity",
                  "value": 0.99568,
                  "unit": "fraction"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "13c0e0ce5e6cd9a70ce97e9f25a0104225cbbf8a3f9d72aad3e04e09284edeb6"
          }
        },
        "limitations": [
          "Mechanism terms are independently parameterized, additive screening proxies and intentionally uncalibrated.",
          "No cell chemistry, SOC history, charge protocol, coupled electrochemistry, uncertainty or warranty prediction."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/engineering_studies.py",
          "tests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "storage-degradation",
            "workspace": "storage",
            "title": "Separate battery aging mechanisms",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_engineering_studies.py::test_degradation_contributions_are_separate_and_increase_with_stress",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "SEI, plating and fatigue remain separate; hotter, colder/faster-charge and higher-cycle cases increase their corresponding loss terms.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "SEI calendar contribution",
                        "value": 1.6970562748477143,
                        "unit": "% capacity"
                      },
                      {
                        "label": "Lithium-plating contribution",
                        "value": 0,
                        "unit": "% capacity"
                      },
                      {
                        "label": "Cycling-fatigue contribution",
                        "value": 0.432,
                        "unit": "% capacity"
                      },
                      {
                        "label": "Total modeled capacity loss",
                        "value": 2.129056274847714,
                        "unit": "% capacity"
                      },
                      {
                        "label": "Remaining normalized capacity",
                        "value": 0.9787094372515228,
                        "unit": "fraction"
                      }
                    ],
                    "scalar_details": {
                      "uncapped_sum_fraction": 0.021290562748477144,
                      "capacity_loss_cap_fraction": 0.95
                    },
                    "table_row_count": 3,
                    "result_sha256": "51dc24355fba62252b9ef493ffa833912dfb4cf66962cbe49f42294f2ec8cd6b"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "SEI calendar contribution",
                        "value": 0.42426406871192857,
                        "unit": "% capacity"
                      },
                      {
                        "label": "Lithium-plating contribution",
                        "value": 0.064,
                        "unit": "% capacity"
                      },
                      {
                        "label": "Cycling-fatigue contribution",
                        "value": 0.432,
                        "unit": "% capacity"
                      },
                      {
                        "label": "Total modeled capacity loss",
                        "value": 0.9202640687119287,
                        "unit": "% capacity"
                      },
                      {
                        "label": "Remaining normalized capacity",
                        "value": 0.9907973593128807,
                        "unit": "fraction"
                      }
                    ],
                    "scalar_details": {
                      "uncapped_sum_fraction": 0.009202640687119287,
                      "capacity_loss_cap_fraction": 0.95
                    },
                    "table_row_count": 3,
                    "result_sha256": "f146167b9af4d5fe3cdf90b7701b6a96ed68ecdec64da951b9633637bebe668b"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "SEI calendar contribution",
                        "value": 3.3941125496954285,
                        "unit": "% capacity"
                      },
                      {
                        "label": "Lithium-plating contribution",
                        "value": 0,
                        "unit": "% capacity"
                      },
                      {
                        "label": "Cycling-fatigue contribution",
                        "value": 0.432,
                        "unit": "% capacity"
                      },
                      {
                        "label": "Total modeled capacity loss",
                        "value": 3.8261125496954285,
                        "unit": "% capacity"
                      },
                      {
                        "label": "Remaining normalized capacity",
                        "value": 0.9617388745030457,
                        "unit": "fraction"
                      }
                    ],
                    "scalar_details": {
                      "uncapped_sum_fraction": 0.03826112549695428,
                      "capacity_loss_cap_fraction": 0.95
                    },
                    "table_row_count": 3,
                    "result_sha256": "8912a8efe28871c123cf025debdbae55023c061591a2be26ca2e05d927c6bc70"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "SEI calendar contribution",
                        "value": 1.6970562748477143,
                        "unit": "% capacity"
                      },
                      {
                        "label": "Lithium-plating contribution",
                        "value": 0,
                        "unit": "% capacity"
                      },
                      {
                        "label": "Cycling-fatigue contribution",
                        "value": 0.864,
                        "unit": "% capacity"
                      },
                      {
                        "label": "Total modeled capacity loss",
                        "value": 2.5610562748477146,
                        "unit": "% capacity"
                      },
                      {
                        "label": "Remaining normalized capacity",
                        "value": 0.9743894372515228,
                        "unit": "fraction"
                      }
                    ],
                    "scalar_details": {
                      "uncapped_sum_fraction": 0.025610562748477145,
                      "capacity_loss_cap_fraction": 0.95
                    },
                    "table_row_count": 3,
                    "result_sha256": "f5eb4259593ee8200f1f0ec63083a4f19fc9541562a8ba961faaa7fa11fd63cc"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "SEI calendar contribution",
                    "value": 1.6970562748477143,
                    "unit": "% capacity"
                  },
                  {
                    "label": "Lithium-plating contribution",
                    "value": 0,
                    "unit": "% capacity"
                  },
                  {
                    "label": "Cycling-fatigue contribution",
                    "value": 0.432,
                    "unit": "% capacity"
                  },
                  {
                    "label": "Total modeled capacity loss",
                    "value": 2.129056274847714,
                    "unit": "% capacity"
                  },
                  {
                    "label": "Remaining normalized capacity",
                    "value": 0.9787094372515228,
                    "unit": "fraction"
                  }
                ],
                "default_result_sha256": "13c0e0ce5e6cd9a70ce97e9f25a0104225cbbf8a3f9d72aad3e04e09284edeb6"
              },
              "units": "fraction / % capacity",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 5
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "years=0 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "SEI calendar contribution",
                    "value": 0,
                    "unit": "% capacity"
                  },
                  {
                    "label": "Lithium-plating contribution",
                    "value": 0,
                    "unit": "% capacity"
                  },
                  {
                    "label": "Cycling-fatigue contribution",
                    "value": 0.432,
                    "unit": "% capacity"
                  },
                  {
                    "label": "Total modeled capacity loss",
                    "value": 0.432,
                    "unit": "% capacity"
                  },
                  {
                    "label": "Remaining normalized capacity",
                    "value": 0.99568,
                    "unit": "fraction"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "13c0e0ce5e6cd9a70ce97e9f25a0104225cbbf8a3f9d72aad3e04e09284edeb6"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/engineering_studies.py",
              "engine_sha256": "3cebc8222e67c45db07c0e288098f53735a4991e773e1cac13751372c9fec642",
              "test_file": "apps/api/tests/test_engineering_studies.py",
              "test_sha256": "240899798522fe3061c8aa817f59eeb91bf66ea1907e8b41163b16371913644b",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Mechanism terms are independently parameterized, additive screening proxies and intentionally uncalibrated.",
              "No cell chemistry, SOC history, charge protocol, coupled electrochemistry, uncertainty or warranty prediction.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-storage-degradation"
    },
    {
      "id": "storage-stacking",
      "title": "Reserve and arbitrage headroom",
      "summary": "Bound an illustrative reserve-plus-arbitrage dispatch against explicit price and service arrays.",
      "workspace": {
        "id": "storage",
        "name": "Storage & flexibility",
        "appRoute": "#storage"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "stacking",
        "equations": [
          "maximize Σ(price·(d−c)·Δt/1000 + reserve_price·reserve_MW·Δt)",
          "SOC[t+1] = SOC[t] + ηc c[t]Δt − d[t]Δt/ηd",
          "SOC[T] = SOC[0]",
          "c + d + r_up + r_down ≤ P; reserve energy margins use stated activation duration"
        ],
        "limitations": [
          "Illustrative perfect-foresight MILP with conservative simultaneous headroom and one terminal-SOC constraint.",
          "Reserve quantities and prices are user assumptions; no market eligibility, bid acceptance, availability, penalties, taxes or annualization."
        ],
        "sources": [
          {
            "title": "NREL Annual Technology Baseline: storage uses and revenue streams",
            "url": "https://atb.nrel.gov/electricity/2024/utility-scale_battery_storage"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "capacity_kwh",
            "label": "Usable energy capacity",
            "default": 10000,
            "min": 1,
            "max": 1000000,
            "step": 100,
            "unit": "kWh"
          },
          {
            "name": "power_kw",
            "label": "Power rating",
            "default": 5000,
            "min": 1,
            "max": 500000,
            "step": 50,
            "unit": "kW"
          },
          {
            "name": "round_trip_efficiency",
            "label": "Round-trip efficiency",
            "default": 0.9,
            "min": 0.01,
            "max": 1,
            "step": 0.01,
            "unit": "fraction"
          },
          {
            "name": "initial_soc_fraction",
            "label": "Initial SOC",
            "default": 0.5,
            "min": 0,
            "max": 1,
            "step": 0.01,
            "unit": "fraction"
          },
          {
            "name": "interval_hours",
            "label": "Interval duration",
            "default": 1,
            "min": 0.25,
            "max": 4,
            "step": 0.25,
            "unit": "h"
          },
          {
            "name": "reserve_duration_hours",
            "label": "Reserve activation duration",
            "default": 0.25,
            "min": 0,
            "max": 4,
            "step": 0.25,
            "unit": "h"
          }
        ],
        "jsonDataFields": [
          {
            "name": "price_eur_mwh",
            "label": "Energy price (€/MWh)",
            "default": {
              "type": "array",
              "items": 24,
              "itemTypes": [
                "number"
              ],
              "sha256": "844e24698dd65f798e3c355854d4a0e0802790af6ba7cc45aa9a94f1fe3a3460"
            }
          },
          {
            "name": "reserve_up_price_eur_mw_h",
            "label": "Upward reserve price (€/MW·h)",
            "default": {
              "type": "array",
              "items": 24,
              "itemTypes": [
                "number"
              ],
              "sha256": "981850663d5fa1a4e59e941a2e92323fce262e4ba73097c57588d91561cdcf61"
            }
          },
          {
            "name": "reserve_down_price_eur_mw_h",
            "label": "Downward reserve price (€/MW·h)",
            "default": {
              "type": "array",
              "items": 24,
              "itemTypes": [
                "number"
              ],
              "sha256": "73f80dcafae74579d16db7d839a2dd3405a6a179167c9b3d5a942b29bbcbcf5c"
            }
          },
          {
            "name": "reserve_up_request_mw",
            "label": "Requested upward service (MW)",
            "default": {
              "type": "array",
              "items": 24,
              "itemTypes": [
                "number"
              ],
              "sha256": "e357056133590e45824e1a5be336becb75f41c002eb1ab689819a92d97a311a0"
            }
          },
          {
            "name": "reserve_down_request_mw",
            "label": "Requested downward service (MW)",
            "default": {
              "type": "array",
              "items": 24,
              "itemTypes": [
                "number"
              ],
              "sha256": "e357056133590e45824e1a5be336becb75f41c002eb1ab689819a92d97a311a0"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "5e6af3c975ecd3bba3181c766496fd3b1ca1b81f512783ace95083077614ecd4",
        "evidenceSha256": "2b0df56ab0c4825b42a13cdc4a0c3361acc31dd394a42a4c40492b4ad910e5dd",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-stacking\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-stacking\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "A 2 kWh/1 kW, two-period case returns to 0.2 kWh terminal SOC and earns 0.2 EUR under the stated price spread.",
          "testIds": [
            {
              "node_id": "tests/test_engineering_studies.py::test_storage_stacking_obeys_energy_power_and_terminal_soc",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "capacity_kwh",
              "unit": "kWh",
              "minimum": 1,
              "maximum": 1000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "power_kw",
              "unit": "kW",
              "minimum": 1,
              "maximum": 500000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "round_trip_efficiency",
              "unit": "fraction",
              "minimum": 0.01,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "initial_soc_fraction",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "interval_hours",
              "unit": "h",
              "minimum": 0.25,
              "maximum": 4,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "reserve_duration_hours",
              "unit": "h",
              "minimum": 0,
              "maximum": 4,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "price_eur_mwh",
              "label": "Energy price (€/MWh)",
              "default": {
                "type": "array",
                "items": 24,
                "itemTypes": [
                  "number"
                ],
                "sha256": "844e24698dd65f798e3c355854d4a0e0802790af6ba7cc45aa9a94f1fe3a3460"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "reserve_up_price_eur_mw_h",
              "label": "Upward reserve price (€/MW·h)",
              "default": {
                "type": "array",
                "items": 24,
                "itemTypes": [
                  "number"
                ],
                "sha256": "981850663d5fa1a4e59e941a2e92323fce262e4ba73097c57588d91561cdcf61"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "reserve_down_price_eur_mw_h",
              "label": "Downward reserve price (€/MW·h)",
              "default": {
                "type": "array",
                "items": 24,
                "itemTypes": [
                  "number"
                ],
                "sha256": "73f80dcafae74579d16db7d839a2dd3405a6a179167c9b3d5a942b29bbcbcf5c"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "reserve_up_request_mw",
              "label": "Requested upward service (MW)",
              "default": {
                "type": "array",
                "items": 24,
                "itemTypes": [
                  "number"
                ],
                "sha256": "e357056133590e45824e1a5be336becb75f41c002eb1ab689819a92d97a311a0"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "reserve_down_request_mw",
              "label": "Requested downward service (MW)",
              "default": {
                "type": "array",
                "items": 24,
                "itemTypes": [
                  "number"
                ],
                "sha256": "e357056133590e45824e1a5be336becb75f41c002eb1ab689819a92d97a311a0"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/engineering_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "5e6af3c975ecd3bba3181c766496fd3b1ca1b81f512783ace95083077614ecd4",
          "evidenceSha256": "2b0df56ab0c4825b42a13cdc4a0c3361acc31dd394a42a4c40492b4ad910e5dd",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-stacking\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-stacking\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "A 2 kWh/1 kW, two-period case returns to 0.2 kWh terminal SOC and earns 0.2 EUR under the stated price spread.",
            "testIds": [
              {
                "node_id": "tests/test_engineering_studies.py::test_storage_stacking_obeys_energy_power_and_terminal_soc",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "capacity_kwh below declared minimum 1",
            "capacity_kwh above declared maximum 1000000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "capacity_kwh=1 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Optimized arbitrage revenue",
                  "value": -0.0018552028939658616,
                  "unit": "EUR"
                },
                {
                  "label": "Illustrative upward reserve revenue",
                  "value": 1.2841377122411688,
                  "unit": "EUR"
                },
                {
                  "label": "Illustrative downward reserve revenue",
                  "value": 0.020238577025082416,
                  "unit": "EUR"
                },
                {
                  "label": "Total modeled gross value",
                  "value": 1.3025210863722854,
                  "unit": "EUR"
                },
                {
                  "label": "Terminal SOC",
                  "value": 0.5,
                  "unit": "kWh"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {
            "solver_status": "optimal"
          },
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "PASS",
          "actual": "optimal"
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "2f8c01ae2b04e2fad4398c886a14de05be2b98555bb3a66480463510ab6a3704"
          }
        },
        "limitations": [
          "Illustrative perfect-foresight MILP with conservative simultaneous headroom and one terminal-SOC constraint.",
          "Reserve quantities and prices are user assumptions; no market eligibility, bid acceptance, availability, penalties, taxes or annualization."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/engineering_studies.py",
          "tests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "storage-stacking",
            "workspace": "storage",
            "title": "Reserve and arbitrage headroom",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_engineering_studies.py::test_storage_stacking_obeys_energy_power_and_terminal_soc",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "A 2 kWh/1 kW, two-period case returns to 0.2 kWh terminal SOC and earns 0.2 EUR under the stated price spread.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Optimized arbitrage revenue",
                        "value": 0.19999999999999998,
                        "unit": "EUR"
                      },
                      {
                        "label": "Illustrative upward reserve revenue",
                        "value": 0,
                        "unit": "EUR"
                      },
                      {
                        "label": "Illustrative downward reserve revenue",
                        "value": 0,
                        "unit": "EUR"
                      },
                      {
                        "label": "Total modeled gross value",
                        "value": 0.19999999999999998,
                        "unit": "EUR"
                      },
                      {
                        "label": "Terminal SOC",
                        "value": 0.2,
                        "unit": "kWh"
                      }
                    ],
                    "scalar_details": {
                      "duration_hours": 2,
                      "solver": "SciPy/HiGHS MILP",
                      "solver_status": "optimal",
                      "initial_soc_kwh": 0.2,
                      "terminal_soc_kwh": 0.2,
                      "power_limit_mw": 0.001,
                      "reserve_activation_duration_hours": 0.25
                    },
                    "table_row_count": 2,
                    "result_sha256": "9790bd4945c2d2b62ce4fdab2a0d748c52ef6a24e7db118e721e6e810a0bc3c5"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Optimized arbitrage revenue",
                    "value": 798.3553452803653,
                    "unit": "EUR"
                  },
                  {
                    "label": "Illustrative upward reserve revenue",
                    "value": 108,
                    "unit": "EUR"
                  },
                  {
                    "label": "Illustrative downward reserve revenue",
                    "value": 69,
                    "unit": "EUR"
                  },
                  {
                    "label": "Total modeled gross value",
                    "value": 975.3553452803653,
                    "unit": "EUR"
                  },
                  {
                    "label": "Terminal SOC",
                    "value": 5000,
                    "unit": "kWh"
                  }
                ],
                "default_result_sha256": "2f8c01ae2b04e2fad4398c886a14de05be2b98555bb3a66480463510ab6a3704"
              },
              "units": "kWh / EUR",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 5
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "capacity_kwh=1 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Optimized arbitrage revenue",
                    "value": -0.0018552028939658616,
                    "unit": "EUR"
                  },
                  {
                    "label": "Illustrative upward reserve revenue",
                    "value": 1.2841377122411688,
                    "unit": "EUR"
                  },
                  {
                    "label": "Illustrative downward reserve revenue",
                    "value": 0.020238577025082416,
                    "unit": "EUR"
                  },
                  {
                    "label": "Total modeled gross value",
                    "value": 1.3025210863722854,
                    "unit": "EUR"
                  },
                  {
                    "label": "Terminal SOC",
                    "value": 0.5,
                    "unit": "kWh"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "2f8c01ae2b04e2fad4398c886a14de05be2b98555bb3a66480463510ab6a3704"
              },
              "solver_feasibility": {
                "status": "PASS",
                "actual": "optimal"
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/engineering_studies.py",
              "engine_sha256": "3cebc8222e67c45db07c0e288098f53735a4991e773e1cac13751372c9fec642",
              "test_file": "apps/api/tests/test_engineering_studies.py",
              "test_sha256": "240899798522fe3061c8aa817f59eeb91bf66ea1907e8b41163b16371913644b",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Illustrative perfect-foresight MILP with conservative simultaneous headroom and one terminal-SOC constraint.",
              "Reserve quantities and prices are user assumptions; no market eligibility, bid acceptance, availability, penalties, taxes or annualization.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-storage-stacking"
    },
    {
      "id": "storage-synergy",
      "title": "Flywheel and battery split-frequency dispatch",
      "summary": "Route high-frequency disturbance to a flywheel and the residual to a bounded battery, tracking both energy states.",
      "workspace": {
        "id": "storage",
        "name": "Storage & flexibility",
        "appRoute": "#storage"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "synergy",
        "equations": [
          "ℓ[t] = ℓ[t−1] + α(P[t]−ℓ[t−1]); α = Δt/(τ+Δt)",
          "Pflywheel,request = P−ℓ; Pbattery,request = P−Pflywheel",
          "E[t+1] = E[t] − PΔt/ηd for P≥0; E[t+1] = E[t] − PηcΔt for P<0"
        ],
        "limitations": [
          "A first-order frequency split and sequential clipping are transparent dispatch proxies, not an optimized controller.",
          "No voltage, thermal, ramp, converter, degradation, coupled control or field response validation."
        ],
        "sources": [
          {
            "title": "DOE Energy Storage: Technology Basics",
            "url": "https://www.energy.gov/oe/energy-storage"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "interval_seconds",
            "label": "Sample interval",
            "default": 1,
            "min": 0.1,
            "max": 60,
            "step": 0.1,
            "unit": "s"
          },
          {
            "name": "filter_time_constant_s",
            "label": "Frequency split time constant",
            "default": 4,
            "min": 0,
            "max": 600,
            "step": 0.5,
            "unit": "s"
          },
          {
            "name": "flywheel_power_kw",
            "label": "Flywheel power limit",
            "default": 500,
            "min": 0,
            "max": 100000,
            "step": 10,
            "unit": "kW"
          },
          {
            "name": "flywheel_energy_kwh",
            "label": "Flywheel energy capacity",
            "default": 20,
            "min": 0.1,
            "max": 100000,
            "step": 1,
            "unit": "kWh"
          },
          {
            "name": "battery_power_kw",
            "label": "Battery power limit",
            "default": 1000,
            "min": 0,
            "max": 500000,
            "step": 10,
            "unit": "kW"
          },
          {
            "name": "battery_energy_kwh",
            "label": "Battery energy capacity",
            "default": 4000,
            "min": 0.1,
            "max": 1000000,
            "step": 10,
            "unit": "kWh"
          }
        ],
        "jsonDataFields": [
          {
            "name": "disturbance_kw",
            "label": "Requested power trace (+ discharge / − charge; ±1 GW/sample)",
            "default": {
              "type": "array",
              "items": 8,
              "itemTypes": [
                "number"
              ],
              "sha256": "b97931082cf8195d983b72773f12089418179ed8eada57d21149241538cbfa7b"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "226e32f06082e55b9b2039b8410779b59459e52f75544ff667cdab1203a72909",
        "evidenceSha256": "e99620988ffbb162eba471bdda39280bd7ae66b96e8759b6942946c94d362678",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-synergy\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-synergy\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "The small split-storage case has zero power-balance residual and zero unmet energy within the tested tolerance.",
          "testIds": [
            {
              "node_id": "tests/test_engineering_studies.py::test_hybrid_storage_split_conserves_energy_and_reports_unmet_power",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "interval_seconds",
              "unit": "s",
              "minimum": 0.1,
              "maximum": 60,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "filter_time_constant_s",
              "unit": "s",
              "minimum": 0,
              "maximum": 600,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "flywheel_power_kw",
              "unit": "kW",
              "minimum": 0,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "flywheel_energy_kwh",
              "unit": "kWh",
              "minimum": 0.1,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "battery_power_kw",
              "unit": "kW",
              "minimum": 0,
              "maximum": 500000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "battery_energy_kwh",
              "unit": "kWh",
              "minimum": 0.1,
              "maximum": 1000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "disturbance_kw",
              "label": "Requested power trace (+ discharge / − charge; ±1 GW/sample)",
              "default": {
                "type": "array",
                "items": 8,
                "itemTypes": [
                  "number"
                ],
                "sha256": "b97931082cf8195d983b72773f12089418179ed8eada57d21149241538cbfa7b"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/engineering_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "226e32f06082e55b9b2039b8410779b59459e52f75544ff667cdab1203a72909",
          "evidenceSha256": "e99620988ffbb162eba471bdda39280bd7ae66b96e8759b6942946c94d362678",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-synergy\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-synergy\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "The small split-storage case has zero power-balance residual and zero unmet energy within the tested tolerance.",
            "testIds": [
              {
                "node_id": "tests/test_engineering_studies.py::test_hybrid_storage_split_conserves_energy_and_reports_unmet_power",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "interval_seconds below declared minimum 0.1",
            "interval_seconds above declared maximum 60",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "interval_seconds=0.1 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Peak flywheel power",
                  "value": 386.6745984533016,
                  "unit": "kW"
                },
                {
                  "label": "Peak battery power",
                  "value": 17.878440533364284,
                  "unit": "kW"
                },
                {
                  "label": "Peak unmet power",
                  "value": 0,
                  "unit": "kW"
                },
                {
                  "label": "Unmet power energy",
                  "value": 0,
                  "unit": "kWh"
                },
                {
                  "label": "Ending flywheel energy",
                  "value": 9.983185792980935,
                  "unit": "kWh"
                },
                {
                  "label": "Ending battery energy",
                  "value": 1999.9976511701034,
                  "unit": "kWh"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "4a69d1e18da7fe0c04dd0a255c9f3d9aaa7a68326e864bbc2557d21526b2eeae"
          }
        },
        "limitations": [
          "A first-order frequency split and sequential clipping are transparent dispatch proxies, not an optimized controller.",
          "No voltage, thermal, ramp, converter, degradation, coupled control or field response validation."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/engineering_studies.py",
          "tests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "storage-synergy",
            "workspace": "storage",
            "title": "Flywheel and battery split-frequency dispatch",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_engineering_studies.py::test_hybrid_storage_split_conserves_energy_and_reports_unmet_power",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "The small split-storage case has zero power-balance residual and zero unmet energy within the tested tolerance.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Peak flywheel power",
                        "value": 100,
                        "unit": "kW"
                      },
                      {
                        "label": "Peak battery power",
                        "value": 36,
                        "unit": "kW"
                      },
                      {
                        "label": "Peak unmet power",
                        "value": 0,
                        "unit": "kW"
                      },
                      {
                        "label": "Unmet power energy",
                        "value": 0,
                        "unit": "kWh"
                      },
                      {
                        "label": "Ending flywheel energy",
                        "value": 0.04261395608873385,
                        "unit": "kWh"
                      },
                      {
                        "label": "Ending battery energy",
                        "value": 0.4754416356777223,
                        "unit": "kWh"
                      }
                    ],
                    "scalar_details": {
                      "maximum_power_balance_residual_kw": 0,
                      "sample_interval_seconds": 1,
                      "split_time_constant_seconds": 4,
                      "fixed_initial_soc_fraction": 0.5,
                      "fixed_round_trip_efficiency": 0.92
                    },
                    "table_row_count": 0,
                    "result_sha256": "93addd109a0d6945572cb978587cdba90713565b0a3250ae2f8d2ef186942271"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Peak flywheel power",
                    "value": 296,
                    "unit": "kW"
                  },
                  {
                    "label": "Peak battery power",
                    "value": 123.2,
                    "unit": "kW"
                  },
                  {
                    "label": "Peak unmet power",
                    "value": 0,
                    "unit": "kW"
                  },
                  {
                    "label": "Unmet power energy",
                    "value": 0,
                    "unit": "kWh"
                  },
                  {
                    "label": "Ending flywheel energy",
                    "value": 9.938409327464269,
                    "unit": "kWh"
                  },
                  {
                    "label": "Ending battery energy",
                    "value": 1999.8662940705917,
                    "unit": "kWh"
                  }
                ],
                "default_result_sha256": "4a69d1e18da7fe0c04dd0a255c9f3d9aaa7a68326e864bbc2557d21526b2eeae"
              },
              "units": "kW / kWh",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 6
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "interval_seconds=0.1 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Peak flywheel power",
                    "value": 386.6745984533016,
                    "unit": "kW"
                  },
                  {
                    "label": "Peak battery power",
                    "value": 17.878440533364284,
                    "unit": "kW"
                  },
                  {
                    "label": "Peak unmet power",
                    "value": 0,
                    "unit": "kW"
                  },
                  {
                    "label": "Unmet power energy",
                    "value": 0,
                    "unit": "kWh"
                  },
                  {
                    "label": "Ending flywheel energy",
                    "value": 9.983185792980935,
                    "unit": "kWh"
                  },
                  {
                    "label": "Ending battery energy",
                    "value": 1999.9976511701034,
                    "unit": "kWh"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "4a69d1e18da7fe0c04dd0a255c9f3d9aaa7a68326e864bbc2557d21526b2eeae"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/engineering_studies.py",
              "engine_sha256": "3cebc8222e67c45db07c0e288098f53735a4991e773e1cac13751372c9fec642",
              "test_file": "apps/api/tests/test_engineering_studies.py",
              "test_sha256": "240899798522fe3061c8aa817f59eeb91bf66ea1907e8b41163b16371913644b",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "A first-order frequency split and sequential clipping are transparent dispatch proxies, not an optimized controller.",
              "No voltage, thermal, ramp, converter, degradation, coupled control or field response validation.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-storage-synergy"
    },
    {
      "id": "pv-backtracking",
      "title": "Slope-aware tracker backtracking screen",
      "summary": "Compare ideal tracker angles with a planar row-shadow backtracking correction over supplied sun positions.",
      "workspace": {
        "id": "pv",
        "name": "PV economics",
        "appRoute": "#pv"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "tracker",
        "equations": [
          "βcross = atan(tan(slope) · sin(azslope − azaxis))",
          "θideal = atan2(sun_cross · cosβ + sun_up · sinβ, −sun_cross · sinβ + sun_up · cosβ)",
          "θbt = θideal − sign(θideal) arccos(|cos θideal|/GCR) when |cos θideal| < GCR"
        ],
        "limitations": [
          "A two-dimensional planar row geometry with a cross-slope projection; edge effects, tracker limits and terrain variation are omitted.",
          "NREL's slope-aware method includes full reference frames and shaded-fraction treatment; this screen is not its implementation or an energy-yield model."
        ],
        "sources": [
          {
            "title": "Slope-Aware Backtracking for Single-Axis Trackers (NREL/TP-5K00-76626)",
            "url": "https://docs.nrel.gov/docs/fy20osti/76626.pdf"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "row_pitch_m",
            "label": "Row pitch",
            "default": 6,
            "min": 0.1,
            "max": 30,
            "step": 0.1,
            "unit": "m"
          },
          {
            "name": "module_width_m",
            "label": "Collector width",
            "default": 2.4,
            "min": 0.1,
            "max": 10,
            "step": 0.1,
            "unit": "m"
          },
          {
            "name": "tracker_axis_azimuth_deg",
            "label": "Tracker-axis azimuth",
            "default": 0,
            "min": 0,
            "max": 360,
            "step": 1,
            "unit": "°"
          },
          {
            "name": "terrain_slope_deg",
            "label": "Terrain slope",
            "default": 5,
            "min": 0,
            "max": 30,
            "step": 1,
            "unit": "°"
          },
          {
            "name": "terrain_slope_azimuth_deg",
            "label": "Terrain downslope azimuth",
            "default": 90,
            "min": 0,
            "max": 360,
            "step": 1,
            "unit": "°"
          },
          {
            "name": "backtracking",
            "label": "Apply backtracking correction",
            "default": 1,
            "min": 0,
            "max": 1,
            "step": 1,
            "unit": "0/1"
          }
        ],
        "jsonDataFields": [
          {
            "name": "sun_elevation_deg",
            "label": "Solar elevation (°)",
            "default": {
              "type": "array",
              "items": 11,
              "itemTypes": [
                "number"
              ],
              "sha256": "a96c29e5705e5226962245cb55357ad39c081f258bfb1ed54e3d2e71fa670cd0"
            }
          },
          {
            "name": "sun_azimuth_deg",
            "label": "Solar azimuth (°)",
            "default": {
              "type": "array",
              "items": 11,
              "itemTypes": [
                "number"
              ],
              "sha256": "618f795cffa2937872e0c30dd674912ccf280794edab53e1de71ea89dfba8c58"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "5a6f3cc3d5a62edbed36b0d14b18e55441730e10992058fb7c61aae07204abfe",
        "evidenceSha256": "ecd238293d7983a546f8804647ee91e60d4366c043aa59d7fe643ef90ad76eb7",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-backtracking\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-backtracking\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Turning backtracking on does not increase mean modeled row shading relative to the same planar case with backtracking off.",
          "testIds": [
            {
              "node_id": "tests/test_engineering_studies.py::test_backtracking_reduces_or_preserves_planar_row_shading",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "row_pitch_m",
              "unit": "m",
              "minimum": 0.1,
              "maximum": 30,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "module_width_m",
              "unit": "m",
              "minimum": 0.1,
              "maximum": 10,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "tracker_axis_azimuth_deg",
              "unit": "°",
              "minimum": 0,
              "maximum": 360,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "terrain_slope_deg",
              "unit": "°",
              "minimum": 0,
              "maximum": 30,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "terrain_slope_azimuth_deg",
              "unit": "°",
              "minimum": 0,
              "maximum": 360,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "backtracking",
              "unit": "0/1",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "sun_elevation_deg",
              "label": "Solar elevation (°)",
              "default": {
                "type": "array",
                "items": 11,
                "itemTypes": [
                  "number"
                ],
                "sha256": "a96c29e5705e5226962245cb55357ad39c081f258bfb1ed54e3d2e71fa670cd0"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "sun_azimuth_deg",
              "label": "Solar azimuth (°)",
              "default": {
                "type": "array",
                "items": 11,
                "itemTypes": [
                  "number"
                ],
                "sha256": "618f795cffa2937872e0c30dd674912ccf280794edab53e1de71ea89dfba8c58"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/engineering_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "5a6f3cc3d5a62edbed36b0d14b18e55441730e10992058fb7c61aae07204abfe",
          "evidenceSha256": "ecd238293d7983a546f8804647ee91e60d4366c043aa59d7fe643ef90ad76eb7",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-backtracking\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-backtracking\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Turning backtracking on does not increase mean modeled row shading relative to the same planar case with backtracking off.",
            "testIds": [
              {
                "node_id": "tests/test_engineering_studies.py::test_backtracking_reduces_or_preserves_planar_row_shading",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "row_pitch_m below declared minimum 0.1",
            "row_pitch_m above declared maximum 30",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "row_pitch_m=30 (max bound)",
              "metric_snapshot": [
                {
                  "label": "Ground coverage ratio",
                  "value": 0.08,
                  "unit": "fraction"
                },
                {
                  "label": "Mean ideal tracker angle",
                  "value": 5.0000000000000036,
                  "unit": "°"
                },
                {
                  "label": "Mean corrected tracker angle",
                  "value": -3.0946175248863312,
                  "unit": "°"
                },
                {
                  "label": "Mean row-shaded fraction",
                  "value": 0,
                  "unit": "fraction"
                },
                {
                  "label": "Maximum row-shaded fraction",
                  "value": 0,
                  "unit": "fraction"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "f4481d1087d5379f5017160697443a2e31664c85530d763b0f386e55cceae972"
          }
        },
        "limitations": [
          "A two-dimensional planar row geometry with a cross-slope projection; edge effects, tracker limits and terrain variation are omitted.",
          "NREL's slope-aware method includes full reference frames and shaded-fraction treatment; this screen is not its implementation or an energy-yield model."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/engineering_studies.py",
          "tests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "pv-backtracking",
            "workspace": "pv",
            "title": "Slope-aware tracker backtracking screen",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_engineering_studies.py::test_backtracking_reduces_or_preserves_planar_row_shading",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Turning backtracking on does not increase mean modeled row shading relative to the same planar case with backtracking off.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Ground coverage ratio",
                        "value": 0.39999999999999997,
                        "unit": "fraction"
                      },
                      {
                        "label": "Mean ideal tracker angle",
                        "value": 5.0000000000000036,
                        "unit": "°"
                      },
                      {
                        "label": "Mean corrected tracker angle",
                        "value": 5.0000000000000036,
                        "unit": "°"
                      },
                      {
                        "label": "Mean row-shaded fraction",
                        "value": 0.4501469447139968,
                        "unit": "fraction"
                      },
                      {
                        "label": "Maximum row-shaded fraction",
                        "value": 1,
                        "unit": "fraction"
                      }
                    ],
                    "scalar_details": {
                      "cross_axis_slope_deg": 5,
                      "ground_coverage_ratio": 0.39999999999999997
                    },
                    "table_row_count": 11,
                    "result_sha256": "1e6cd0d1eb25138f27167bb27b527b2f7d159eb21288be3e315e56705721b42a"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Ground coverage ratio",
                        "value": 0.39999999999999997,
                        "unit": "fraction"
                      },
                      {
                        "label": "Mean ideal tracker angle",
                        "value": 5.0000000000000036,
                        "unit": "°"
                      },
                      {
                        "label": "Mean corrected tracker angle",
                        "value": -2.5626653545843223,
                        "unit": "°"
                      },
                      {
                        "label": "Mean row-shaded fraction",
                        "value": 2.682605013415749e-15,
                        "unit": "fraction"
                      },
                      {
                        "label": "Maximum row-shaded fraction",
                        "value": 2.7755631620401982e-14,
                        "unit": "fraction"
                      }
                    ],
                    "scalar_details": {
                      "cross_axis_slope_deg": 5,
                      "ground_coverage_ratio": 0.39999999999999997
                    },
                    "table_row_count": 11,
                    "result_sha256": "f3c52c6d1b8ecd1458b1c8876d0dcbfa0f6891d508dbc6381aed1e1eb7688222"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Ground coverage ratio",
                    "value": 0.39999999999999997,
                    "unit": "fraction"
                  },
                  {
                    "label": "Mean ideal tracker angle",
                    "value": 5.0000000000000036,
                    "unit": "°"
                  },
                  {
                    "label": "Mean corrected tracker angle",
                    "value": -2.5626653545843223,
                    "unit": "°"
                  },
                  {
                    "label": "Mean row-shaded fraction",
                    "value": 2.682605013415749e-15,
                    "unit": "fraction"
                  },
                  {
                    "label": "Maximum row-shaded fraction",
                    "value": 2.7755631620401982e-14,
                    "unit": "fraction"
                  }
                ],
                "default_result_sha256": "f4481d1087d5379f5017160697443a2e31664c85530d763b0f386e55cceae972"
              },
              "units": "fraction",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 5
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "row_pitch_m=30 (max bound)",
                "metric_snapshot": [
                  {
                    "label": "Ground coverage ratio",
                    "value": 0.08,
                    "unit": "fraction"
                  },
                  {
                    "label": "Mean ideal tracker angle",
                    "value": 5.0000000000000036,
                    "unit": "°"
                  },
                  {
                    "label": "Mean corrected tracker angle",
                    "value": -3.0946175248863312,
                    "unit": "°"
                  },
                  {
                    "label": "Mean row-shaded fraction",
                    "value": 0,
                    "unit": "fraction"
                  },
                  {
                    "label": "Maximum row-shaded fraction",
                    "value": 0,
                    "unit": "fraction"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "f4481d1087d5379f5017160697443a2e31664c85530d763b0f386e55cceae972"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/engineering_studies.py",
              "engine_sha256": "3cebc8222e67c45db07c0e288098f53735a4991e773e1cac13751372c9fec642",
              "test_file": "apps/api/tests/test_engineering_studies.py",
              "test_sha256": "240899798522fe3061c8aa817f59eeb91bf66ea1907e8b41163b16371913644b",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "A two-dimensional planar row geometry with a cross-slope projection; edge effects, tracker limits and terrain variation are omitted.",
              "NREL's slope-aware method includes full reference frames and shaded-fraction treatment; this screen is not its implementation or an energy-yield model.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-pv-backtracking"
    },
    {
      "id": "pv-agrivoltaic",
      "title": "Agrivoltaic PAR and land geometry",
      "summary": "Screen a simple crop-light tradeoff and polygon land area after a conservative perimeter setback deduction.",
      "workspace": {
        "id": "pv",
        "name": "PV economics",
        "appRoute": "#pv"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "agrivoltaic",
        "equations": [
          "A = ½ |Σ(xᵢyᵢ₊₁ − xᵢ₊₁yᵢ)|",
          "Ausable ≈ max(0, A − perimeter·setback)",
          "PARcrop ≈ PARopen · [1 − GCR · (1 − module transmission)]"
        ],
        "limitations": [
          "Perimeter setback subtraction is a conservative area proxy, not an exact polygon offset or cadastral calculation.",
          "PAR proxy omits time-of-day geometry, crop response, spectral effects, diffuse light, row spacing, equipment access and yield validation."
        ],
        "sources": [
          {
            "title": "Adapting Agrivoltaics (NREL)",
            "url": "https://www.nrel.gov/docs/fy24osti/88444.pdf"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "ground_coverage_ratio",
            "label": "Ground coverage ratio",
            "default": 0.35,
            "min": 0,
            "max": 0.9,
            "step": 0.01,
            "unit": "fraction"
          },
          {
            "name": "module_transmission_fraction",
            "label": "Module PAR transmission",
            "default": 0.08,
            "min": 0,
            "max": 0.8,
            "step": 0.01,
            "unit": "fraction"
          },
          {
            "name": "setback_m",
            "label": "Illustrative setback",
            "default": 2,
            "min": 0,
            "max": 100,
            "step": 0.5,
            "unit": "m"
          },
          {
            "name": "baseline_par_mol_m2_day",
            "label": "Open-field daily PAR",
            "default": 35,
            "min": 0.1,
            "max": 100,
            "step": 0.5,
            "unit": "mol/m²/day"
          }
        ],
        "jsonDataFields": [
          {
            "name": "land_polygon_m",
            "label": "Local land-boundary polygon vertices (m)",
            "default": {
              "type": "array",
              "items": 4,
              "itemTypes": [
                "array"
              ],
              "sha256": "f618a20d9df81308bd9d5218dd9137bbebc48aac1e5a6c6024ab197a02df3adc"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "20baee265bd2bf692a6288b92e431cb8808a5e2d546093568a21b9d39cde87bf",
        "evidenceSha256": "77d5281b58063da339964c6a71a3078c3d8c4d361f3d06d391afbc7831129cb3",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-agrivoltaic\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-agrivoltaic\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "A 100 m by 50 m rectangle has area 5,000 m²; a 1 m setback leaves 4,700 m² and lower coverage increases modeled crop PAR.",
          "testIds": [
            {
              "node_id": "tests/test_engineering_studies.py::test_agrivoltaic_polygon_area_and_par_tradeoff_hand_case",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "ground_coverage_ratio",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 0.9,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "module_transmission_fraction",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 0.8,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "setback_m",
              "unit": "m",
              "minimum": 0,
              "maximum": 100,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "baseline_par_mol_m2_day",
              "unit": "mol/m²/day",
              "minimum": 0.1,
              "maximum": 100,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "land_polygon_m",
              "label": "Local land-boundary polygon vertices (m)",
              "default": {
                "type": "array",
                "items": 4,
                "itemTypes": [
                  "array"
                ],
                "sha256": "f618a20d9df81308bd9d5218dd9137bbebc48aac1e5a6c6024ab197a02df3adc"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/engineering_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "20baee265bd2bf692a6288b92e431cb8808a5e2d546093568a21b9d39cde87bf",
          "evidenceSha256": "77d5281b58063da339964c6a71a3078c3d8c4d361f3d06d391afbc7831129cb3",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-agrivoltaic\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-agrivoltaic\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "A 100 m by 50 m rectangle has area 5,000 m²; a 1 m setback leaves 4,700 m² and lower coverage increases modeled crop PAR.",
            "testIds": [
              {
                "node_id": "tests/test_engineering_studies.py::test_agrivoltaic_polygon_area_and_par_tradeoff_hand_case",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "ground_coverage_ratio below declared minimum 0",
            "ground_coverage_ratio above declared maximum 0.9",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "ground_coverage_ratio=0 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Land polygon area",
                  "value": 5000,
                  "unit": "m²"
                },
                {
                  "label": "Polygon perimeter",
                  "value": 300,
                  "unit": "m"
                },
                {
                  "label": "Conservative usable area",
                  "value": 4400,
                  "unit": "m²"
                },
                {
                  "label": "Estimated crop PAR",
                  "value": 35,
                  "unit": "mol/m²/day"
                },
                {
                  "label": "Estimated PV-covered area",
                  "value": 0,
                  "unit": "m²"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "78a3a60b03fe3fa1120f87d46cf6d79e117ebbba6f4f1973a3f39abb8703bfba"
          }
        },
        "limitations": [
          "Perimeter setback subtraction is a conservative area proxy, not an exact polygon offset or cadastral calculation.",
          "PAR proxy omits time-of-day geometry, crop response, spectral effects, diffuse light, row spacing, equipment access and yield validation."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/engineering_studies.py",
          "tests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "pv-agrivoltaic",
            "workspace": "pv",
            "title": "Agrivoltaic PAR and land geometry",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_engineering_studies.py::test_agrivoltaic_polygon_area_and_par_tradeoff_hand_case",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "A 100 m by 50 m rectangle has area 5,000 m²; a 1 m setback leaves 4,700 m² and lower coverage increases modeled crop PAR.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Land polygon area",
                        "value": 5000,
                        "unit": "m²"
                      },
                      {
                        "label": "Polygon perimeter",
                        "value": 300,
                        "unit": "m"
                      },
                      {
                        "label": "Conservative usable area",
                        "value": 4700,
                        "unit": "m²"
                      },
                      {
                        "label": "Estimated crop PAR",
                        "value": 22.119999999999997,
                        "unit": "mol/m²/day"
                      },
                      {
                        "label": "Estimated PV-covered area",
                        "value": 1880,
                        "unit": "m²"
                      }
                    ],
                    "scalar_details": {
                      "setback_deduction_area_m2": 300,
                      "crop_par_fraction_of_open_field": 0.6319999999999999
                    },
                    "table_row_count": 0,
                    "result_sha256": "3c695779da8b2e3adabc92c0b588fc417992d5838cdc20e3a2801342f1814d91"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Land polygon area",
                        "value": 5000,
                        "unit": "m²"
                      },
                      {
                        "label": "Polygon perimeter",
                        "value": 300,
                        "unit": "m"
                      },
                      {
                        "label": "Conservative usable area",
                        "value": 4700,
                        "unit": "m²"
                      },
                      {
                        "label": "Estimated crop PAR",
                        "value": 28.56,
                        "unit": "mol/m²/day"
                      },
                      {
                        "label": "Estimated PV-covered area",
                        "value": 940,
                        "unit": "m²"
                      }
                    ],
                    "scalar_details": {
                      "setback_deduction_area_m2": 300,
                      "crop_par_fraction_of_open_field": 0.816
                    },
                    "table_row_count": 0,
                    "result_sha256": "2bd5acc7cc19eabb986ac90c7396f16f9a5e520c678cad3ae823dabef46e378b"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Land polygon area",
                    "value": 5000,
                    "unit": "m²"
                  },
                  {
                    "label": "Polygon perimeter",
                    "value": 300,
                    "unit": "m"
                  },
                  {
                    "label": "Conservative usable area",
                    "value": 4400,
                    "unit": "m²"
                  },
                  {
                    "label": "Estimated crop PAR",
                    "value": 23.729999999999997,
                    "unit": "mol/m²/day"
                  },
                  {
                    "label": "Estimated PV-covered area",
                    "value": 1540,
                    "unit": "m²"
                  }
                ],
                "default_result_sha256": "78a3a60b03fe3fa1120f87d46cf6d79e117ebbba6f4f1973a3f39abb8703bfba"
              },
              "units": "m² / mol/m²/day",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 5
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "ground_coverage_ratio=0 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Land polygon area",
                    "value": 5000,
                    "unit": "m²"
                  },
                  {
                    "label": "Polygon perimeter",
                    "value": 300,
                    "unit": "m"
                  },
                  {
                    "label": "Conservative usable area",
                    "value": 4400,
                    "unit": "m²"
                  },
                  {
                    "label": "Estimated crop PAR",
                    "value": 35,
                    "unit": "mol/m²/day"
                  },
                  {
                    "label": "Estimated PV-covered area",
                    "value": 0,
                    "unit": "m²"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "78a3a60b03fe3fa1120f87d46cf6d79e117ebbba6f4f1973a3f39abb8703bfba"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/engineering_studies.py",
              "engine_sha256": "3cebc8222e67c45db07c0e288098f53735a4991e773e1cac13751372c9fec642",
              "test_file": "apps/api/tests/test_engineering_studies.py",
              "test_sha256": "240899798522fe3061c8aa817f59eeb91bf66ea1907e8b41163b16371913644b",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Perimeter setback subtraction is a conservative area proxy, not an exact polygon offset or cadastral calculation.",
              "PAR proxy omits time-of-day geometry, crop response, spectral effects, diffuse light, row spacing, equipment access and yield validation.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-pv-agrivoltaic"
    },
    {
      "id": "pv-albedo",
      "title": "Monthly bifacial albedo response",
      "summary": "Combine monthly front-plane and reflected rear irradiance with bifaciality, albedo, view factor and PR.",
      "workspace": {
        "id": "pv",
        "name": "PV economics",
        "appRoute": "#pv"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "bifacial",
        "equations": [
          "Grear = Gground · albedo · rear_view_factor",
          "Efront,m = Pdc · PR · Gfront,m",
          "Etotal,m = Pdc · PR · (Gfront,m + bifaciality · Grear,m)",
          "Eyear = Σₘ Etotal,m"
        ],
        "limitations": [
          "Monthly scalar irradiance screen; rear view factor and PR are user assumptions, not ray tracing or site meteorology.",
          "No electrical mismatch, row shading, temperature, spectral, terrain or measured validation; annual energy is only as representative as the supplied 12 months."
        ],
        "sources": [
          {
            "title": "Photovoltaic Bifacial Irradiance and Performance Modeling Toolkit (NREL)",
            "url": "https://www.nrel.gov/pv/pv-bifacial-irradiance-toolkit"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "system_capacity_kwp",
            "label": "System DC capacity",
            "default": 5000,
            "min": 1,
            "max": 10000000,
            "step": 100,
            "unit": "kWp"
          },
          {
            "name": "performance_ratio",
            "label": "Performance ratio",
            "default": 0.82,
            "min": 0.01,
            "max": 1,
            "step": 0.01,
            "unit": "fraction"
          },
          {
            "name": "bifaciality",
            "label": "Rear/front response ratio",
            "default": 0.7,
            "min": 0,
            "max": 1,
            "step": 0.01,
            "unit": "fraction"
          },
          {
            "name": "albedo",
            "label": "Ground albedo",
            "default": 0.25,
            "min": 0,
            "max": 1,
            "step": 0.01,
            "unit": "fraction"
          },
          {
            "name": "rear_view_factor",
            "label": "Rear ground view factor",
            "default": 0.25,
            "min": 0,
            "max": 1,
            "step": 0.01,
            "unit": "fraction"
          }
        ],
        "jsonDataFields": [
          {
            "name": "front_plane_irradiance_kwh_m2",
            "label": "Monthly front-plane irradiation",
            "default": {
              "type": "array",
              "items": 12,
              "itemTypes": [
                "number"
              ],
              "sha256": "d369ad29f2beaccfdec6fb8157fc5197e81513d90a0e49e04c93d3b08cf0919f"
            }
          },
          {
            "name": "rear_ground_irradiance_kwh_m2",
            "label": "Monthly rear ground irradiation before albedo",
            "default": {
              "type": "array",
              "items": 12,
              "itemTypes": [
                "number"
              ],
              "sha256": "4f9a61aef4ceda4a0794201463d2e431bed74b58bcdb92dd1f372db6e2bf46e6"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "2f933af9a62a5e13d931885ce2394885b0977cd1c9c95cc78bbb27f19f512a8e",
        "evidenceSha256": "acad44d4dc0ad9af93f0f0824f70b174add5c949a99f4574e118d84a0f6bef61",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-albedo\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-albedo\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Monthly rear-response energy sums to annual total; zero albedo contributes zero rear-side energy.",
          "testIds": [
            {
              "node_id": "tests/test_engineering_studies.py::test_bifacial_albedo_gain_and_monthly_annual_reconciliation",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "system_capacity_kwp",
              "unit": "kWp",
              "minimum": 1,
              "maximum": 10000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "performance_ratio",
              "unit": "fraction",
              "minimum": 0.01,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "bifaciality",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "albedo",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "rear_view_factor",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "front_plane_irradiance_kwh_m2",
              "label": "Monthly front-plane irradiation",
              "default": {
                "type": "array",
                "items": 12,
                "itemTypes": [
                  "number"
                ],
                "sha256": "d369ad29f2beaccfdec6fb8157fc5197e81513d90a0e49e04c93d3b08cf0919f"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "rear_ground_irradiance_kwh_m2",
              "label": "Monthly rear ground irradiation before albedo",
              "default": {
                "type": "array",
                "items": 12,
                "itemTypes": [
                  "number"
                ],
                "sha256": "4f9a61aef4ceda4a0794201463d2e431bed74b58bcdb92dd1f372db6e2bf46e6"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/engineering_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "2f933af9a62a5e13d931885ce2394885b0977cd1c9c95cc78bbb27f19f512a8e",
          "evidenceSha256": "acad44d4dc0ad9af93f0f0824f70b174add5c949a99f4574e118d84a0f6bef61",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-albedo\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-albedo\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Monthly rear-response energy sums to annual total; zero albedo contributes zero rear-side energy.",
            "testIds": [
              {
                "node_id": "tests/test_engineering_studies.py::test_bifacial_albedo_gain_and_monthly_annual_reconciliation",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "system_capacity_kwp below declared minimum 1",
            "system_capacity_kwp above declared maximum 10000000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "system_capacity_kwp=1 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Annual front-side energy",
                  "value": 1123.3999999999999,
                  "unit": "kWh"
                },
                {
                  "label": "Annual rear-side contribution",
                  "value": 40.753999999999905,
                  "unit": "kWh"
                },
                {
                  "label": "Annual energy with rear response",
                  "value": 1164.1539999999998,
                  "unit": "kWh"
                },
                {
                  "label": "Rear contribution share",
                  "value": 0.03500739592871726,
                  "unit": "fraction"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "6aa265a4c65eff7d26f908204cd040263ce1196e7c42629591bc01c015dd87e3"
          }
        },
        "limitations": [
          "Monthly scalar irradiance screen; rear view factor and PR are user assumptions, not ray tracing or site meteorology.",
          "No electrical mismatch, row shading, temperature, spectral, terrain or measured validation; annual energy is only as representative as the supplied 12 months."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/engineering_studies.py",
          "tests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "pv-albedo",
            "workspace": "pv",
            "title": "Monthly bifacial albedo response",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_engineering_studies.py::test_bifacial_albedo_gain_and_monthly_annual_reconciliation",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Monthly rear-response energy sums to annual total; zero albedo contributes zero rear-side energy.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Annual front-side energy",
                        "value": 5617000,
                        "unit": "kWh"
                      },
                      {
                        "label": "Annual rear-side contribution",
                        "value": 81508,
                        "unit": "kWh"
                      },
                      {
                        "label": "Annual energy with rear response",
                        "value": 5698508,
                        "unit": "kWh"
                      },
                      {
                        "label": "Rear contribution share",
                        "value": 0.014303393098684778,
                        "unit": "fraction"
                      }
                    ],
                    "scalar_details": {
                      "annual_front_energy_kwh": 5617000,
                      "annual_rear_energy_kwh": 81508,
                      "annual_total_energy_kwh": 5698508
                    },
                    "table_row_count": 12,
                    "result_sha256": "91d91e5317dcb462ff761adf72e1ffe1a832ec56fcc12e21a2e2faad7297a097"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Annual front-side energy",
                        "value": 5617000,
                        "unit": "kWh"
                      },
                      {
                        "label": "Annual rear-side contribution",
                        "value": 652064,
                        "unit": "kWh"
                      },
                      {
                        "label": "Annual energy with rear response",
                        "value": 6269064,
                        "unit": "kWh"
                      },
                      {
                        "label": "Rear contribution share",
                        "value": 0.10401297546172762,
                        "unit": "fraction"
                      }
                    ],
                    "scalar_details": {
                      "annual_front_energy_kwh": 5617000,
                      "annual_rear_energy_kwh": 652064,
                      "annual_total_energy_kwh": 6269064
                    },
                    "table_row_count": 12,
                    "result_sha256": "9a2938620c69f035a71356f7dacd0b6b302961b6d6eb9b8bd538576442734ad7"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Annual front-side energy",
                        "value": 5617000,
                        "unit": "kWh"
                      },
                      {
                        "label": "Annual rear-side contribution",
                        "value": 0,
                        "unit": "kWh"
                      },
                      {
                        "label": "Annual energy with rear response",
                        "value": 5617000,
                        "unit": "kWh"
                      },
                      {
                        "label": "Rear contribution share",
                        "value": 0,
                        "unit": "fraction"
                      }
                    ],
                    "scalar_details": {
                      "annual_front_energy_kwh": 5617000,
                      "annual_rear_energy_kwh": 0,
                      "annual_total_energy_kwh": 5617000
                    },
                    "table_row_count": 12,
                    "result_sha256": "af0710671288e399449f65139196cb1e13e18c731473cf18a8dbc11f19194f14"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Annual front-side energy",
                    "value": 5617000,
                    "unit": "kWh"
                  },
                  {
                    "label": "Annual rear-side contribution",
                    "value": 203770,
                    "unit": "kWh"
                  },
                  {
                    "label": "Annual energy with rear response",
                    "value": 5820770,
                    "unit": "kWh"
                  },
                  {
                    "label": "Rear contribution share",
                    "value": 0.035007395928717336,
                    "unit": "fraction"
                  }
                ],
                "default_result_sha256": "6aa265a4c65eff7d26f908204cd040263ce1196e7c42629591bc01c015dd87e3"
              },
              "units": "kWh / fraction",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 4
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "system_capacity_kwp=1 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Annual front-side energy",
                    "value": 1123.3999999999999,
                    "unit": "kWh"
                  },
                  {
                    "label": "Annual rear-side contribution",
                    "value": 40.753999999999905,
                    "unit": "kWh"
                  },
                  {
                    "label": "Annual energy with rear response",
                    "value": 1164.1539999999998,
                    "unit": "kWh"
                  },
                  {
                    "label": "Rear contribution share",
                    "value": 0.03500739592871726,
                    "unit": "fraction"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "6aa265a4c65eff7d26f908204cd040263ce1196e7c42629591bc01c015dd87e3"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/engineering_studies.py",
              "engine_sha256": "3cebc8222e67c45db07c0e288098f53735a4991e773e1cac13751372c9fec642",
              "test_file": "apps/api/tests/test_engineering_studies.py",
              "test_sha256": "240899798522fe3061c8aa817f59eeb91bf66ea1907e8b41163b16371913644b",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Monthly scalar irradiance screen; rear view factor and PR are user assumptions, not ray tracing or site meteorology.",
              "No electrical mismatch, row shading, temperature, spectral, terrain or measured validation; annual energy is only as representative as the supplied 12 months.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-pv-albedo"
    },
    {
      "id": "research-vibration",
      "title": "Vibration spectrum and drift",
      "summary": "Estimate one-sided FFT amplitudes and compare early/late half-record spectral peaks.",
      "workspace": {
        "id": "research",
        "name": "Research pillars",
        "appRoute": "#research"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "vibration",
        "equations": [
          "X[k] = Σₙ x[n] exp(−j 2πkn/N)",
          "A[k] = 2|X[k]|/Σw[n] for interior one-sided bins",
          "Spectral drift = late-half dominant frequency − early-half dominant frequency"
        ],
        "limitations": [
          "Single evenly spaced, finite record with a Hann window; half-window frequency resolution is limited.",
          "Peaks and drift are descriptive spectrum statistics, not a fault classifier, causal diagnosis or maintenance recommendation."
        ],
        "sources": [
          {
            "title": "Discrete Fourier transform and spectral analysis (SciPy User Guide)",
            "url": "https://docs.scipy.org/doc/scipy/tutorial/signal.html"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "sample_rate_hz",
            "label": "Sample rate",
            "default": 256,
            "min": 128,
            "max": 20000,
            "step": 1,
            "unit": "Hz"
          },
          {
            "name": "synthetic_frequency_hz",
            "label": "Synthetic primary frequency",
            "default": 24,
            "min": 1,
            "max": 10000,
            "step": 1,
            "unit": "Hz"
          },
          {
            "name": "synthetic_drift_hz",
            "label": "Synthetic sweep across record",
            "default": 3,
            "min": -5000,
            "max": 5000,
            "step": 0.1,
            "unit": "Hz"
          },
          {
            "name": "synthetic_amplitude",
            "label": "Primary amplitude",
            "default": 1,
            "min": 0,
            "max": 1000000,
            "step": 0.1,
            "unit": "signal unit"
          },
          {
            "name": "noise_std",
            "label": "Synthetic noise standard deviation",
            "default": 0.05,
            "min": 0,
            "max": 1000000,
            "step": 0.01,
            "unit": "signal unit"
          },
          {
            "name": "sample_count",
            "label": "Synthetic sample count",
            "default": 1024,
            "min": 64,
            "max": 8192,
            "step": 64,
            "unit": "samples"
          }
        ],
        "jsonDataFields": [
          {
            "name": "signal",
            "label": "Supplied evenly spaced signal (empty uses seeded synthetic signal)",
            "default": {
              "type": "array",
              "items": 0,
              "itemTypes": [],
              "sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "85fcfc03ead35873f1e809474a228630a847b952cfd1d9bd61194141cdab1e30",
        "evidenceSha256": "4a8f32f1cb2a5aa5c1a91c877c49d474e73f9114a3debcf8db32f48811af428c",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"research-vibration\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"research-vibration\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "A 64 Hz pure sine sampled at 1,024 Hz is reported at 64 Hz; fixed-seed default output repeats exactly.",
          "testIds": [
            {
              "node_id": "tests/test_engineering_studies.py::test_vibration_fft_recovers_known_sine_peak_and_seed_is_reproducible",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "sample_rate_hz",
              "unit": "Hz",
              "minimum": 128,
              "maximum": 20000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "synthetic_frequency_hz",
              "unit": "Hz",
              "minimum": 1,
              "maximum": 10000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "synthetic_drift_hz",
              "unit": "Hz",
              "minimum": -5000,
              "maximum": 5000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "synthetic_amplitude",
              "unit": "signal unit",
              "minimum": 0,
              "maximum": 1000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "noise_std",
              "unit": "signal unit",
              "minimum": 0,
              "maximum": 1000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "sample_count",
              "unit": "samples",
              "minimum": 64,
              "maximum": 8192,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "signal",
              "label": "Supplied evenly spaced signal (empty uses seeded synthetic signal)",
              "default": {
                "type": "array",
                "items": 0,
                "itemTypes": [],
                "sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/engineering_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "85fcfc03ead35873f1e809474a228630a847b952cfd1d9bd61194141cdab1e30",
          "evidenceSha256": "4a8f32f1cb2a5aa5c1a91c877c49d474e73f9114a3debcf8db32f48811af428c",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"research-vibration\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"research-vibration\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "A 64 Hz pure sine sampled at 1,024 Hz is reported at 64 Hz; fixed-seed default output repeats exactly.",
            "testIds": [
              {
                "node_id": "tests/test_engineering_studies.py::test_vibration_fft_recovers_known_sine_peak_and_seed_is_reproducible",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "sample_rate_hz below declared minimum 128",
            "sample_rate_hz above declared maximum 20000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "sample_rate_hz=128 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Dominant frequency",
                  "value": 25.5,
                  "unit": "Hz"
                },
                {
                  "label": "Dominant peak amplitude",
                  "value": 0.40465351146145173,
                  "unit": "signal unit"
                },
                {
                  "label": "Early-half dominant frequency",
                  "value": 24.75,
                  "unit": "Hz"
                },
                {
                  "label": "Late-half dominant frequency",
                  "value": 26.25,
                  "unit": "Hz"
                },
                {
                  "label": "Spectral drift",
                  "value": 1.5,
                  "unit": "Hz"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "6da216b775e4f7a97eb6cdb1ef1ccb587a57f5d0be16e0593a9b46f8fb577fe1"
          }
        },
        "limitations": [
          "Single evenly spaced, finite record with a Hann window; half-window frequency resolution is limited.",
          "Peaks and drift are descriptive spectrum statistics, not a fault classifier, causal diagnosis or maintenance recommendation."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/engineering_studies.py",
          "tests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "research-vibration",
            "workspace": "research",
            "title": "Vibration spectrum and drift",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_engineering_studies.py::test_vibration_fft_recovers_known_sine_peak_and_seed_is_reproducible",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "A 64 Hz pure sine sampled at 1,024 Hz is reported at 64 Hz; fixed-seed default output repeats exactly.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Dominant frequency",
                        "value": 64,
                        "unit": "Hz"
                      },
                      {
                        "label": "Dominant peak amplitude",
                        "value": 0.9999999462657966,
                        "unit": "signal unit"
                      },
                      {
                        "label": "Early-half dominant frequency",
                        "value": 64,
                        "unit": "Hz"
                      },
                      {
                        "label": "Late-half dominant frequency",
                        "value": 64,
                        "unit": "Hz"
                      },
                      {
                        "label": "Spectral drift",
                        "value": 0,
                        "unit": "Hz"
                      }
                    ],
                    "scalar_details": {
                      "sample_count": 1024,
                      "sample_rate_hz": 1024,
                      "signal_source": "user_supplied",
                      "seed": null,
                      "frequency_resolution_hz": 1
                    },
                    "table_row_count": 10,
                    "result_sha256": "5e05f79f54376379c2ee4d00fc6a0cbfcd060d0a0d5a1116d36e72338be523de"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Dominant frequency",
                        "value": 25.5,
                        "unit": "Hz"
                      },
                      {
                        "label": "Dominant peak amplitude",
                        "value": 0.5720468156528937,
                        "unit": "signal unit"
                      },
                      {
                        "label": "Early-half dominant frequency",
                        "value": 24.5,
                        "unit": "Hz"
                      },
                      {
                        "label": "Late-half dominant frequency",
                        "value": 26,
                        "unit": "Hz"
                      },
                      {
                        "label": "Spectral drift",
                        "value": 1.5,
                        "unit": "Hz"
                      }
                    ],
                    "scalar_details": {
                      "sample_count": 1024,
                      "sample_rate_hz": 256,
                      "signal_source": "seeded_synthetic",
                      "seed": 42,
                      "frequency_resolution_hz": 0.25
                    },
                    "table_row_count": 10,
                    "result_sha256": "640db53b863938837a19f3d31900d9c5c00e2446566caeb572133e70df798bb1"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Dominant frequency",
                        "value": 25.5,
                        "unit": "Hz"
                      },
                      {
                        "label": "Dominant peak amplitude",
                        "value": 0.5720468156528937,
                        "unit": "signal unit"
                      },
                      {
                        "label": "Early-half dominant frequency",
                        "value": 24.5,
                        "unit": "Hz"
                      },
                      {
                        "label": "Late-half dominant frequency",
                        "value": 26,
                        "unit": "Hz"
                      },
                      {
                        "label": "Spectral drift",
                        "value": 1.5,
                        "unit": "Hz"
                      }
                    ],
                    "scalar_details": {
                      "sample_count": 1024,
                      "sample_rate_hz": 256,
                      "signal_source": "seeded_synthetic",
                      "seed": 42,
                      "frequency_resolution_hz": 0.25
                    },
                    "table_row_count": 10,
                    "result_sha256": "640db53b863938837a19f3d31900d9c5c00e2446566caeb572133e70df798bb1"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Dominant frequency",
                    "value": 25.5,
                    "unit": "Hz"
                  },
                  {
                    "label": "Dominant peak amplitude",
                    "value": 0.5720468156528937,
                    "unit": "signal unit"
                  },
                  {
                    "label": "Early-half dominant frequency",
                    "value": 24.5,
                    "unit": "Hz"
                  },
                  {
                    "label": "Late-half dominant frequency",
                    "value": 26,
                    "unit": "Hz"
                  },
                  {
                    "label": "Spectral drift",
                    "value": 1.5,
                    "unit": "Hz"
                  }
                ],
                "default_result_sha256": "6da216b775e4f7a97eb6cdb1ef1ccb587a57f5d0be16e0593a9b46f8fb577fe1"
              },
              "units": "Hz / signal unit",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 5
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "sample_rate_hz=128 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Dominant frequency",
                    "value": 25.5,
                    "unit": "Hz"
                  },
                  {
                    "label": "Dominant peak amplitude",
                    "value": 0.40465351146145173,
                    "unit": "signal unit"
                  },
                  {
                    "label": "Early-half dominant frequency",
                    "value": 24.75,
                    "unit": "Hz"
                  },
                  {
                    "label": "Late-half dominant frequency",
                    "value": 26.25,
                    "unit": "Hz"
                  },
                  {
                    "label": "Spectral drift",
                    "value": 1.5,
                    "unit": "Hz"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "6da216b775e4f7a97eb6cdb1ef1ccb587a57f5d0be16e0593a9b46f8fb577fe1"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/engineering_studies.py",
              "engine_sha256": "3cebc8222e67c45db07c0e288098f53735a4991e773e1cac13751372c9fec642",
              "test_file": "apps/api/tests/test_engineering_studies.py",
              "test_sha256": "240899798522fe3061c8aa817f59eeb91bf66ea1907e8b41163b16371913644b",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Single evenly spaced, finite record with a Hann window; half-window frequency resolution is limited.",
              "Peaks and drift are descriptive spectrum statistics, not a fault classifier, causal diagnosis or maintenance recommendation.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-research-vibration"
    },
    {
      "id": "research-epd",
      "title": "Supplier-stage emissions evidence ledger",
      "summary": "Sum supplied supplier-stage quantities and factors while surfacing duplicate IDs and missing evidence.",
      "workspace": {
        "id": "research",
        "name": "Research pillars",
        "appRoute": "#research"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "epd",
        "equations": [
          "Stage emissionsᵢ = quantityᵢ · supplier/activity factorᵢ",
          "Total included emissions = Σ unique, factor-complete stage records"
        ],
        "limitations": [
          "Per-record quantities are bounded at 1e12 declared units and factors at 1e6 kgCO₂e per declared unit to keep the arithmetic finite; choose and document suitable units.",
          "A traceable arithmetic ledger only: it does not verify EPD validity, product-category rules, allocation, boundary, assurance or Scope 3 category.",
          "Duplicate IDs retain only their first row; missing factors are reported and excluded, not imputed. Example defaults are synthetic."
        ],
        "sources": [
          {
            "title": "Category 1 Purchased Goods and Services: Scope 3 Calculation Guidance (GHG Protocol)",
            "url": "https://ghgprotocol.org/sites/default/files/standards/Scope3_Calculation_Guidance_0.pdf"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "include_materials",
            "label": "Include materials stage",
            "default": 1,
            "min": 0,
            "max": 1,
            "step": 1,
            "unit": "0/1"
          },
          {
            "name": "include_manufacturing",
            "label": "Include manufacturing stage",
            "default": 1,
            "min": 0,
            "max": 1,
            "step": 1,
            "unit": "0/1"
          },
          {
            "name": "include_transport",
            "label": "Include transport stage",
            "default": 1,
            "min": 0,
            "max": 1,
            "step": 1,
            "unit": "0/1"
          },
          {
            "name": "include_use",
            "label": "Include use stage",
            "default": 1,
            "min": 0,
            "max": 1,
            "step": 1,
            "unit": "0/1"
          },
          {
            "name": "include_end_of_life",
            "label": "Include end-of-life stage",
            "default": 1,
            "min": 0,
            "max": 1,
            "step": 1,
            "unit": "0/1"
          },
          {
            "name": "include_other",
            "label": "Include other stage",
            "default": 1,
            "min": 0,
            "max": 1,
            "step": 1,
            "unit": "0/1"
          }
        ],
        "jsonDataFields": [
          {
            "name": "records",
            "label": "Supplier-stage records (quantity ≤1e12; factor ≤1e6 kgCO₂e/unit)",
            "default": {
              "type": "array",
              "items": 3,
              "itemTypes": [
                "object"
              ],
              "sha256": "9537165dab30ba98d28a3bdbb869b4eb9be65bec4e9dc9fb2b7d0b27a6aa0fa8"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "041ebb0658b8fee3695682f8299af17abb79c0c868ef740b97248365edb181a9",
        "evidenceSha256": "9c4ff05f57737f51cfcd7531220e7dc176b6d444728b830c545b0d2f0349e818",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"research-epd\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"research-epd\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Two duplicate 2-unit records at factor 3 contribute 6 kgCO₂e once; incomplete record is disclosed rather than imputed.",
          "testIds": [
            {
              "node_id": "tests/test_engineering_studies.py::test_epd_sum_skips_duplicate_and_makes_missing_records_visible",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "include_materials",
              "unit": "0/1",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "include_manufacturing",
              "unit": "0/1",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "include_transport",
              "unit": "0/1",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "include_use",
              "unit": "0/1",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "include_end_of_life",
              "unit": "0/1",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "include_other",
              "unit": "0/1",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "records",
              "label": "Supplier-stage records (quantity ≤1e12; factor ≤1e6 kgCO₂e/unit)",
              "default": {
                "type": "array",
                "items": 3,
                "itemTypes": [
                  "object"
                ],
                "sha256": "9537165dab30ba98d28a3bdbb869b4eb9be65bec4e9dc9fb2b7d0b27a6aa0fa8"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/engineering_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "041ebb0658b8fee3695682f8299af17abb79c0c868ef740b97248365edb181a9",
          "evidenceSha256": "9c4ff05f57737f51cfcd7531220e7dc176b6d444728b830c545b0d2f0349e818",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"research-epd\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"research-epd\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Two duplicate 2-unit records at factor 3 contribute 6 kgCO₂e once; incomplete record is disclosed rather than imputed.",
            "testIds": [
              {
                "node_id": "tests/test_engineering_studies.py::test_epd_sum_skips_duplicate_and_makes_missing_records_visible",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "include_materials below declared minimum 0",
            "include_materials above declared maximum 1",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "include_materials=0 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Included emissions",
                  "value": 15,
                  "unit": "kgCO₂e"
                },
                {
                  "label": "Unique records",
                  "value": 3,
                  "unit": "records"
                },
                {
                  "label": "Included factor-complete records",
                  "value": 1,
                  "unit": "records"
                },
                {
                  "label": "Duplicate record IDs",
                  "value": 0,
                  "unit": "IDs"
                },
                {
                  "label": "Records with missing fields",
                  "value": 1,
                  "unit": "records"
                },
                {
                  "label": "Evidence completeness",
                  "value": 0.6666666666666666,
                  "unit": "fraction"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "0a703b3a75be610ddb09d2f8bb4a87572c63cc8747153c693177592d51580a70"
          }
        },
        "limitations": [
          "Per-record quantities are bounded at 1e12 declared units and factors at 1e6 kgCO₂e per declared unit to keep the arithmetic finite; choose and document suitable units.",
          "A traceable arithmetic ledger only: it does not verify EPD validity, product-category rules, allocation, boundary, assurance or Scope 3 category.",
          "Duplicate IDs retain only their first row; missing factors are reported and excluded, not imputed. Example defaults are synthetic."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/engineering_studies.py",
          "tests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "research-epd",
            "workspace": "research",
            "title": "Supplier-stage emissions evidence ledger",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_engineering_studies.py::test_epd_sum_skips_duplicate_and_makes_missing_records_visible",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Two duplicate 2-unit records at factor 3 contribute 6 kgCO₂e once; incomplete record is disclosed rather than imputed.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Included emissions",
                        "value": 6,
                        "unit": "kgCO₂e"
                      },
                      {
                        "label": "Unique records",
                        "value": 2,
                        "unit": "records"
                      },
                      {
                        "label": "Included factor-complete records",
                        "value": 1,
                        "unit": "records"
                      },
                      {
                        "label": "Duplicate record IDs",
                        "value": 1,
                        "unit": "IDs"
                      },
                      {
                        "label": "Records with missing fields",
                        "value": 1,
                        "unit": "records"
                      },
                      {
                        "label": "Evidence completeness",
                        "value": 0.5,
                        "unit": "fraction"
                      }
                    ],
                    "scalar_details": {
                      "first_occurrence_included_only": true,
                      "unique_records": 2
                    },
                    "table_row_count": 3,
                    "result_sha256": "06a0fbb46225cd574094262f966cc49430a8a0d004323022037a1aa43ee88381"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Included emissions",
                    "value": 2115,
                    "unit": "kgCO₂e"
                  },
                  {
                    "label": "Unique records",
                    "value": 3,
                    "unit": "records"
                  },
                  {
                    "label": "Included factor-complete records",
                    "value": 2,
                    "unit": "records"
                  },
                  {
                    "label": "Duplicate record IDs",
                    "value": 0,
                    "unit": "IDs"
                  },
                  {
                    "label": "Records with missing fields",
                    "value": 1,
                    "unit": "records"
                  },
                  {
                    "label": "Evidence completeness",
                    "value": 0.6666666666666666,
                    "unit": "fraction"
                  }
                ],
                "default_result_sha256": "0a703b3a75be610ddb09d2f8bb4a87572c63cc8747153c693177592d51580a70"
              },
              "units": "kgCO₂e / records",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 6
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "include_materials=0 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Included emissions",
                    "value": 15,
                    "unit": "kgCO₂e"
                  },
                  {
                    "label": "Unique records",
                    "value": 3,
                    "unit": "records"
                  },
                  {
                    "label": "Included factor-complete records",
                    "value": 1,
                    "unit": "records"
                  },
                  {
                    "label": "Duplicate record IDs",
                    "value": 0,
                    "unit": "IDs"
                  },
                  {
                    "label": "Records with missing fields",
                    "value": 1,
                    "unit": "records"
                  },
                  {
                    "label": "Evidence completeness",
                    "value": 0.6666666666666666,
                    "unit": "fraction"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "0a703b3a75be610ddb09d2f8bb4a87572c63cc8747153c693177592d51580a70"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/engineering_studies.py",
              "engine_sha256": "3cebc8222e67c45db07c0e288098f53735a4991e773e1cac13751372c9fec642",
              "test_file": "apps/api/tests/test_engineering_studies.py",
              "test_sha256": "240899798522fe3061c8aa817f59eeb91bf66ea1907e8b41163b16371913644b",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Per-record quantities are bounded at 1e12 declared units and factors at 1e6 kgCO₂e per declared unit to keep the arithmetic finite; choose and document suitable units.",
              "A traceable arithmetic ledger only: it does not verify EPD validity, product-category rules, allocation, boundary, assurance or Scope 3 category.",
              "Duplicate IDs retain only their first row; missing factors are reported and excluded, not imputed. Example defaults are synthetic.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-research-epd"
    },
    {
      "id": "research-scr",
      "title": "Short-circuit strength and voltage drop",
      "summary": "Calculate a simple Ssc/P screening ratio and a balanced small-voltage-drop estimate from R, X, P and Q.",
      "workspace": {
        "id": "research",
        "name": "Research pillars",
        "appRoute": "#research"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "grid-strength",
        "equations": [
          "SCRscreen = Ssc / Pplant",
          "ΔVpu ≈ (R P + X Q)/VLL²",
          "|Zth| ≈ VLL²/Ssc"
        ],
        "limitations": [
          "Classical single-infeed ratio and balanced first-order voltage-drop approximation only.",
          "SCR is not a stability guarantee; interactions, loads, X/R, controls, protection and operating points require detailed network and dynamic studies."
        ],
        "sources": [
          {
            "title": "Experimental Evaluation of SCR and X/R Ratio on GFM and GFL Inverters (IEEE PESGM)",
            "url": "https://ieeexplore.ieee.org/document/11225025/"
          },
          {
            "title": "Grid Strength Assessment in Weak Distribution Systems: Beyond Short Circuit Ratio (IEEE PESGM)",
            "url": "https://ieeexplore.ieee.org/abstract/document/11693070/"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "short_circuit_mva",
            "label": "Grid short-circuit capacity",
            "default": 600,
            "min": 0.1,
            "max": 2000000,
            "step": 10,
            "unit": "MVA"
          },
          {
            "name": "plant_mw",
            "label": "Plant active-power rating",
            "default": 100,
            "min": 0.1,
            "max": 2000000,
            "step": 1,
            "unit": "MW"
          },
          {
            "name": "active_power_mw",
            "label": "Operating active power",
            "default": 80,
            "min": 0,
            "max": 2000000,
            "step": 1,
            "unit": "MW"
          },
          {
            "name": "reactive_power_mvar",
            "label": "Operating reactive power",
            "default": 20,
            "min": -2000000,
            "max": 2000000,
            "step": 1,
            "unit": "Mvar"
          },
          {
            "name": "voltage_kv",
            "label": "Line-to-line voltage",
            "default": 20,
            "min": 0.1,
            "max": 1000,
            "step": 0.1,
            "unit": "kV"
          },
          {
            "name": "line_r_ohm",
            "label": "Equivalent series resistance",
            "default": 0.1,
            "min": 0,
            "max": 100000,
            "step": 0.01,
            "unit": "Ω/phase"
          }
        ],
        "jsonDataFields": []
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "44c8d00cc9704c69f52f926523d81d3507cc4dd87f778f8e3cfed9f66b7a2fe7",
        "evidenceSha256": "f67ff32f99c448935aa0c875ff3cea9d13322d9cc94a33000ce53fd4a30bd88e",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"research-scr\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"research-scr\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "600 MVA / 100 MW gives SCR 6; the specified three-phase screening case gives 4.25% voltage drop.",
          "testIds": [
            {
              "node_id": "tests/test_engineering_studies.py::test_scr_and_voltage_drop_hand_case_is_labeled_as_screening",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "short_circuit_mva",
              "unit": "MVA",
              "minimum": 0.1,
              "maximum": 2000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "plant_mw",
              "unit": "MW",
              "minimum": 0.1,
              "maximum": 2000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "active_power_mw",
              "unit": "MW",
              "minimum": 0,
              "maximum": 2000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "reactive_power_mvar",
              "unit": "Mvar",
              "minimum": -2000000,
              "maximum": 2000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "voltage_kv",
              "unit": "kV",
              "minimum": 0.1,
              "maximum": 1000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "line_r_ohm",
              "unit": "Ω/phase",
              "minimum": 0,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [],
          "strictValidationReference": "apps/api/helioforge/engines/engineering_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "44c8d00cc9704c69f52f926523d81d3507cc4dd87f778f8e3cfed9f66b7a2fe7",
          "evidenceSha256": "f67ff32f99c448935aa0c875ff3cea9d13322d9cc94a33000ce53fd4a30bd88e",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"research-scr\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"research-scr\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "600 MVA / 100 MW gives SCR 6; the specified three-phase screening case gives 4.25% voltage drop.",
            "testIds": [
              {
                "node_id": "tests/test_engineering_studies.py::test_scr_and_voltage_drop_hand_case_is_labeled_as_screening",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "short_circuit_mva below declared minimum 0.1",
            "short_circuit_mva above declared maximum 2000000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "short_circuit_mva=0.1 (min bound)",
              "metric_snapshot": [
                {
                  "label": "SCR at plant rating",
                  "value": 0.001,
                  "unit": "MVA/MW"
                },
                {
                  "label": "Approximate voltage-drop magnitude",
                  "value": 3.5000000000000004,
                  "unit": "%"
                },
                {
                  "label": "Approximate Thevenin impedance magnitude",
                  "value": 4000,
                  "unit": "Ω"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "3d804393c655ad531cac2a73160731a489a51422c2e685bb08bb3c1d99c0897d"
          }
        },
        "limitations": [
          "Classical single-infeed ratio and balanced first-order voltage-drop approximation only.",
          "SCR is not a stability guarantee; interactions, loads, X/R, controls, protection and operating points require detailed network and dynamic studies."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/engineering_studies.py",
          "tests": [
            "apps/api/tests/test_engineering_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "research-scr",
            "workspace": "research",
            "title": "Short-circuit strength and voltage drop",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_engineering_studies.py::test_scr_and_voltage_drop_hand_case_is_labeled_as_screening",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "600 MVA / 100 MW gives SCR 6; the specified three-phase screening case gives 4.25% voltage drop.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "SCR at plant rating",
                        "value": 6,
                        "unit": "MVA/MW"
                      },
                      {
                        "label": "Approximate voltage-drop magnitude",
                        "value": 4.25,
                        "unit": "%"
                      },
                      {
                        "label": "Approximate Thevenin impedance magnitude",
                        "value": 0.6666666666666666,
                        "unit": "Ω"
                      }
                    ],
                    "scalar_details": {
                      "voltage_drop_signed_pu": 0.0425,
                      "voltage_drop_magnitude_pct": 4.25,
                      "short_circuit_mva": 600,
                      "plant_mw": 100,
                      "active_power_mw": 80,
                      "reactive_power_mvar": 30,
                      "voltage_kv_line_to_line": 20,
                      "fixed_line_reactance_ohm": 0.3
                    },
                    "table_row_count": 0,
                    "result_sha256": "deabcce4c6cea124842d9ee640cadd6a6fdad924d23b124611271b944e9e8cef"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "SCR at plant rating",
                    "value": 6,
                    "unit": "MVA/MW"
                  },
                  {
                    "label": "Approximate voltage-drop magnitude",
                    "value": 3.5000000000000004,
                    "unit": "%"
                  },
                  {
                    "label": "Approximate Thevenin impedance magnitude",
                    "value": 0.6666666666666666,
                    "unit": "Ω"
                  }
                ],
                "default_result_sha256": "3d804393c655ad531cac2a73160731a489a51422c2e685bb08bb3c1d99c0897d"
              },
              "units": "MVA/MW / % / Ω",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 3
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "short_circuit_mva=0.1 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "SCR at plant rating",
                    "value": 0.001,
                    "unit": "MVA/MW"
                  },
                  {
                    "label": "Approximate voltage-drop magnitude",
                    "value": 3.5000000000000004,
                    "unit": "%"
                  },
                  {
                    "label": "Approximate Thevenin impedance magnitude",
                    "value": 4000,
                    "unit": "Ω"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "3d804393c655ad531cac2a73160731a489a51422c2e685bb08bb3c1d99c0897d"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/engineering_studies.py",
              "engine_sha256": "3cebc8222e67c45db07c0e288098f53735a4991e773e1cac13751372c9fec642",
              "test_file": "apps/api/tests/test_engineering_studies.py",
              "test_sha256": "240899798522fe3061c8aa817f59eeb91bf66ea1907e8b41163b16371913644b",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Classical single-infeed ratio and balanced first-order voltage-drop approximation only.",
              "SCR is not a stability guarantee; interactions, loads, X/R, controls, protection and operating points require detailed network and dynamic studies.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-research-scr"
    },
    {
      "id": "overview-weather",
      "title": "Correlated weather stress",
      "summary": "Compare shared and asset-specific weather shocks across six fictional assets.",
      "workspace": {
        "id": "overview",
        "name": "Command centre",
        "appRoute": "#overview"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "weather",
        "equations": [
          "z_i,t = sqrt(common_share) z_common,t + sqrt(1−common_share) z_idiosyncratic,i,t",
          "stress_i,t = generation_i × sensitivity_i × max(0, −z_i,t), capped at 100%"
        ],
        "limitations": [
          "Synthetic standardized shocks; no weather observations, technology calibration, spatial model or outage probability.",
          "The reported stress index is a bounded generation-weighted exposure proxy."
        ],
        "sources": [
          {
            "title": "NREL solar integration data and studies",
            "url": "https://www.nrel.gov/grid/solar-power-data.html"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "horizon_years",
            "label": "Horizon",
            "default": 10,
            "min": 1,
            "max": 40,
            "step": 1,
            "unit": "years"
          },
          {
            "name": "common_shock_fraction",
            "label": "Common shock share",
            "default": 0.75,
            "min": 0,
            "max": 1,
            "step": 0.05,
            "unit": "fraction"
          },
          {
            "name": "seed",
            "label": "Seed",
            "default": 42,
            "min": 0,
            "max": 4294967295,
            "step": 1,
            "unit": "integer"
          }
        ],
        "jsonDataFields": [
          {
            "name": "assets",
            "label": "Six synthetic assets",
            "default": {
              "type": "array",
              "items": 6,
              "itemTypes": [
                "object"
              ],
              "sha256": "359dce81eb5421ee8cb3ad76d2e33501516a62290e168ba7b55c37b35e688164"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "0402e26d161c32c1c8f0807ad1813a69c9b2856c42973a5f61e7d4db73734382",
        "evidenceSha256": "65958eed23736922bd1dfcdb02d5df2a4ea0ddabf40447affa588099bd8ee712",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"overview-weather\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"overview-weather\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "A 0.5 common shock fraction appears as 0.5 off-diagonal theoretical correlation in the six-row synthetic portfolio.",
          "testIds": [
            {
              "node_id": "tests/test_decision_studies.py::test_weather_stress_exposes_the_common_factor_correlation",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "horizon_years",
              "unit": "years",
              "minimum": 1,
              "maximum": 40,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "common_shock_fraction",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "seed",
              "unit": "integer",
              "minimum": 0,
              "maximum": 4294967295,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "assets",
              "label": "Six synthetic assets",
              "default": {
                "type": "array",
                "items": 6,
                "itemTypes": [
                  "object"
                ],
                "sha256": "359dce81eb5421ee8cb3ad76d2e33501516a62290e168ba7b55c37b35e688164"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/decision_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "0402e26d161c32c1c8f0807ad1813a69c9b2856c42973a5f61e7d4db73734382",
          "evidenceSha256": "65958eed23736922bd1dfcdb02d5df2a4ea0ddabf40447affa588099bd8ee712",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"overview-weather\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"overview-weather\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "A 0.5 common shock fraction appears as 0.5 off-diagonal theoretical correlation in the six-row synthetic portfolio.",
            "testIds": [
              {
                "node_id": "tests/test_decision_studies.py::test_weather_stress_exposes_the_common_factor_correlation",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "horizon_years below declared minimum 1",
            "horizon_years above declared maximum 40",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "horizon_years=1 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Mean portfolio stress index",
                  "value": 18.201618,
                  "unit": "%"
                },
                {
                  "label": "P95 portfolio stress index",
                  "value": 18.201618,
                  "unit": "%"
                },
                {
                  "label": "Horizon",
                  "value": 1,
                  "unit": "years"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [
            "seed"
          ],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "7e592aae0caf67bb33225212a7fe6656cbc76599978c9a1e76f79b679dfc5f15"
          }
        },
        "limitations": [
          "Synthetic standardized shocks; no weather observations, technology calibration, spatial model or outage probability.",
          "The reported stress index is a bounded generation-weighted exposure proxy."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/decision_studies.py",
          "tests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "overview-weather",
            "workspace": "overview",
            "title": "Correlated weather stress",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_decision_studies.py::test_weather_stress_exposes_the_common_factor_correlation",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "A 0.5 common shock fraction appears as 0.5 off-diagonal theoretical correlation in the six-row synthetic portfolio.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Mean portfolio stress index",
                        "value": 23.908590333333333,
                        "unit": "%"
                      },
                      {
                        "label": "P95 portfolio stress index",
                        "value": 31.1135728,
                        "unit": "%"
                      },
                      {
                        "label": "Horizon",
                        "value": 3,
                        "unit": "years"
                      }
                    ],
                    "scalar_details": {
                      "seed": 2,
                      "common_shock_fraction": 0.5,
                      "total_base_generation_mwh": 107000
                    },
                    "table_row_count": 6,
                    "result_sha256": "27efa3fbaf4d1d1a9cc31012255ce5b94de9b4f6ae3313d628c9da698ac488c5"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Mean portfolio stress index",
                    "value": 28.675280200000003,
                    "unit": "%"
                  },
                  {
                    "label": "P95 portfolio stress index",
                    "value": 73.04983484999998,
                    "unit": "%"
                  },
                  {
                    "label": "Horizon",
                    "value": 10,
                    "unit": "years"
                  }
                ],
                "default_result_sha256": "7e592aae0caf67bb33225212a7fe6656cbc76599978c9a1e76f79b679dfc5f15"
              },
              "units": "% / correlation",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 3
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "horizon_years=1 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Mean portfolio stress index",
                    "value": 18.201618,
                    "unit": "%"
                  },
                  {
                    "label": "P95 portfolio stress index",
                    "value": 18.201618,
                    "unit": "%"
                  },
                  {
                    "label": "Horizon",
                    "value": 1,
                    "unit": "years"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "7e592aae0caf67bb33225212a7fe6656cbc76599978c9a1e76f79b679dfc5f15"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/decision_studies.py",
              "engine_sha256": "7730c9e9bd5fec2eb4249d70e0bacd0a7ba29921635aa88945f22f66df6439d1",
              "test_file": "apps/api/tests/test_decision_studies.py",
              "test_sha256": "857d6c292f08b499b133f2b42de828d700c3e82cf7472d84d7b1ccf5183f1dfd",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Synthetic standardized shocks; no weather observations, technology calibration, spatial model or outage probability.",
              "The reported stress index is a bounded generation-weighted exposure proxy.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-overview-weather"
    },
    {
      "id": "overview-merit",
      "title": "Value versus variable cost",
      "summary": "Compare asset variable costs with bilateral, spot and balancing value assumptions.",
      "workspace": {
        "id": "overview",
        "name": "Command centre",
        "appRoute": "#overview"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "merit",
        "equations": [
          "net value_route,i = assumed value_route,i − variable cost_i",
          "weighted net value_i = Σ_route weight_route × net value_route,i"
        ],
        "limitations": [
          "All matrix values and route weights are user assumptions; no dispatch, balancing eligibility, fees, shape or contract terms are inferred."
        ],
        "sources": [
          {
            "title": "U.S. EIA: Wholesale Electricity Markets",
            "url": "https://www.eia.gov/electricity/wholesalemarkets/"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "bilateral_weight",
            "label": "Bilateral weight",
            "default": 0.4,
            "min": 0,
            "max": 1,
            "step": 0.05,
            "unit": "fraction"
          },
          {
            "name": "spot_weight",
            "label": "Spot weight",
            "default": 0.4,
            "min": 0,
            "max": 1,
            "step": 0.05,
            "unit": "fraction"
          },
          {
            "name": "balancing_weight",
            "label": "Balancing weight",
            "default": 0.2,
            "min": 0,
            "max": 1,
            "step": 0.05,
            "unit": "fraction"
          }
        ],
        "jsonDataFields": [
          {
            "name": "assets",
            "label": "Asset value matrix",
            "default": {
              "type": "array",
              "items": 6,
              "itemTypes": [
                "object"
              ],
              "sha256": "7b5d1439bfedb08e4c3b369bffeea18ca85678b350d7f35c988223b168cf9b1e"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "d1e4ec58847b38d434cae98662c115ee1b24ff4a1218aa6420b7435f43efc2d4",
        "evidenceSha256": "e8e1666b778d48ca6c37e2dc6333f8d33f7006f6d5eec46226b9a8f5e53e0179",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"overview-merit\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"overview-merit\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "For the hand asset, net route values are 30, 50 and 70 EUR/MWh; weights yield 45 EUR/MWh.",
          "testIds": [
            {
              "node_id": "tests/test_decision_studies.py::test_merit_matrix_compares_value_routes_to_variable_cost",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "bilateral_weight",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "spot_weight",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "balancing_weight",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "assets",
              "label": "Asset value matrix",
              "default": {
                "type": "array",
                "items": 6,
                "itemTypes": [
                  "object"
                ],
                "sha256": "7b5d1439bfedb08e4c3b369bffeea18ca85678b350d7f35c988223b168cf9b1e"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/decision_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "d1e4ec58847b38d434cae98662c115ee1b24ff4a1218aa6420b7435f43efc2d4",
          "evidenceSha256": "e8e1666b778d48ca6c37e2dc6333f8d33f7006f6d5eec46226b9a8f5e53e0179",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"overview-merit\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"overview-merit\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "For the hand asset, net route values are 30, 50 and 70 EUR/MWh; weights yield 45 EUR/MWh.",
            "testIds": [
              {
                "node_id": "tests/test_decision_studies.py::test_merit_matrix_compares_value_routes_to_variable_cost",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "bilateral_weight below declared minimum 0",
            "bilateral_weight above declared maximum 1",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "assets=valid prefix length 1",
              "metric_snapshot": [
                {
                  "label": "Assets compared",
                  "value": 1,
                  "unit": "assets"
                },
                {
                  "label": "Mean weighted net value",
                  "value": 58.6,
                  "unit": "EUR/MWh"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "da9c2ac74402fb223344a72c48b56f7dafe4068caf776b374bcdaa299926f900"
          }
        },
        "limitations": [
          "All matrix values and route weights are user assumptions; no dispatch, balancing eligibility, fees, shape or contract terms are inferred."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/decision_studies.py",
          "tests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "overview-merit",
            "workspace": "overview",
            "title": "Value versus variable cost",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_decision_studies.py::test_merit_matrix_compares_value_routes_to_variable_cost",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "For the hand asset, net route values are 30, 50 and 70 EUR/MWh; weights yield 45 EUR/MWh.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Assets compared",
                        "value": 1,
                        "unit": "assets"
                      },
                      {
                        "label": "Mean weighted net value",
                        "value": 45,
                        "unit": "EUR/MWh"
                      }
                    ],
                    "scalar_details": {},
                    "table_row_count": 1,
                    "result_sha256": "16c836a1189c83dbe356e7db15b90f5a42c1335c2965d47f2c7c819c33e30e91"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Assets compared",
                    "value": 6,
                    "unit": "assets"
                  },
                  {
                    "label": "Mean weighted net value",
                    "value": 50.43333333333334,
                    "unit": "EUR/MWh"
                  }
                ],
                "default_result_sha256": "da9c2ac74402fb223344a72c48b56f7dafe4068caf776b374bcdaa299926f900"
              },
              "units": "EUR/MWh",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 2
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "assets=valid prefix length 1",
                "metric_snapshot": [
                  {
                    "label": "Assets compared",
                    "value": 1,
                    "unit": "assets"
                  },
                  {
                    "label": "Mean weighted net value",
                    "value": 58.6,
                    "unit": "EUR/MWh"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "da9c2ac74402fb223344a72c48b56f7dafe4068caf776b374bcdaa299926f900"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/decision_studies.py",
              "engine_sha256": "7730c9e9bd5fec2eb4249d70e0bacd0a7ba29921635aa88945f22f66df6439d1",
              "test_file": "apps/api/tests/test_decision_studies.py",
              "test_sha256": "857d6c292f08b499b133f2b42de828d700c3e82cf7472d84d7b1ccf5183f1dfd",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "All matrix values and route weights are user assumptions; no dispatch, balancing eligibility, fees, shape or contract terms are inferred.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-overview-merit"
    },
    {
      "id": "overview-carbon",
      "title": "Hourly versus static emissions",
      "summary": "Apply supplied hourly marginal factors and compare with one static factor on the same sampled generation.",
      "workspace": {
        "id": "overview",
        "name": "Command centre",
        "appRoute": "#overview"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "carbon",
        "equations": [
          "hourly emissions = Σ_h generation_h × marginal_factor_h",
          "static comparison = Σ_h generation_h × annual_static_factor"
        ],
        "limitations": [
          "Supplied factors are not recalculated or calibrated; this does not estimate lifecycle emissions, avoided credits or annualize a sample day."
        ],
        "sources": [
          {
            "title": "UNFCCC electricity-system emission-factor methodology",
            "url": "https://cdm.unfccc.int/Reference/catalogue/document?doc_id=000001719"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "static_factor_kgco2e_mwh",
            "label": "Static factor",
            "default": 400,
            "min": 0,
            "max": 5000,
            "step": 10,
            "unit": "kgCO2e/MWh"
          },
          {
            "name": "sample_hours",
            "label": "Sample hours",
            "default": 24,
            "min": 1,
            "max": 8760,
            "step": 1,
            "unit": "hours"
          },
          {
            "name": "generation_scale",
            "label": "Generation scale",
            "default": 1,
            "min": 0.01,
            "max": 100,
            "step": 0.1,
            "unit": "multiplier"
          }
        ],
        "jsonDataFields": [
          {
            "name": "generation_mwh",
            "label": "Hourly generation",
            "default": {
              "type": "array",
              "items": 24,
              "itemTypes": [
                "number"
              ],
              "sha256": "e7303136d19e9e340f26fcb75f64507459e1881c7cbff70c1524d4467d4c0022"
            }
          },
          {
            "name": "marginal_factors_kgco2e_mwh",
            "label": "Hourly marginal factors",
            "default": {
              "type": "array",
              "items": 24,
              "itemTypes": [
                "number"
              ],
              "sha256": "71b39889da6d2331e852037ccc64c8765900e14989a46d58834e0d37e843b8ce"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "580b5057ad05badedcad285a52632446d74ebb992fe47b6d8fb029f79418f96c",
        "evidenceSha256": "d274a32d97acfccb87cb4e976149159344c30aaa6670ae14d20835a268738a41",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"overview-carbon\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"overview-carbon\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Two hourly samples produce 2,800 kgCO₂e marginal, 2,000 kgCO₂e static, and an 800 kgCO₂e difference.",
          "testIds": [
            {
              "node_id": "tests/test_decision_studies.py::test_carbon_uses_hourly_marginal_factors_without_credits_or_day_annualization",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "static_factor_kgco2e_mwh",
              "unit": "kgCO2e/MWh",
              "minimum": 0,
              "maximum": 5000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "sample_hours",
              "unit": "hours",
              "minimum": 1,
              "maximum": 8760,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "generation_scale",
              "unit": "multiplier",
              "minimum": 0.01,
              "maximum": 100,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "generation_mwh",
              "label": "Hourly generation",
              "default": {
                "type": "array",
                "items": 24,
                "itemTypes": [
                  "number"
                ],
                "sha256": "e7303136d19e9e340f26fcb75f64507459e1881c7cbff70c1524d4467d4c0022"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "marginal_factors_kgco2e_mwh",
              "label": "Hourly marginal factors",
              "default": {
                "type": "array",
                "items": 24,
                "itemTypes": [
                  "number"
                ],
                "sha256": "71b39889da6d2331e852037ccc64c8765900e14989a46d58834e0d37e843b8ce"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/decision_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "580b5057ad05badedcad285a52632446d74ebb992fe47b6d8fb029f79418f96c",
          "evidenceSha256": "d274a32d97acfccb87cb4e976149159344c30aaa6670ae14d20835a268738a41",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"overview-carbon\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"overview-carbon\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Two hourly samples produce 2,800 kgCO₂e marginal, 2,000 kgCO₂e static, and an 800 kgCO₂e difference.",
            "testIds": [
              {
                "node_id": "tests/test_decision_studies.py::test_carbon_uses_hourly_marginal_factors_without_credits_or_day_annualization",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "static_factor_kgco2e_mwh below declared minimum 0",
            "static_factor_kgco2e_mwh above declared maximum 5000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "static_factor_kgco2e_mwh=0 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Hourly marginal emissions",
                  "value": 64565,
                  "unit": "kgCO2e"
                },
                {
                  "label": "Static-factor emissions",
                  "value": 0,
                  "unit": "kgCO2e"
                },
                {
                  "label": "Difference (hourly minus static)",
                  "value": 64565,
                  "unit": "kgCO2e"
                },
                {
                  "label": "Sample generation",
                  "value": 150,
                  "unit": "MWh"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "9eca133a1ea5122f09422e5734b117ee6db77d848c3fb0df2ab33fd019a6f182"
          }
        },
        "limitations": [
          "Supplied factors are not recalculated or calibrated; this does not estimate lifecycle emissions, avoided credits or annualize a sample day."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/decision_studies.py",
          "tests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "overview-carbon",
            "workspace": "overview",
            "title": "Hourly versus static emissions",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_decision_studies.py::test_carbon_uses_hourly_marginal_factors_without_credits_or_day_annualization",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Two hourly samples produce 2,800 kgCO₂e marginal, 2,000 kgCO₂e static, and an 800 kgCO₂e difference.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Hourly marginal emissions",
                        "value": 2800,
                        "unit": "kgCO2e"
                      },
                      {
                        "label": "Static-factor emissions",
                        "value": 2000,
                        "unit": "kgCO2e"
                      },
                      {
                        "label": "Difference (hourly minus static)",
                        "value": 800,
                        "unit": "kgCO2e"
                      },
                      {
                        "label": "Sample generation",
                        "value": 4,
                        "unit": "MWh"
                      }
                    ],
                    "scalar_details": {
                      "sample_hours": 2,
                      "generation_scale": 1,
                      "static_factor_kgco2e_mwh": 500,
                      "credits_tco2e": 0
                    },
                    "table_row_count": 2,
                    "result_sha256": "fef45279a3585c86e8f69253fd275f2a737086de4ba304a232f65ca69c901e29"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Hourly marginal emissions",
                    "value": 64565,
                    "unit": "kgCO2e"
                  },
                  {
                    "label": "Static-factor emissions",
                    "value": 60000,
                    "unit": "kgCO2e"
                  },
                  {
                    "label": "Difference (hourly minus static)",
                    "value": 4565,
                    "unit": "kgCO2e"
                  },
                  {
                    "label": "Sample generation",
                    "value": 150,
                    "unit": "MWh"
                  }
                ],
                "default_result_sha256": "9eca133a1ea5122f09422e5734b117ee6db77d848c3fb0df2ab33fd019a6f182"
              },
              "units": "kgCO₂e / MWh",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 4
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "static_factor_kgco2e_mwh=0 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Hourly marginal emissions",
                    "value": 64565,
                    "unit": "kgCO2e"
                  },
                  {
                    "label": "Static-factor emissions",
                    "value": 0,
                    "unit": "kgCO2e"
                  },
                  {
                    "label": "Difference (hourly minus static)",
                    "value": 64565,
                    "unit": "kgCO2e"
                  },
                  {
                    "label": "Sample generation",
                    "value": 150,
                    "unit": "MWh"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "9eca133a1ea5122f09422e5734b117ee6db77d848c3fb0df2ab33fd019a6f182"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/decision_studies.py",
              "engine_sha256": "7730c9e9bd5fec2eb4249d70e0bacd0a7ba29921635aa88945f22f66df6439d1",
              "test_file": "apps/api/tests/test_decision_studies.py",
              "test_sha256": "857d6c292f08b499b133f2b42de828d700c3e82cf7472d84d7b1ccf5183f1dfd",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Supplied factors are not recalculated or calibrated; this does not estimate lifecycle emissions, avoided credits or annualize a sample day.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-overview-carbon"
    },
    {
      "id": "markets-capture",
      "title": "Generation-weighted capture price",
      "summary": "Apply a transparent penetration-linked price adjustment to user-supplied intervals.",
      "workspace": {
        "id": "markets",
        "name": "Market intelligence",
        "appRoute": "#markets"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "capture",
        "equations": [
          "adjusted_price_t = spot_price_t − slope × (penetration − reference) × exposure_t",
          "capture price = Σ_t generation_t × adjusted_price_t / Σ_t generation_t"
        ],
        "limitations": [
          "The linear slope is a user-defined sensitivity, not an estimated market response or price forecast. Capture price is null when supplied generation sums to zero."
        ],
        "sources": [
          {
            "title": "Hirth, The market value of variable renewables",
            "url": "https://doi.org/10.1016/j.eneco.2013.02.004"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "penetration_fraction",
            "label": "Penetration",
            "default": 0.4,
            "min": 0,
            "max": 1,
            "step": 0.01,
            "unit": "fraction"
          },
          {
            "name": "reference_penetration_fraction",
            "label": "Reference penetration",
            "default": 0.1,
            "min": 0,
            "max": 1,
            "step": 0.01,
            "unit": "fraction"
          },
          {
            "name": "cannibalization_slope_eur_mwh",
            "label": "Price slope",
            "default": 100,
            "min": 0,
            "max": 5000,
            "step": 5,
            "unit": "EUR/MWh per fraction"
          }
        ],
        "jsonDataFields": [
          {
            "name": "intervals",
            "label": "Synthetic or user-provided intervals",
            "default": {
              "type": "array",
              "items": 24,
              "itemTypes": [
                "object"
              ],
              "sha256": "3adab35a365ae32950da8db0f4cabace97f76a8a0f79fcd07e62e0eb775ad9b9"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "685f83ab37b3f6252b0ffe1460acabc7683c5157d5488daf7ae4681beddf3e9f",
        "evidenceSha256": "034f015c15b795ee6f8126b00c13e4e2a0127eb2e59b4d644411efc11541caba",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"markets-capture\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"markets-capture\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Capture price is null when total supplied generation is zero; no price is fabricated from a zero denominator.",
          "testIds": [
            {
              "node_id": "tests/test_decision_studies.py::test_capture_price_zero_generation_has_null_not_fabricated_value",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "penetration_fraction",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "reference_penetration_fraction",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "cannibalization_slope_eur_mwh",
              "unit": "EUR/MWh per fraction",
              "minimum": 0,
              "maximum": 5000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "intervals",
              "label": "Synthetic or user-provided intervals",
              "default": {
                "type": "array",
                "items": 24,
                "itemTypes": [
                  "object"
                ],
                "sha256": "3adab35a365ae32950da8db0f4cabace97f76a8a0f79fcd07e62e0eb775ad9b9"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/decision_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "685f83ab37b3f6252b0ffe1460acabc7683c5157d5488daf7ae4681beddf3e9f",
          "evidenceSha256": "034f015c15b795ee6f8126b00c13e4e2a0127eb2e59b4d644411efc11541caba",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"markets-capture\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"markets-capture\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Capture price is null when total supplied generation is zero; no price is fabricated from a zero denominator.",
            "testIds": [
              {
                "node_id": "tests/test_decision_studies.py::test_capture_price_zero_generation_has_null_not_fabricated_value",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "penetration_fraction below declared minimum 0",
            "penetration_fraction above declared maximum 1",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "penetration_fraction=0 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Capture price",
                  "value": 76.696667,
                  "unit": "EUR/MWh"
                },
                {
                  "label": "Generation-weighted revenue",
                  "value": 11504.5,
                  "unit": "EUR"
                },
                {
                  "label": "Mean adjusted interval price",
                  "value": 53.729167,
                  "unit": "EUR/MWh"
                },
                {
                  "label": "Supplied generation",
                  "value": 150,
                  "unit": "MWh"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "8367225b9e38c76d76c1e2b8d216edacfa2818fb3be63eead7609cd262079383"
          }
        },
        "limitations": [
          "The linear slope is a user-defined sensitivity, not an estimated market response or price forecast. Capture price is null when supplied generation sums to zero."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/decision_studies.py",
          "tests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "markets-capture",
            "workspace": "markets",
            "title": "Generation-weighted capture price",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_decision_studies.py::test_capture_price_zero_generation_has_null_not_fabricated_value",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Capture price is null when total supplied generation is zero; no price is fabricated from a zero denominator.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Capture price",
                        "value": null,
                        "unit": "EUR/MWh"
                      },
                      {
                        "label": "Generation-weighted revenue",
                        "value": 0,
                        "unit": "EUR"
                      },
                      {
                        "label": "Mean adjusted interval price",
                        "value": 50,
                        "unit": "EUR/MWh"
                      },
                      {
                        "label": "Supplied generation",
                        "value": 0,
                        "unit": "MWh"
                      }
                    ],
                    "scalar_details": {
                      "penetration_fraction": 0.5,
                      "reference_penetration_fraction": 0.2,
                      "price_adjustment_eur_mwh": -30
                    },
                    "table_row_count": 1,
                    "result_sha256": "fcad70e7769eb60f8e8eaa967cb2ab18c609a36b1f28e45cff55899922b1e689"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Capture price",
                    "value": 44.416667,
                    "unit": "EUR/MWh"
                  },
                  {
                    "label": "Generation-weighted revenue",
                    "value": 6662.5,
                    "unit": "EUR"
                  },
                  {
                    "label": "Mean adjusted interval price",
                    "value": 40.645833,
                    "unit": "EUR/MWh"
                  },
                  {
                    "label": "Supplied generation",
                    "value": 150,
                    "unit": "MWh"
                  }
                ],
                "default_result_sha256": "8367225b9e38c76d76c1e2b8d216edacfa2818fb3be63eead7609cd262079383"
              },
              "units": "EUR/MWh; null at zero MWh",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 4
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "penetration_fraction=0 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Capture price",
                    "value": 76.696667,
                    "unit": "EUR/MWh"
                  },
                  {
                    "label": "Generation-weighted revenue",
                    "value": 11504.5,
                    "unit": "EUR"
                  },
                  {
                    "label": "Mean adjusted interval price",
                    "value": 53.729167,
                    "unit": "EUR/MWh"
                  },
                  {
                    "label": "Supplied generation",
                    "value": 150,
                    "unit": "MWh"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "8367225b9e38c76d76c1e2b8d216edacfa2818fb3be63eead7609cd262079383"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/decision_studies.py",
              "engine_sha256": "7730c9e9bd5fec2eb4249d70e0bacd0a7ba29921635aa88945f22f66df6439d1",
              "test_file": "apps/api/tests/test_decision_studies.py",
              "test_sha256": "857d6c292f08b499b133f2b42de828d700c3e82cf7472d84d7b1ccf5183f1dfd",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "The linear slope is a user-defined sensitivity, not an estimated market response or price forecast. Capture price is null when supplied generation sums to zero.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-markets-capture"
    },
    {
      "id": "markets-negative",
      "title": "Negative settlement intervals",
      "summary": "Count negative 15-minute prices and identify runs reaching a user-configured suspension duration.",
      "workspace": {
        "id": "markets",
        "name": "Market intelligence",
        "appRoute": "#markets"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "negative",
        "equations": [
          "negative interval if price < 0 and price ≤ configured threshold",
          "triggering run if consecutive_negative_intervals × 15 min ≥ configured duration"
        ],
        "limitations": [
          "Country label is metadata only. Any suspension setting is a hypothetical user scenario explicitly UNVERIFIED; no subsidy, settlement, legal or eligibility rule is asserted."
        ],
        "sources": [
          {
            "title": "Regulation (EU) 2019/943, consolidated text",
            "url": "https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02019R0943-20220623"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "interval_minutes",
            "label": "Interval",
            "default": 15,
            "min": 15,
            "max": 15,
            "step": 15,
            "unit": "minutes"
          },
          {
            "name": "suspension_duration_minutes",
            "label": "Suspension duration",
            "default": 60,
            "min": 15,
            "max": 10080,
            "step": 15,
            "unit": "minutes"
          },
          {
            "name": "negative_threshold_eur_mwh",
            "label": "Negative threshold",
            "default": 0,
            "min": -10000,
            "max": 0,
            "step": 1,
            "unit": "EUR/MWh"
          }
        ],
        "jsonDataFields": [
          {
            "name": "prices_eur_mwh",
            "label": "15-minute prices",
            "default": {
              "type": "array",
              "items": 24,
              "itemTypes": [
                "number"
              ],
              "sha256": "b72f651d2e0c2a97a15e13c7b815d581a16de1675a39e263eba870ef8e7bf8ea"
            }
          },
          {
            "name": "country_scenario",
            "label": "Hypothetical jurisdiction label",
            "default": {
              "type": "object",
              "keys": [
                "country_code"
              ],
              "sha256": "67c482243f604951ac751908daddf1a2b1b3f3cf9923d9526c2e2b25e909176d"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "eacf4af788e4b32d707f5939b6523b6cc83e40b5261a3a472c6c77660c2523e5",
        "evidenceSha256": "e9203607487263217ed62dc56ed9ac845566bc4b99d0ddf5fa043cf6db524037",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"markets-negative\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"markets-negative\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Five negative quarter-hours form two runs meeting a two-interval suspension threshold; country policy remains explicitly unverified.",
          "testIds": [
            {
              "node_id": "tests/test_decision_studies.py::test_negative_price_suspension_uses_15_minute_runs_and_warns_about_hypotheses",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "interval_minutes",
              "unit": "minutes",
              "minimum": 15,
              "maximum": 15,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "suspension_duration_minutes",
              "unit": "minutes",
              "minimum": 15,
              "maximum": 10080,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "negative_threshold_eur_mwh",
              "unit": "EUR/MWh",
              "minimum": -10000,
              "maximum": 0,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "prices_eur_mwh",
              "label": "15-minute prices",
              "default": {
                "type": "array",
                "items": 24,
                "itemTypes": [
                  "number"
                ],
                "sha256": "b72f651d2e0c2a97a15e13c7b815d581a16de1675a39e263eba870ef8e7bf8ea"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "country_scenario",
              "label": "Hypothetical jurisdiction label",
              "default": {
                "type": "object",
                "keys": [
                  "country_code"
                ],
                "sha256": "67c482243f604951ac751908daddf1a2b1b3f3cf9923d9526c2e2b25e909176d"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/decision_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "eacf4af788e4b32d707f5939b6523b6cc83e40b5261a3a472c6c77660c2523e5",
          "evidenceSha256": "e9203607487263217ed62dc56ed9ac845566bc4b99d0ddf5fa043cf6db524037",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"markets-negative\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"markets-negative\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Five negative quarter-hours form two runs meeting a two-interval suspension threshold; country policy remains explicitly unverified.",
            "testIds": [
              {
                "node_id": "tests/test_decision_studies.py::test_negative_price_suspension_uses_15_minute_runs_and_warns_about_hypotheses",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "interval_minutes below declared minimum 15",
            "interval_minutes above declared maximum 15",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "interval_minutes=15 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Negative settlement intervals",
                  "value": 5,
                  "unit": "15-minute intervals"
                },
                {
                  "label": "Negative settlement duration",
                  "value": 75,
                  "unit": "minutes"
                },
                {
                  "label": "Suspension-triggering runs",
                  "value": 1,
                  "unit": "runs"
                },
                {
                  "label": "Longest negative run",
                  "value": 75,
                  "unit": "minutes"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {
            "hypothetical_country_rule_status": "UNVERIFIED user scenario; no country rule applied"
          },
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "e10b0aba96df9fd0e36bce1e94878ce2837ae76613f2e9186302aa65da2e2586"
          }
        },
        "limitations": [
          "Country label is metadata only. Any suspension setting is a hypothetical user scenario explicitly UNVERIFIED; no subsidy, settlement, legal or eligibility rule is asserted."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/decision_studies.py",
          "tests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "markets-negative",
            "workspace": "markets",
            "title": "Negative settlement intervals",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_decision_studies.py::test_negative_price_suspension_uses_15_minute_runs_and_warns_about_hypotheses",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Five negative quarter-hours form two runs meeting a two-interval suspension threshold; country policy remains explicitly unverified.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Negative settlement intervals",
                        "value": 5,
                        "unit": "15-minute intervals"
                      },
                      {
                        "label": "Negative settlement duration",
                        "value": 75,
                        "unit": "minutes"
                      },
                      {
                        "label": "Suspension-triggering runs",
                        "value": 2,
                        "unit": "runs"
                      },
                      {
                        "label": "Longest negative run",
                        "value": 45,
                        "unit": "minutes"
                      }
                    ],
                    "scalar_details": {
                      "country_code_scenario_label": "DE",
                      "suspension_threshold_intervals": 2,
                      "hypothetical_country_rule_status": "UNVERIFIED user scenario; no country rule applied"
                    },
                    "table_row_count": 8,
                    "result_sha256": "1eab9f46f00a1f9e0e8d16e4903d0e68ff5964c7a8916a8630dbe46e81740003"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Negative settlement intervals",
                    "value": 5,
                    "unit": "15-minute intervals"
                  },
                  {
                    "label": "Negative settlement duration",
                    "value": 75,
                    "unit": "minutes"
                  },
                  {
                    "label": "Suspension-triggering runs",
                    "value": 1,
                    "unit": "runs"
                  },
                  {
                    "label": "Longest negative run",
                    "value": 75,
                    "unit": "minutes"
                  }
                ],
                "default_result_sha256": "e10b0aba96df9fd0e36bce1e94878ce2837ae76613f2e9186302aa65da2e2586"
              },
              "units": "15-minute intervals / minutes / runs",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 4
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "interval_minutes=15 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Negative settlement intervals",
                    "value": 5,
                    "unit": "15-minute intervals"
                  },
                  {
                    "label": "Negative settlement duration",
                    "value": 75,
                    "unit": "minutes"
                  },
                  {
                    "label": "Suspension-triggering runs",
                    "value": 1,
                    "unit": "runs"
                  },
                  {
                    "label": "Longest negative run",
                    "value": 75,
                    "unit": "minutes"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "e10b0aba96df9fd0e36bce1e94878ce2837ae76613f2e9186302aa65da2e2586"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/decision_studies.py",
              "engine_sha256": "7730c9e9bd5fec2eb4249d70e0bacd0a7ba29921635aa88945f22f66df6439d1",
              "test_file": "apps/api/tests/test_decision_studies.py",
              "test_sha256": "857d6c292f08b499b133f2b42de828d700c3e82cf7472d84d7b1ccf5183f1dfd",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Country label is metadata only. Any suspension setting is a hypothetical user scenario explicitly UNVERIFIED; no subsidy, settlement, legal or eligibility rule is asserted.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-markets-negative"
    },
    {
      "id": "markets-interconnection",
      "title": "Two-zone interconnection",
      "summary": "Choose the least-cost flow between two zones with quadratic supply curves and a bounded interconnector.",
      "workspace": {
        "id": "markets",
        "name": "Market intelligence",
        "appRoute": "#markets"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "interconnection",
        "equations": [
          "min_f C_A(d_A+f)+C_B(d_B−f)",
          "C_i(q)=a_i q + 0.5 b_i q²; −min(capacity,d_A) ≤ f ≤ min(capacity,d_B)"
        ],
        "limitations": [
          "Single-link two-zone convex dispatch proxy; ignores losses, network loop flows, ramping, reserves, bids, indivisibilities and actual market-coupling constraints."
        ],
        "sources": [
          {
            "title": "ENTSO-E Core day-ahead flow-based market coupling",
            "url": "https://www.entsoe.eu/bites/ccr-core/day-ahead/"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "capacity_mw",
            "label": "Interconnector capacity",
            "default": 500,
            "min": 0,
            "max": 1000000,
            "step": 10,
            "unit": "MW"
          },
          {
            "name": "supply_slope_a_eur_mwh_mw",
            "label": "Zone A supply slope",
            "default": 0.02,
            "min": 0.000001,
            "max": 1000,
            "step": 0.001,
            "unit": "EUR/MWh per MW"
          },
          {
            "name": "supply_slope_b_eur_mwh_mw",
            "label": "Zone B supply slope",
            "default": 0.02,
            "min": 0.000001,
            "max": 1000,
            "step": 0.001,
            "unit": "EUR/MWh per MW"
          }
        ],
        "jsonDataFields": [
          {
            "name": "intervals",
            "label": "Zone demand and supply intercepts",
            "default": {
              "type": "array",
              "items": 24,
              "itemTypes": [
                "object"
              ],
              "sha256": "a639be6f6fb0821bd3f5d48e998a50dcaeaf04d758d62817dd6c7f69540b4742"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "a342218f216fc5478e138046c13f739e7506300d5efa38efdf97d80d65bbbb9a",
        "evidenceSha256": "14e0cd9162051d049bb4b83dff4872288bc3088003d4edf48ea67a2ce6b0bc8a",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"markets-interconnection\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"markets-interconnection\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Transfer flow is exactly zero at zero interconnection capacity even with a price spread.",
          "testIds": [
            {
              "node_id": "tests/test_decision_studies.py::test_interconnection_zero_capacity_cannot_create_flow",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "capacity_mw",
              "unit": "MW",
              "minimum": 0,
              "maximum": 1000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "supply_slope_a_eur_mwh_mw",
              "unit": "EUR/MWh per MW",
              "minimum": 0.000001,
              "maximum": 1000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "supply_slope_b_eur_mwh_mw",
              "unit": "EUR/MWh per MW",
              "minimum": 0.000001,
              "maximum": 1000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "intervals",
              "label": "Zone demand and supply intercepts",
              "default": {
                "type": "array",
                "items": 24,
                "itemTypes": [
                  "object"
                ],
                "sha256": "a639be6f6fb0821bd3f5d48e998a50dcaeaf04d758d62817dd6c7f69540b4742"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/decision_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "a342218f216fc5478e138046c13f739e7506300d5efa38efdf97d80d65bbbb9a",
          "evidenceSha256": "14e0cd9162051d049bb4b83dff4872288bc3088003d4edf48ea67a2ce6b0bc8a",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"markets-interconnection\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"markets-interconnection\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Transfer flow is exactly zero at zero interconnection capacity even with a price spread.",
            "testIds": [
              {
                "node_id": "tests/test_decision_studies.py::test_interconnection_zero_capacity_cannot_create_flow",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "capacity_mw below declared minimum 0",
            "capacity_mw above declared maximum 1000000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "capacity_mw=0 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Total absolute flow",
                  "value": 0,
                  "unit": "MW-intervals"
                },
                {
                  "label": "Net welfare gain versus isolated zones",
                  "value": 0,
                  "unit": "EUR per interval-set"
                },
                {
                  "label": "Configured transfer capacity",
                  "value": 0,
                  "unit": "MW"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "dd72553d3ddb07c10323edece678042146ac389cf98715fda03ff97f227833f2"
          }
        },
        "limitations": [
          "Single-link two-zone convex dispatch proxy; ignores losses, network loop flows, ramping, reserves, bids, indivisibilities and actual market-coupling constraints."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/decision_studies.py",
          "tests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "markets-interconnection",
            "workspace": "markets",
            "title": "Two-zone interconnection",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_decision_studies.py::test_interconnection_zero_capacity_cannot_create_flow",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Transfer flow is exactly zero at zero interconnection capacity even with a price spread.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Total absolute flow",
                        "value": 0,
                        "unit": "MW-intervals"
                      },
                      {
                        "label": "Net welfare gain versus isolated zones",
                        "value": 0,
                        "unit": "EUR per interval-set"
                      },
                      {
                        "label": "Configured transfer capacity",
                        "value": 0,
                        "unit": "MW"
                      }
                    ],
                    "scalar_details": {
                      "flow_sign_convention": "positive exports from zone A to zone B",
                      "supply_curve": "marginal cost = intercept + slope × output"
                    },
                    "table_row_count": 1,
                    "result_sha256": "6bf008a18b480db2c6b1bbf244a10a4e87892607bc3fe7614f572eea735c2529"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Total absolute flow",
                    "value": 8427,
                    "unit": "MW-intervals"
                  },
                  {
                    "label": "Net welfare gain versus isolated zones",
                    "value": 59594.045,
                    "unit": "EUR per interval-set"
                  },
                  {
                    "label": "Configured transfer capacity",
                    "value": 500,
                    "unit": "MW"
                  }
                ],
                "default_result_sha256": "dd72553d3ddb07c10323edece678042146ac389cf98715fda03ff97f227833f2"
              },
              "units": "MW",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 3
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "capacity_mw=0 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Total absolute flow",
                    "value": 0,
                    "unit": "MW-intervals"
                  },
                  {
                    "label": "Net welfare gain versus isolated zones",
                    "value": 0,
                    "unit": "EUR per interval-set"
                  },
                  {
                    "label": "Configured transfer capacity",
                    "value": 0,
                    "unit": "MW"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "dd72553d3ddb07c10323edece678042146ac389cf98715fda03ff97f227833f2"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/decision_studies.py",
              "engine_sha256": "7730c9e9bd5fec2eb4249d70e0bacd0a7ba29921635aa88945f22f66df6439d1",
              "test_file": "apps/api/tests/test_decision_studies.py",
              "test_sha256": "857d6c292f08b499b133f2b42de828d700c3e82cf7472d84d7b1ccf5183f1dfd",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Single-link two-zone convex dispatch proxy; ignores losses, network loop flows, ramping, reserves, bids, indivisibilities and actual market-coupling constraints.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-markets-interconnection"
    },
    {
      "id": "forecast-regimes",
      "title": "Three-state price regimes",
      "summary": "Generate seeded annual scenario paths from normal, scarcity and oversupply Markov states.",
      "workspace": {
        "id": "forecast",
        "name": "Forecast studio",
        "appRoute": "#forecast"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "regime",
        "equations": [
          "S_t ∼ categorical(P[S_{t−1},·])",
          "price_t | S_t=s ∼ Normal(state_mean_s, state_volatility_s²)"
        ],
        "limitations": [
          "Seeded annual scenarios only; user-supplied transition probabilities and state parameters are not calibrated, backtested or market forecasts."
        ],
        "sources": [
          {
            "title": "Hamilton, A new approach to the economic analysis of nonstationary time series",
            "url": "https://doi.org/10.2307/1912559"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "years",
            "label": "Horizon",
            "default": 20,
            "min": 1,
            "max": 40,
            "step": 1,
            "unit": "years"
          },
          {
            "name": "paths",
            "label": "Paths",
            "default": 1000,
            "min": 20,
            "max": 10000,
            "step": 100,
            "unit": "paths"
          },
          {
            "name": "seed",
            "label": "Seed",
            "default": 42,
            "min": 0,
            "max": 4294967295,
            "step": 1,
            "unit": "integer"
          }
        ],
        "jsonDataFields": [
          {
            "name": "state_means_eur_mwh",
            "label": "State price means",
            "default": {
              "type": "array",
              "items": 3,
              "itemTypes": [
                "number"
              ],
              "sha256": "7503ddfc0ab4b0838ec2e1d94d8f9c721c58bdc78ee1a29a24422988b61f1090"
            }
          },
          {
            "name": "state_volatilities_eur_mwh",
            "label": "State volatilities",
            "default": {
              "type": "array",
              "items": 3,
              "itemTypes": [
                "number"
              ],
              "sha256": "78773dea9fb438140498f712446cbe2c8c0d4d064aed05287a89d11cdb36a86f"
            }
          },
          {
            "name": "transition_matrix",
            "label": "Transition probabilities",
            "default": {
              "type": "array",
              "items": 3,
              "itemTypes": [
                "array"
              ],
              "sha256": "420f835981c8cb3bf1c70dc11705a0cd5616a01d7367a8b1bc1472c845cbf041"
            }
          },
          {
            "name": "initial_state_probabilities",
            "label": "Initial probabilities",
            "default": {
              "type": "array",
              "items": 3,
              "itemTypes": [
                "number"
              ],
              "sha256": "1df1fa431ecaacee744db67ebb1e05ecc0994dc725e3fecfb42831655c9e56f9"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "6f9e112d753bf0d942dc3eefc5d5f58914154a89b279957377f7b9fd172a2cdf",
        "evidenceSha256": "31defb04af903be902409a7a79214d90104ac62bf1fbdfd51d626cf80963a9d3",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"forecast-regimes\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"forecast-regimes\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Identical seed and inputs reproduce paths; a changed seed changes them and p10 <= p50 <= p90.",
          "testIds": [
            {
              "node_id": "tests/test_decision_studies.py::test_markov_paths_are_seeded_and_quantiles_are_ordered",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "years",
              "unit": "years",
              "minimum": 1,
              "maximum": 40,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "paths",
              "unit": "paths",
              "minimum": 20,
              "maximum": 10000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "seed",
              "unit": "integer",
              "minimum": 0,
              "maximum": 4294967295,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "state_means_eur_mwh",
              "label": "State price means",
              "default": {
                "type": "array",
                "items": 3,
                "itemTypes": [
                  "number"
                ],
                "sha256": "7503ddfc0ab4b0838ec2e1d94d8f9c721c58bdc78ee1a29a24422988b61f1090"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "state_volatilities_eur_mwh",
              "label": "State volatilities",
              "default": {
                "type": "array",
                "items": 3,
                "itemTypes": [
                  "number"
                ],
                "sha256": "78773dea9fb438140498f712446cbe2c8c0d4d064aed05287a89d11cdb36a86f"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "transition_matrix",
              "label": "Transition probabilities",
              "default": {
                "type": "array",
                "items": 3,
                "itemTypes": [
                  "array"
                ],
                "sha256": "420f835981c8cb3bf1c70dc11705a0cd5616a01d7367a8b1bc1472c845cbf041"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "initial_state_probabilities",
              "label": "Initial probabilities",
              "default": {
                "type": "array",
                "items": 3,
                "itemTypes": [
                  "number"
                ],
                "sha256": "1df1fa431ecaacee744db67ebb1e05ecc0994dc725e3fecfb42831655c9e56f9"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/decision_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "6f9e112d753bf0d942dc3eefc5d5f58914154a89b279957377f7b9fd172a2cdf",
          "evidenceSha256": "31defb04af903be902409a7a79214d90104ac62bf1fbdfd51d626cf80963a9d3",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"forecast-regimes\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"forecast-regimes\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Identical seed and inputs reproduce paths; a changed seed changes them and p10 <= p50 <= p90.",
            "testIds": [
              {
                "node_id": "tests/test_decision_studies.py::test_markov_paths_are_seeded_and_quantiles_are_ordered",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "years below declared minimum 1",
            "years above declared maximum 40",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "years=1 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Paths",
                  "value": 1000,
                  "unit": "paths"
                },
                {
                  "label": "Horizon",
                  "value": 1,
                  "unit": "years"
                },
                {
                  "label": "Seed",
                  "value": 42,
                  "unit": "integer"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [
            "seed"
          ],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "70689c96e8b0cf7c928afe7aa892042e5c86d44f42eacf5e9145ad01a5aa179e"
          }
        },
        "limitations": [
          "Seeded annual scenarios only; user-supplied transition probabilities and state parameters are not calibrated, backtested or market forecasts."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/decision_studies.py",
          "tests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "forecast-regimes",
            "workspace": "forecast",
            "title": "Three-state price regimes",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_decision_studies.py::test_markov_paths_are_seeded_and_quantiles_are_ordered",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Identical seed and inputs reproduce paths; a changed seed changes them and p10 <= p50 <= p90.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Paths",
                        "value": 80,
                        "unit": "paths"
                      },
                      {
                        "label": "Horizon",
                        "value": 5,
                        "unit": "years"
                      },
                      {
                        "label": "Seed",
                        "value": 13,
                        "unit": "integer"
                      }
                    ],
                    "scalar_details": {
                      "seed": 13,
                      "paths": 80
                    },
                    "table_row_count": 5,
                    "result_sha256": "0fd53a98679aebb8bcefcde36913d4b84a4f74869e5c347d07eb63557497047d"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Paths",
                        "value": 80,
                        "unit": "paths"
                      },
                      {
                        "label": "Horizon",
                        "value": 5,
                        "unit": "years"
                      },
                      {
                        "label": "Seed",
                        "value": 13,
                        "unit": "integer"
                      }
                    ],
                    "scalar_details": {
                      "seed": 13,
                      "paths": 80
                    },
                    "table_row_count": 5,
                    "result_sha256": "0fd53a98679aebb8bcefcde36913d4b84a4f74869e5c347d07eb63557497047d"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Paths",
                        "value": 80,
                        "unit": "paths"
                      },
                      {
                        "label": "Horizon",
                        "value": 5,
                        "unit": "years"
                      },
                      {
                        "label": "Seed",
                        "value": 14,
                        "unit": "integer"
                      }
                    ],
                    "scalar_details": {
                      "seed": 14,
                      "paths": 80
                    },
                    "table_row_count": 5,
                    "result_sha256": "7503fd6695cf5c204af7878f632c0391f313d2acb0b1321a011e4601277ff440"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Paths",
                    "value": 1000,
                    "unit": "paths"
                  },
                  {
                    "label": "Horizon",
                    "value": 20,
                    "unit": "years"
                  },
                  {
                    "label": "Seed",
                    "value": 42,
                    "unit": "integer"
                  }
                ],
                "default_result_sha256": "70689c96e8b0cf7c928afe7aa892042e5c86d44f42eacf5e9145ad01a5aa179e"
              },
              "units": "EUR/MWh / paths",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 3
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "years=1 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Paths",
                    "value": 1000,
                    "unit": "paths"
                  },
                  {
                    "label": "Horizon",
                    "value": 1,
                    "unit": "years"
                  },
                  {
                    "label": "Seed",
                    "value": 42,
                    "unit": "integer"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "70689c96e8b0cf7c928afe7aa892042e5c86d44f42eacf5e9145ad01a5aa179e"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/decision_studies.py",
              "engine_sha256": "7730c9e9bd5fec2eb4249d70e0bacd0a7ba29921635aa88945f22f66df6439d1",
              "test_file": "apps/api/tests/test_decision_studies.py",
              "test_sha256": "857d6c292f08b499b133f2b42de828d700c3e82cf7472d84d7b1ccf5183f1dfd",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Seeded annual scenarios only; user-supplied transition probabilities and state parameters are not calibrated, backtested or market forecasts.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-forecast-regimes"
    },
    {
      "id": "forecast-commodities",
      "title": "Correlated commodity shocks",
      "summary": "Simulate correlated gas, carbon and power price paths using a bounded, PSD correlation matrix.",
      "workspace": {
        "id": "forecast",
        "name": "Forecast studio",
        "appRoute": "#forecast"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "commodities",
        "equations": [
          "Z_t = L ε_t, L Lᵀ = correlation matrix",
          "P_j,t = P_j,t−1 exp((μ_j−0.5σ_j²)+σ_j Z_j,t)"
        ],
        "limitations": [
          "Synthetic geometric-random-walk shocks with constant user inputs; no forward curves, cointegration, structural fuel dispatch, calibration or forecast claim."
        ],
        "sources": [
          {
            "title": "NumPy multivariate normal sampling reference",
            "url": "https://numpy.org/doc/stable/reference/random/generated/numpy.random.Generator.multivariate_normal.html"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "years",
            "label": "Horizon",
            "default": 20,
            "min": 1,
            "max": 40,
            "step": 1,
            "unit": "years"
          },
          {
            "name": "paths",
            "label": "Paths",
            "default": 1000,
            "min": 20,
            "max": 5000,
            "step": 100,
            "unit": "paths"
          },
          {
            "name": "seed",
            "label": "Seed",
            "default": 42,
            "min": 0,
            "max": 4294967295,
            "step": 1,
            "unit": "integer"
          }
        ],
        "jsonDataFields": [
          {
            "name": "starting_prices",
            "label": "Starting gas, carbon and power prices",
            "default": {
              "type": "array",
              "items": 3,
              "itemTypes": [
                "number"
              ],
              "sha256": "009cfc5a5d3f09a9404a35e659feca0c654006caba77253fa5e58a27178ae279"
            }
          },
          {
            "name": "annual_volatilities",
            "label": "Annual volatilities",
            "default": {
              "type": "array",
              "items": 3,
              "itemTypes": [
                "number"
              ],
              "sha256": "5bc297ef73bd42e8f4b3f05a4be5df313d6937fac161637443835948eecd3905"
            }
          },
          {
            "name": "annual_log_drifts",
            "label": "Annual log drifts",
            "default": {
              "type": "array",
              "items": 3,
              "itemTypes": [
                "number"
              ],
              "sha256": "eb52fd75fdccfeb5a497bdc83277ad64c8ff226314d5f323f5ad79c9ce3c4661"
            }
          },
          {
            "name": "correlation_matrix",
            "label": "Correlation matrix",
            "default": {
              "type": "array",
              "items": 3,
              "itemTypes": [
                "array"
              ],
              "sha256": "6de851cd25e3cf9c650f712a3dacd3e6d20a0e0a865939c123680d074fb66789"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "9b982aecc8668d522e4bc837b4efcf02baade464bb8634048df0898cfb3a9980",
        "evidenceSha256": "8a81664f0baac4cc18ceadd7dd2143391c565272ffc71295122d31460681d408",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"forecast-commodities\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"forecast-commodities\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "An explicitly indefinite correlation matrix is rejected before path generation.",
          "testIds": [
            {
              "node_id": "tests/test_decision_studies.py::test_commodity_correlation_matrix_must_be_positive_semidefinite",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "years",
              "unit": "years",
              "minimum": 1,
              "maximum": 40,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "paths",
              "unit": "paths",
              "minimum": 20,
              "maximum": 5000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "seed",
              "unit": "integer",
              "minimum": 0,
              "maximum": 4294967295,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "starting_prices",
              "label": "Starting gas, carbon and power prices",
              "default": {
                "type": "array",
                "items": 3,
                "itemTypes": [
                  "number"
                ],
                "sha256": "009cfc5a5d3f09a9404a35e659feca0c654006caba77253fa5e58a27178ae279"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "annual_volatilities",
              "label": "Annual volatilities",
              "default": {
                "type": "array",
                "items": 3,
                "itemTypes": [
                  "number"
                ],
                "sha256": "5bc297ef73bd42e8f4b3f05a4be5df313d6937fac161637443835948eecd3905"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "annual_log_drifts",
              "label": "Annual log drifts",
              "default": {
                "type": "array",
                "items": 3,
                "itemTypes": [
                  "number"
                ],
                "sha256": "eb52fd75fdccfeb5a497bdc83277ad64c8ff226314d5f323f5ad79c9ce3c4661"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "correlation_matrix",
              "label": "Correlation matrix",
              "default": {
                "type": "array",
                "items": 3,
                "itemTypes": [
                  "array"
                ],
                "sha256": "6de851cd25e3cf9c650f712a3dacd3e6d20a0e0a865939c123680d074fb66789"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/decision_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "9b982aecc8668d522e4bc837b4efcf02baade464bb8634048df0898cfb3a9980",
          "evidenceSha256": "8a81664f0baac4cc18ceadd7dd2143391c565272ffc71295122d31460681d408",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"forecast-commodities\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"forecast-commodities\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "An explicitly indefinite correlation matrix is rejected before path generation.",
            "testIds": [
              {
                "node_id": "tests/test_decision_studies.py::test_commodity_correlation_matrix_must_be_positive_semidefinite",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "years below declared minimum 1",
            "years above declared maximum 40",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "years=1 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Paths",
                  "value": 1000,
                  "unit": "paths"
                },
                {
                  "label": "Horizon",
                  "value": 1,
                  "unit": "years"
                },
                {
                  "label": "Seed",
                  "value": 42,
                  "unit": "integer"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [
            "seed"
          ],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "de00732d060598a5419f67bbb31f86f68af47af6ae1a4512d398e262e1b3e280"
          }
        },
        "limitations": [
          "Synthetic geometric-random-walk shocks with constant user inputs; no forward curves, cointegration, structural fuel dispatch, calibration or forecast claim."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/decision_studies.py",
          "tests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "forecast-commodities",
            "workspace": "forecast",
            "title": "Correlated commodity shocks",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_decision_studies.py::test_commodity_correlation_matrix_must_be_positive_semidefinite",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "An explicitly indefinite correlation matrix is rejected before path generation.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "raised",
                    "exception": "ValidationError",
                    "message": "1 validation error for CommoditiesInput\n  Value error, Correlation matrix must be positive semidefinite. [type=value_error, input_value={'years': 3, 'paths': 20,..., 0.9], [-0.9, 0.9, 1]]}, input_type=dict]\n    For further information visit"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Paths",
                    "value": 1000,
                    "unit": "paths"
                  },
                  {
                    "label": "Horizon",
                    "value": 20,
                    "unit": "years"
                  },
                  {
                    "label": "Seed",
                    "value": 42,
                    "unit": "integer"
                  }
                ],
                "default_result_sha256": "de00732d060598a5419f67bbb31f86f68af47af6ae1a4512d398e262e1b3e280"
              },
              "units": "correlation matrix",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 3
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "years=1 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Paths",
                    "value": 1000,
                    "unit": "paths"
                  },
                  {
                    "label": "Horizon",
                    "value": 1,
                    "unit": "years"
                  },
                  {
                    "label": "Seed",
                    "value": 42,
                    "unit": "integer"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "de00732d060598a5419f67bbb31f86f68af47af6ae1a4512d398e262e1b3e280"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/decision_studies.py",
              "engine_sha256": "7730c9e9bd5fec2eb4249d70e0bacd0a7ba29921635aa88945f22f66df6439d1",
              "test_file": "apps/api/tests/test_decision_studies.py",
              "test_sha256": "857d6c292f08b499b133f2b42de828d700c3e82cf7472d84d7b1ccf5183f1dfd",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Synthetic geometric-random-walk shocks with constant user inputs; no forward curves, cointegration, structural fuel dispatch, calibration or forecast claim.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-forecast-commodities"
    },
    {
      "id": "forecast-tail",
      "title": "PPA settlement tail risk",
      "summary": "Estimate empirical 95% and 99% loss VaR/CVaR from seeded annual PPA settlement paths.",
      "workspace": {
        "id": "forecast",
        "name": "Forecast studio",
        "appRoute": "#forecast"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "tail",
        "equations": [
          "spot_t = strike + Σ_{k≤t} Normal(0, (volatility × strike)²)",
          "loss_t = (spot_t − strike) × volume",
          "CVaR_α = mean of the largest ceil((1−α)N) empirical losses"
        ],
        "limitations": [
          "Synthetic arithmetic price walk; signed buyer settlement losses versus strike, not project revenue, calibrated PPA basis, hedge effectiveness or a probability guarantee."
        ],
        "sources": [
          {
            "title": "Rockafellar and Uryasev, Optimization of Conditional Value-at-Risk",
            "url": "https://doi.org/10.21314/JOR.2000.038"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "strike_eur_mwh",
            "label": "PPA strike",
            "default": 80,
            "min": 0.01,
            "max": 5000,
            "step": 1,
            "unit": "EUR/MWh"
          },
          {
            "name": "volume_mwh",
            "label": "Settlement volume",
            "default": 10000,
            "min": 0,
            "max": 10000000,
            "step": 100,
            "unit": "MWh/year"
          },
          {
            "name": "years",
            "label": "Horizon",
            "default": 10,
            "min": 1,
            "max": 40,
            "step": 1,
            "unit": "years"
          },
          {
            "name": "paths",
            "label": "Paths",
            "default": 1500,
            "min": 100,
            "max": 10000,
            "step": 100,
            "unit": "paths"
          },
          {
            "name": "seed",
            "label": "Seed",
            "default": 42,
            "min": 0,
            "max": 4294967295,
            "step": 1,
            "unit": "integer"
          },
          {
            "name": "annual_volatility",
            "label": "Annual volatility",
            "default": 0.25,
            "min": 0,
            "max": 2,
            "step": 0.05,
            "unit": "fraction"
          }
        ],
        "jsonDataFields": [
          {
            "name": "histogram_year",
            "label": "Selected year and histogram bins",
            "default": {
              "type": "object",
              "keys": [
                "bins",
                "year"
              ],
              "sha256": "7c3888e0f543e35c434e0fa049ad3ce20f5a3bf020de8eb57ab5884ddea0ab87"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "9cd8d1581533cc8fb1868c81def6688e5786cde3644fda43fbf18ab031efa354",
        "evidenceSha256": "cc414c25643cc97ca8992cb08b679f6021b204f074a47f9ae8b18f60f0c8e98a",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"forecast-tail\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"forecast-tail\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Losses equal (price - 80 EUR/MWh) x 100 MWh; 95% and 99% CVaR are not below their VaR.",
          "testIds": [
            {
              "node_id": "tests/test_decision_studies.py::test_tail_metrics_use_losses_and_cvar_is_not_below_var",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "strike_eur_mwh",
              "unit": "EUR/MWh",
              "minimum": 0.01,
              "maximum": 5000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "volume_mwh",
              "unit": "MWh/year",
              "minimum": 0,
              "maximum": 10000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "years",
              "unit": "years",
              "minimum": 1,
              "maximum": 40,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "paths",
              "unit": "paths",
              "minimum": 100,
              "maximum": 10000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "seed",
              "unit": "integer",
              "minimum": 0,
              "maximum": 4294967295,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "annual_volatility",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 2,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "histogram_year",
              "label": "Selected year and histogram bins",
              "default": {
                "type": "object",
                "keys": [
                  "bins",
                  "year"
                ],
                "sha256": "7c3888e0f543e35c434e0fa049ad3ce20f5a3bf020de8eb57ab5884ddea0ab87"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/decision_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "9cd8d1581533cc8fb1868c81def6688e5786cde3644fda43fbf18ab031efa354",
          "evidenceSha256": "cc414c25643cc97ca8992cb08b679f6021b204f074a47f9ae8b18f60f0c8e98a",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"forecast-tail\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"forecast-tail\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Losses equal (price - 80 EUR/MWh) x 100 MWh; 95% and 99% CVaR are not below their VaR.",
            "testIds": [
              {
                "node_id": "tests/test_decision_studies.py::test_tail_metrics_use_losses_and_cvar_is_not_below_var",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "strike_eur_mwh below declared minimum 0.01",
            "strike_eur_mwh above declared maximum 5000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "strike_eur_mwh=0.01 (min bound)",
              "metric_snapshot": [
                {
                  "label": "95% loss VaR",
                  "value": 39.75,
                  "unit": "EUR"
                },
                {
                  "label": "95% loss CVaR",
                  "value": 48.06,
                  "unit": "EUR"
                },
                {
                  "label": "99% loss VaR",
                  "value": 53.88,
                  "unit": "EUR"
                },
                {
                  "label": "99% loss CVaR",
                  "value": 58.99,
                  "unit": "EUR"
                },
                {
                  "label": "Selected histogram year",
                  "value": 1,
                  "unit": "year"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [
            "seed"
          ],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "fc13b8f0323a95ba485a6564edeb41937a1ed2f98accb809dd5bcd9b2506eeba"
          }
        },
        "limitations": [
          "Synthetic arithmetic price walk; signed buyer settlement losses versus strike, not project revenue, calibrated PPA basis, hedge effectiveness or a probability guarantee."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/decision_studies.py",
          "tests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "forecast-tail",
            "workspace": "forecast",
            "title": "PPA settlement tail risk",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_decision_studies.py::test_tail_metrics_use_losses_and_cvar_is_not_below_var",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Losses equal (price - 80 EUR/MWh) x 100 MWh; 95% and 99% CVaR are not below their VaR.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "95% loss VaR",
                        "value": 4956.27,
                        "unit": "EUR"
                      },
                      {
                        "label": "95% loss CVaR",
                        "value": 6433.76,
                        "unit": "EUR"
                      },
                      {
                        "label": "99% loss VaR",
                        "value": 8474.26,
                        "unit": "EUR"
                      },
                      {
                        "label": "99% loss CVaR",
                        "value": 8528.98,
                        "unit": "EUR"
                      },
                      {
                        "label": "Selected histogram year",
                        "value": 2,
                        "unit": "year"
                      }
                    ],
                    "scalar_details": {
                      "seed": 19,
                      "paths": 200,
                      "loss_convention": "positive means buyer settlement cost above fixed strike; negative means settlement benefit"
                    },
                    "table_row_count": 2,
                    "result_sha256": "938f75522b454f7345c5a87dd21ca9a053dc737bf4ffa2636e19f04b7a4605af"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "95% loss VaR",
                    "value": 317968.45,
                    "unit": "EUR"
                  },
                  {
                    "label": "95% loss CVaR",
                    "value": 384502.62,
                    "unit": "EUR"
                  },
                  {
                    "label": "99% loss VaR",
                    "value": 431057.52,
                    "unit": "EUR"
                  },
                  {
                    "label": "99% loss CVaR",
                    "value": 471905.16,
                    "unit": "EUR"
                  },
                  {
                    "label": "Selected histogram year",
                    "value": 1,
                    "unit": "year"
                  }
                ],
                "default_result_sha256": "fc13b8f0323a95ba485a6564edeb41937a1ed2f98accb809dd5bcd9b2506eeba"
              },
              "units": "EUR / EUR/MWh / MWh",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 5
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "strike_eur_mwh=0.01 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "95% loss VaR",
                    "value": 39.75,
                    "unit": "EUR"
                  },
                  {
                    "label": "95% loss CVaR",
                    "value": 48.06,
                    "unit": "EUR"
                  },
                  {
                    "label": "99% loss VaR",
                    "value": 53.88,
                    "unit": "EUR"
                  },
                  {
                    "label": "99% loss CVaR",
                    "value": 58.99,
                    "unit": "EUR"
                  },
                  {
                    "label": "Selected histogram year",
                    "value": 1,
                    "unit": "year"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "fc13b8f0323a95ba485a6564edeb41937a1ed2f98accb809dd5bcd9b2506eeba"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/decision_studies.py",
              "engine_sha256": "7730c9e9bd5fec2eb4249d70e0bacd0a7ba29921635aa88945f22f66df6439d1",
              "test_file": "apps/api/tests/test_decision_studies.py",
              "test_sha256": "857d6c292f08b499b133f2b42de828d700c3e82cf7472d84d7b1ccf5183f1dfd",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Synthetic arithmetic price walk; signed buyer settlement losses versus strike, not project revenue, calibrated PPA basis, hedge effectiveness or a probability guarantee.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-forecast-tail"
    },
    {
      "id": "investment-sculpt",
      "title": "DSCR-constrained sculpted debt",
      "summary": "Compare the existing finance screen and annuity sizing with a non-negative-amortization CFADS sculpting bound.",
      "workspace": {
        "id": "investment",
        "name": "Investment desk",
        "appRoute": "#investment"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "sculpt",
        "equations": [
          "target service_t = max(0, CFADS_t) / target_DSCR",
          "debt capacity = largest initial balance whose target-limited service amortizes within tenor without service below interest",
          "balance_t = balance_{t−1} + interest_t − principal_t"
        ],
        "limitations": [
          "Uses the existing pre-tax nominal project-finance engine and its rounded annual CFADS. No taxes, DSRA, fees, reserve facility or legal covenant is added; infeasible requests are disclosed."
        ],
        "sources": [
          {
            "title": "NREL System Advisor Model help, DSCR debt sizing",
            "url": "https://sam.nrel.gov/images/web_page_files/sam-help-2025-4-16.pdf"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "target_dscr",
            "label": "Target DSCR",
            "default": 1.3,
            "min": 1,
            "max": 5,
            "step": 0.05,
            "unit": "ratio"
          },
          {
            "name": "requested_debt_eur",
            "label": "Requested debt",
            "default": 1760000,
            "min": 0,
            "max": 1000000000,
            "step": 10000,
            "unit": "EUR"
          },
          {
            "name": "discount_rate",
            "label": "Project discount rate",
            "default": 0.08,
            "min": 0,
            "max": 0.4,
            "step": 0.005,
            "unit": "fraction"
          }
        ],
        "jsonDataFields": [
          {
            "name": "finance_request",
            "label": "Existing project-finance assumptions",
            "default": {
              "type": "object",
              "keys": [
                "annual_gross_margin_eur",
                "annual_margin_degradation",
                "annual_opex_eur",
                "augmentation_cost_eur",
                "augmentation_retention_restore",
                "augmentation_year",
                "capex_eur",
                "contract_years",
                "contracted_fraction",
                "debt_fraction",
                "debt_interest_rate",
                "debt_tenor_years",
                "discount_rate",
                "escalation_rate",
                "merchant_margin_factor",
                "minimum_dscr",
                "years"
              ],
              "sha256": "bdcb1f1f5bccc2f8c5f8109a2761dd40dbb09116c0886ea02da550c86215ea69"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "dd9ed5077bd5f46e5e0dc2f76fad6f593a4d88272dd0f7fdbc1f7b68c60194f7",
        "evidenceSha256": "7f1eef49684d1cfbe7d4185e47301cd74fd1d168151f33c7c828ca61ffd57c19",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"investment-sculpt\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"investment-sculpt\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Zero-rate debt amortizes 50,000 EUR to zero; with zero debt, DSCR is null rather than a fabricated ratio.",
          "testIds": [
            {
              "node_id": "tests/test_decision_studies.py::test_sculpting_reconciles_amortization_and_handles_zero_rate_and_zero_debt",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "target_dscr",
              "unit": "ratio",
              "minimum": 1,
              "maximum": 5,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "requested_debt_eur",
              "unit": "EUR",
              "minimum": 0,
              "maximum": 1000000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "discount_rate",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 0.4,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "finance_request",
              "label": "Existing project-finance assumptions",
              "default": {
                "type": "object",
                "keys": [
                  "annual_gross_margin_eur",
                  "annual_margin_degradation",
                  "annual_opex_eur",
                  "augmentation_cost_eur",
                  "augmentation_retention_restore",
                  "augmentation_year",
                  "capex_eur",
                  "contract_years",
                  "contracted_fraction",
                  "debt_fraction",
                  "debt_interest_rate",
                  "debt_tenor_years",
                  "discount_rate",
                  "escalation_rate",
                  "merchant_margin_factor",
                  "minimum_dscr",
                  "years"
                ],
                "sha256": "bdcb1f1f5bccc2f8c5f8109a2761dd40dbb09116c0886ea02da550c86215ea69"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/decision_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "dd9ed5077bd5f46e5e0dc2f76fad6f593a4d88272dd0f7fdbc1f7b68c60194f7",
          "evidenceSha256": "7f1eef49684d1cfbe7d4185e47301cd74fd1d168151f33c7c828ca61ffd57c19",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"investment-sculpt\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"investment-sculpt\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Zero-rate debt amortizes 50,000 EUR to zero; with zero debt, DSCR is null rather than a fabricated ratio.",
            "testIds": [
              {
                "node_id": "tests/test_decision_studies.py::test_sculpting_reconciles_amortization_and_handles_zero_rate_and_zero_debt",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "target_dscr below declared minimum 1",
            "target_dscr above declared maximum 5",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "target_dscr=1 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Project NPV",
                  "value": 1857086.59,
                  "unit": "EUR"
                },
                {
                  "label": "Pre-tax nominal project IRR",
                  "value": 16.128,
                  "unit": "%"
                },
                {
                  "label": "Existing level-payment debt capacity",
                  "value": 964265.33,
                  "unit": "EUR"
                },
                {
                  "label": "PV of DSCR-limited service before feasibility screen",
                  "value": 4091352.75,
                  "unit": "EUR"
                },
                {
                  "label": "Sculpted debt capacity",
                  "value": 3200000,
                  "unit": "EUR"
                },
                {
                  "label": "Sized debt",
                  "value": 1760000,
                  "unit": "EUR"
                },
                {
                  "label": "Minimum DSCR",
                  "value": 1,
                  "unit": "ratio"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {
            "requested_debt_feasible": true
          },
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "3b19e8ebf9d845ba04a3cf7407088d5dd449a564668202e2a977fab8ea235edc"
          }
        },
        "limitations": [
          "Uses the existing pre-tax nominal project-finance engine and its rounded annual CFADS. No taxes, DSRA, fees, reserve facility or legal covenant is added; infeasible requests are disclosed."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/decision_studies.py",
          "tests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "investment-sculpt",
            "workspace": "investment",
            "title": "DSCR-constrained sculpted debt",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_decision_studies.py::test_sculpting_reconciles_amortization_and_handles_zero_rate_and_zero_debt",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Zero-rate debt amortizes 50,000 EUR to zero; with zero debt, DSCR is null rather than a fabricated ratio.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Project NPV",
                        "value": 0,
                        "unit": "EUR"
                      },
                      {
                        "label": "Pre-tax nominal project IRR",
                        "value": 0,
                        "unit": "%"
                      },
                      {
                        "label": "Existing level-payment debt capacity",
                        "value": 80000,
                        "unit": "EUR"
                      },
                      {
                        "label": "PV of DSCR-limited service before feasibility screen",
                        "value": 80000,
                        "unit": "EUR"
                      },
                      {
                        "label": "Sculpted debt capacity",
                        "value": 80000,
                        "unit": "EUR"
                      },
                      {
                        "label": "Sized debt",
                        "value": 50000,
                        "unit": "EUR"
                      },
                      {
                        "label": "Minimum DSCR",
                        "value": 1.25,
                        "unit": "ratio"
                      }
                    ],
                    "scalar_details": {
                      "requested_debt_eur": 50000,
                      "capped_requested_debt_eur": 50000,
                      "requested_debt_feasible": true,
                      "project_irr_pct": 0,
                      "project_cashflow_model": "project-finance-v1",
                      "negative_amortization_allowed": false
                    },
                    "table_row_count": 2,
                    "result_sha256": "8b13d572b7019259ad2fd0353edc2ce6ebb03ac70721a46b9903c69978d6ba8d"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Project NPV",
                        "value": 0,
                        "unit": "EUR"
                      },
                      {
                        "label": "Pre-tax nominal project IRR",
                        "value": 0,
                        "unit": "%"
                      },
                      {
                        "label": "Existing level-payment debt capacity",
                        "value": 69421.49,
                        "unit": "EUR"
                      },
                      {
                        "label": "PV of DSCR-limited service before feasibility screen",
                        "value": 69421.49,
                        "unit": "EUR"
                      },
                      {
                        "label": "Sculpted debt capacity",
                        "value": 69421.49,
                        "unit": "EUR"
                      },
                      {
                        "label": "Sized debt",
                        "value": 50000,
                        "unit": "EUR"
                      },
                      {
                        "label": "Minimum DSCR",
                        "value": 1.25,
                        "unit": "ratio"
                      }
                    ],
                    "scalar_details": {
                      "requested_debt_eur": 50000,
                      "capped_requested_debt_eur": 50000,
                      "requested_debt_feasible": true,
                      "project_irr_pct": 0,
                      "project_cashflow_model": "project-finance-v1",
                      "negative_amortization_allowed": false
                    },
                    "table_row_count": 2,
                    "result_sha256": "65cf071450888bb63cf1cce130fb8a1670638379f970b779d527784532fa438b"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Project NPV",
                        "value": 0,
                        "unit": "EUR"
                      },
                      {
                        "label": "Pre-tax nominal project IRR",
                        "value": 0,
                        "unit": "%"
                      },
                      {
                        "label": "Existing level-payment debt capacity",
                        "value": 80000,
                        "unit": "EUR"
                      },
                      {
                        "label": "PV of DSCR-limited service before feasibility screen",
                        "value": 80000,
                        "unit": "EUR"
                      },
                      {
                        "label": "Sculpted debt capacity",
                        "value": 80000,
                        "unit": "EUR"
                      },
                      {
                        "label": "Sized debt",
                        "value": 0,
                        "unit": "EUR"
                      },
                      {
                        "label": "Minimum DSCR",
                        "value": null,
                        "unit": "ratio"
                      }
                    ],
                    "scalar_details": {
                      "requested_debt_eur": 0,
                      "capped_requested_debt_eur": 0,
                      "requested_debt_feasible": true,
                      "project_irr_pct": 0,
                      "project_cashflow_model": "project-finance-v1",
                      "negative_amortization_allowed": false
                    },
                    "table_row_count": 2,
                    "result_sha256": "877ec9f13d1aea5ef4f055bdc341b40d209a588ef92af9006a8197e3ee24faf6"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Project NPV",
                    "value": 1857086.59,
                    "unit": "EUR"
                  },
                  {
                    "label": "Pre-tax nominal project IRR",
                    "value": 16.128,
                    "unit": "%"
                  },
                  {
                    "label": "Existing level-payment debt capacity",
                    "value": 741742.56,
                    "unit": "EUR"
                  },
                  {
                    "label": "PV of DSCR-limited service before feasibility screen",
                    "value": 3147194.42,
                    "unit": "EUR"
                  },
                  {
                    "label": "Sculpted debt capacity",
                    "value": 3147194.42,
                    "unit": "EUR"
                  },
                  {
                    "label": "Sized debt",
                    "value": 1760000,
                    "unit": "EUR"
                  },
                  {
                    "label": "Minimum DSCR",
                    "value": 1.3,
                    "unit": "ratio"
                  }
                ],
                "default_result_sha256": "3b19e8ebf9d845ba04a3cf7407088d5dd449a564668202e2a977fab8ea235edc"
              },
              "units": "EUR / ratio; null at zero debt",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 7
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "target_dscr=1 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Project NPV",
                    "value": 1857086.59,
                    "unit": "EUR"
                  },
                  {
                    "label": "Pre-tax nominal project IRR",
                    "value": 16.128,
                    "unit": "%"
                  },
                  {
                    "label": "Existing level-payment debt capacity",
                    "value": 964265.33,
                    "unit": "EUR"
                  },
                  {
                    "label": "PV of DSCR-limited service before feasibility screen",
                    "value": 4091352.75,
                    "unit": "EUR"
                  },
                  {
                    "label": "Sculpted debt capacity",
                    "value": 3200000,
                    "unit": "EUR"
                  },
                  {
                    "label": "Sized debt",
                    "value": 1760000,
                    "unit": "EUR"
                  },
                  {
                    "label": "Minimum DSCR",
                    "value": 1,
                    "unit": "ratio"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "3b19e8ebf9d845ba04a3cf7407088d5dd449a564668202e2a977fab8ea235edc"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/decision_studies.py",
              "engine_sha256": "7730c9e9bd5fec2eb4249d70e0bacd0a7ba29921635aa88945f22f66df6439d1",
              "test_file": "apps/api/tests/test_decision_studies.py",
              "test_sha256": "857d6c292f08b499b133f2b42de828d700c3e82cf7472d84d7b1ccf5183f1dfd",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Uses the existing pre-tax nominal project-finance engine and its rounded annual CFADS. No taxes, DSRA, fees, reserve facility or legal covenant is added; infeasible requests are disclosed.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-investment-sculpt"
    },
    {
      "id": "investment-tranches",
      "title": "Contracted and merchant tranches",
      "summary": "Size debt separately against PPA-floor CFADS and user-haircut merchant cash flows.",
      "workspace": {
        "id": "investment",
        "name": "Investment desk",
        "appRoute": "#investment"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "tranches",
        "equations": [
          "contracted_CFADS_t = floor_price_t × contracted_MWh_t − OPEX_t (within floor term)",
          "merchant_CFADS_t = haircut × merchant_price_t × merchant_MWh_t − OPEX_t",
          "debt-service ceiling_t = max(0, CFADS_t) / target_DSCR"
        ],
        "limitations": [
          "User-supplied floor is a hypothetical exposure input, not an executed PPA or eligibility claim. No volume shape, basis, counterparty risk, taxes or post-expiry contracted-volume conversion is inferred."
        ],
        "sources": [
          {
            "title": "NREL System Advisor Model help, DSCR debt sizing",
            "url": "https://sam.nrel.gov/images/web_page_files/sam-help-2025-4-16.pdf"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "target_dscr",
            "label": "Target DSCR",
            "default": 1.3,
            "min": 1,
            "max": 5,
            "step": 0.05,
            "unit": "ratio"
          },
          {
            "name": "merchant_haircut_fraction",
            "label": "Merchant factor",
            "default": 0.5,
            "min": 0,
            "max": 1,
            "step": 0.05,
            "unit": "fraction"
          },
          {
            "name": "interest_rate",
            "label": "Debt interest",
            "default": 0.055,
            "min": 0,
            "max": 0.3,
            "step": 0.005,
            "unit": "fraction"
          },
          {
            "name": "tenor_years",
            "label": "Debt tenor",
            "default": 10,
            "min": 1,
            "max": 40,
            "step": 1,
            "unit": "years"
          },
          {
            "name": "contract_years",
            "label": "PPA floor years",
            "default": 10,
            "min": 0,
            "max": 40,
            "step": 1,
            "unit": "years"
          }
        ],
        "jsonDataFields": [
          {
            "name": "annual_rows",
            "label": "Annual floor, merchant and operating assumptions",
            "default": {
              "type": "array",
              "items": 20,
              "itemTypes": [
                "object"
              ],
              "sha256": "abb4a03aea3e75f6ee819eddc9792b822e001fec8289d7478d4b52b287fd3fcf"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "33c2d99f5f21fb454c314174d09b3a41406b840af739e85863308b1c1a4450f4",
        "evidenceSha256": "143ec198d1a264f24eba3e6a4f50dbaec1a85108fbaacd53351ba46f0a8454e0",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"investment-tranches\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"investment-tranches\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "At zero interest and DSCR 1, the independent 40-year floor and merchant capacities are 4e12 and 8e12 EUR.",
          "testIds": [
            {
              "node_id": "tests/test_decision_studies.py::test_tranche_capacity_uses_cash_flow_bound_above_one_trillion_euros",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "target_dscr",
              "unit": "ratio",
              "minimum": 1,
              "maximum": 5,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "merchant_haircut_fraction",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "interest_rate",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 0.3,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "tenor_years",
              "unit": "years",
              "minimum": 1,
              "maximum": 40,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "contract_years",
              "unit": "years",
              "minimum": 0,
              "maximum": 40,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "annual_rows",
              "label": "Annual floor, merchant and operating assumptions",
              "default": {
                "type": "array",
                "items": 20,
                "itemTypes": [
                  "object"
                ],
                "sha256": "abb4a03aea3e75f6ee819eddc9792b822e001fec8289d7478d4b52b287fd3fcf"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/decision_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "33c2d99f5f21fb454c314174d09b3a41406b840af739e85863308b1c1a4450f4",
          "evidenceSha256": "143ec198d1a264f24eba3e6a4f50dbaec1a85108fbaacd53351ba46f0a8454e0",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"investment-tranches\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"investment-tranches\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "At zero interest and DSCR 1, the independent 40-year floor and merchant capacities are 4e12 and 8e12 EUR.",
            "testIds": [
              {
                "node_id": "tests/test_decision_studies.py::test_tranche_capacity_uses_cash_flow_bound_above_one_trillion_euros",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "target_dscr below declared minimum 1",
            "target_dscr above declared maximum 5",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "target_dscr=1 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Contracted-floor debt capacity",
                  "value": 5050209.31,
                  "unit": "EUR"
                },
                {
                  "label": "Haircut merchant debt capacity",
                  "value": 7311497.05,
                  "unit": "EUR"
                },
                {
                  "label": "Full merchant debt capacity",
                  "value": 9572784.8,
                  "unit": "EUR"
                },
                {
                  "label": "Merchant haircut",
                  "value": 0.5,
                  "unit": "fraction"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "2a1298cc0210e89302d0e3c4be1b6f1a39845b79b69153a49667d655f68c30dc"
          }
        },
        "limitations": [
          "User-supplied floor is a hypothetical exposure input, not an executed PPA or eligibility claim. No volume shape, basis, counterparty risk, taxes or post-expiry contracted-volume conversion is inferred."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/decision_studies.py",
          "tests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "investment-tranches",
            "workspace": "investment",
            "title": "Contracted and merchant tranches",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_decision_studies.py::test_tranche_capacity_uses_cash_flow_bound_above_one_trillion_euros",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "At zero interest and DSCR 1, the independent 40-year floor and merchant capacities are 4e12 and 8e12 EUR.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Contracted-floor debt capacity",
                        "value": 4000000000000,
                        "unit": "EUR"
                      },
                      {
                        "label": "Haircut merchant debt capacity",
                        "value": 8000000000000,
                        "unit": "EUR"
                      },
                      {
                        "label": "Full merchant debt capacity",
                        "value": 8000000000000,
                        "unit": "EUR"
                      },
                      {
                        "label": "Merchant haircut",
                        "value": 1,
                        "unit": "fraction"
                      }
                    ],
                    "scalar_details": {
                      "target_dscr": 1,
                      "interest_rate": 0,
                      "tenor_years": 40,
                      "contract_years": 40,
                      "capacity_method": "largest debt amortizing at or below target-DSCR service without negative amortization"
                    },
                    "table_row_count": 40,
                    "result_sha256": "d991831b9ffda855ee2a4b7f578c5ae6ecb9f91f79e7a14cbe6e02f1ef836ca3"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Contracted-floor debt capacity",
                    "value": 3884776.39,
                    "unit": "EUR"
                  },
                  {
                    "label": "Haircut merchant debt capacity",
                    "value": 5624228.5,
                    "unit": "EUR"
                  },
                  {
                    "label": "Full merchant debt capacity",
                    "value": 7363680.62,
                    "unit": "EUR"
                  },
                  {
                    "label": "Merchant haircut",
                    "value": 0.5,
                    "unit": "fraction"
                  }
                ],
                "default_result_sha256": "2a1298cc0210e89302d0e3c4be1b6f1a39845b79b69153a49667d655f68c30dc"
              },
              "units": "EUR",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 4
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "target_dscr=1 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Contracted-floor debt capacity",
                    "value": 5050209.31,
                    "unit": "EUR"
                  },
                  {
                    "label": "Haircut merchant debt capacity",
                    "value": 7311497.05,
                    "unit": "EUR"
                  },
                  {
                    "label": "Full merchant debt capacity",
                    "value": 9572784.8,
                    "unit": "EUR"
                  },
                  {
                    "label": "Merchant haircut",
                    "value": 0.5,
                    "unit": "fraction"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "2a1298cc0210e89302d0e3c4be1b6f1a39845b79b69153a49667d655f68c30dc"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/decision_studies.py",
              "engine_sha256": "7730c9e9bd5fec2eb4249d70e0bacd0a7ba29921635aa88945f22f66df6439d1",
              "test_file": "apps/api/tests/test_decision_studies.py",
              "test_sha256": "857d6c292f08b499b133f2b42de828d700c3e82cf7472d84d7b1ccf5183f1dfd",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "User-supplied floor is a hypothetical exposure input, not an executed PPA or eligibility claim. No volume shape, basis, counterparty risk, taxes or post-expiry contracted-volume conversion is inferred.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-investment-tranches"
    },
    {
      "id": "investment-waterfall",
      "title": "Capital and promote waterfall",
      "summary": "Allocate one exit pool through return of LP capital, accrued preferred return, a 50/50 catch-up and an 80/20 promote.",
      "workspace": {
        "id": "investment",
        "name": "Investment desk",
        "appRoute": "#investment"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "waterfall",
        "equations": [
          "preferred return due = capital × ((1 + preferred_rate)^years − 1)",
          "catch-up total = promote_share × LP_pref_paid / (catch-up_share − promote_share)",
          "remaining pool split LP/sponsor = (1 − promote_share)/promote_share"
        ],
        "limitations": [
          "Explicit single-LP, time-zero contribution and single-exit convention; no interim distributions, taxes, return-of-capital priority to sponsor, clawback, IRR hurdle or legal partnership terms.",
          "First-party algebraic convention; the source reference is not independent validation or a legal partnership agreement."
        ],
        "sources": [
          {
            "title": "First-party HelioForge waterfall implementation and declared equations",
            "url": "https://github.com/mohammadrezwankhan/helioforge-energy-lab/blob/74d6730e38323b69c180d7f4b4945d11010203f3/apps/api/helioforge/engines/decision_studies.py"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "capital_eur",
            "label": "LP contributed capital",
            "default": 5000000,
            "min": 0,
            "max": 1000000000000,
            "step": 10000,
            "unit": "EUR"
          },
          {
            "name": "distribution_pool_eur",
            "label": "Exit distribution pool",
            "default": 12000000,
            "min": 0,
            "max": 1000000000000,
            "step": 10000,
            "unit": "EUR"
          },
          {
            "name": "years",
            "label": "Accrual horizon",
            "default": 10,
            "min": 1,
            "max": 50,
            "step": 1,
            "unit": "years"
          },
          {
            "name": "preferred_return_rate",
            "label": "Preferred return",
            "default": 0.08,
            "min": 0,
            "max": 0.5,
            "step": 0.005,
            "unit": "fraction"
          },
          {
            "name": "catchup_share",
            "label": "Sponsor catch-up share",
            "default": 0.5,
            "min": 0.01,
            "max": 1,
            "step": 0.05,
            "unit": "fraction"
          },
          {
            "name": "sponsor_promote_share",
            "label": "Sponsor promote share",
            "default": 0.2,
            "min": 0,
            "max": 0.99,
            "step": 0.05,
            "unit": "fraction"
          }
        ],
        "jsonDataFields": []
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "80637a51f24f187bb37eb39ad714e957e643c018eed8891e8035977730423bc6",
        "evidenceSha256": "4d69d195973aeeb02ab07f78dca12eb0d5f364e063444558a277ba214ca2f923",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"investment-waterfall\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"investment-waterfall\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "For a 200 EUR distribution pool, tiers allocate the full 200 EUR: capital, pref, catch-up and promote in order.",
          "testIds": [
            {
              "node_id": "tests/test_decision_studies.py::test_waterfall_conserves_distribution_and_applies_catchup_then_promote",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "capital_eur",
              "unit": "EUR",
              "minimum": 0,
              "maximum": 1000000000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "distribution_pool_eur",
              "unit": "EUR",
              "minimum": 0,
              "maximum": 1000000000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "years",
              "unit": "years",
              "minimum": 1,
              "maximum": 50,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "preferred_return_rate",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 0.5,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "catchup_share",
              "unit": "fraction",
              "minimum": 0.01,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "sponsor_promote_share",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 0.99,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [],
          "strictValidationReference": "apps/api/helioforge/engines/decision_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "80637a51f24f187bb37eb39ad714e957e643c018eed8891e8035977730423bc6",
          "evidenceSha256": "4d69d195973aeeb02ab07f78dca12eb0d5f364e063444558a277ba214ca2f923",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"investment-waterfall\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"investment-waterfall\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "For a 200 EUR distribution pool, tiers allocate the full 200 EUR: capital, pref, catch-up and promote in order.",
            "testIds": [
              {
                "node_id": "tests/test_decision_studies.py::test_waterfall_conserves_distribution_and_applies_catchup_then_promote",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "capital_eur below declared minimum 0",
            "capital_eur above declared maximum 1000000000000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "capital_eur=0 (min bound)",
              "metric_snapshot": [
                {
                  "label": "LP distribution",
                  "value": 9600000,
                  "unit": "EUR"
                },
                {
                  "label": "Sponsor distribution",
                  "value": 2400000,
                  "unit": "EUR"
                },
                {
                  "label": "Total allocated",
                  "value": 12000000,
                  "unit": "EUR"
                },
                {
                  "label": "LP preferred return paid",
                  "value": 0,
                  "unit": "EUR"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "476ff900a746edfb40072cb1153a5d9e31de7ca81c0b34975c88592b76ab9856"
          }
        },
        "limitations": [
          "Explicit single-LP, time-zero contribution and single-exit convention; no interim distributions, taxes, return-of-capital priority to sponsor, clawback, IRR hurdle or legal partnership terms.",
          "First-party algebraic convention; the source reference is not independent validation or a legal partnership agreement."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/decision_studies.py",
          "tests": [
            "apps/api/tests/test_decision_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "investment-waterfall",
            "workspace": "investment",
            "title": "Capital and promote waterfall",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_decision_studies.py::test_waterfall_conserves_distribution_and_applies_catchup_then_promote",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "For a 200 EUR distribution pool, tiers allocate the full 200 EUR: capital, pref, catch-up and promote in order.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "LP distribution",
                        "value": 180,
                        "unit": "EUR"
                      },
                      {
                        "label": "Sponsor distribution",
                        "value": 20,
                        "unit": "EUR"
                      },
                      {
                        "label": "Total allocated",
                        "value": 200,
                        "unit": "EUR"
                      },
                      {
                        "label": "LP preferred return paid",
                        "value": 16.64,
                        "unit": "EUR"
                      }
                    ],
                    "scalar_details": {
                      "lp_capital_return_eur": 100,
                      "lp_preferred_return_due_eur": 16.64,
                      "lp_preferred_return_eur": 16.64,
                      "unpaid_preferred_return_eur": 0,
                      "sponsor_catchup_eur": 5.55,
                      "lp_catchup_eur": 5.55,
                      "sponsor_promote_eur": 14.45,
                      "lp_promote_eur": 57.81,
                      "unallocated_distribution_eur": 0,
                      "exact_conservation_residual_eur": 0,
                      "convention": "single LP contributes at time zero; return LP capital, compound 8%/configured pref to one exit, split catch-up at configured sponsor share until promote share is reached, then split remaining pool LP/sponsor"
                    },
                    "table_row_count": 4,
                    "result_sha256": "d8f60c8cce67897b2a5f370e9982892a1c7aebef89fbf9a1864ca430c4ee621a"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "LP distribution",
                    "value": 11397312.49,
                    "unit": "EUR"
                  },
                  {
                    "label": "Sponsor distribution",
                    "value": 602687.51,
                    "unit": "EUR"
                  },
                  {
                    "label": "Total allocated",
                    "value": 12000000,
                    "unit": "EUR"
                  },
                  {
                    "label": "LP preferred return paid",
                    "value": 5794624.99,
                    "unit": "EUR"
                  }
                ],
                "default_result_sha256": "476ff900a746edfb40072cb1153a5d9e31de7ca81c0b34975c88592b76ab9856"
              },
              "units": "EUR",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 4
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "capital_eur=0 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "LP distribution",
                    "value": 9600000,
                    "unit": "EUR"
                  },
                  {
                    "label": "Sponsor distribution",
                    "value": 2400000,
                    "unit": "EUR"
                  },
                  {
                    "label": "Total allocated",
                    "value": 12000000,
                    "unit": "EUR"
                  },
                  {
                    "label": "LP preferred return paid",
                    "value": 0,
                    "unit": "EUR"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "476ff900a746edfb40072cb1153a5d9e31de7ca81c0b34975c88592b76ab9856"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/decision_studies.py",
              "engine_sha256": "7730c9e9bd5fec2eb4249d70e0bacd0a7ba29921635aa88945f22f66df6439d1",
              "test_file": "apps/api/tests/test_decision_studies.py",
              "test_sha256": "857d6c292f08b499b133f2b42de828d700c3e82cf7472d84d7b1ccf5183f1dfd",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Explicit single-LP, time-zero contribution and single-exit convention; no interim distributions, taxes, return-of-capital priority to sponsor, clawback, IRR hurdle or legal partnership terms.",
              "First-party algebraic convention; the source reference is not independent validation or a legal partnership agreement.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-investment-waterfall"
    },
    {
      "id": "lessons-duck",
      "title": "Net load and ramps",
      "summary": "Work through renewable subtraction, hourly ramps, and energy closure.",
      "workspace": {
        "id": "lessons",
        "name": "Learning studio",
        "appRoute": "#lessons"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "learning",
        "equations": [
          "net load[t] = load[t] - solar[t] - wind[t]",
          "ramp[t] = (net load[t] - net load[t-1]) / interval hours",
          "net-load energy = sum(net load[t] * interval hours)"
        ],
        "limitations": [
          "Synthetic equal-duration series; no dispatch, forecast, or grid-constraint model."
        ],
        "sources": [
          {
            "title": "Renewable integration study of net load and ramping",
            "url": "https://docs.nrel.gov/docs/fy18osti/71151.pdf"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "interval_hours",
            "label": "Interval duration",
            "default": 1,
            "min": 0.25,
            "max": 4,
            "step": 0.25,
            "unit": "h"
          },
          {
            "name": "ramp_limit_kw_per_hour",
            "label": "Illustrative ramp marker",
            "default": 45,
            "min": 0,
            "max": 10000,
            "step": 5,
            "unit": "kW/h"
          },
          {
            "name": "ramp_percentile",
            "label": "Ramp percentile",
            "default": 95,
            "min": 50,
            "max": 99.9,
            "step": 1,
            "unit": "%"
          }
        ],
        "jsonDataFields": [
          {
            "name": "load_kw",
            "label": "Demand",
            "default": {
              "type": "array",
              "items": 24,
              "itemTypes": [
                "number"
              ],
              "sha256": "0c82ad30214dafaeec9938c4bd816d27356e7f9c63d6c18e9bf6f53d559456ec"
            }
          },
          {
            "name": "solar_kw",
            "label": "Solar output",
            "default": {
              "type": "array",
              "items": 24,
              "itemTypes": [
                "number"
              ],
              "sha256": "10a94fc5f77b4920a95711906bdb20dd46c87663f47d2f68490a22aac7d999f3"
            }
          },
          {
            "name": "wind_kw",
            "label": "Wind output",
            "default": {
              "type": "array",
              "items": 24,
              "itemTypes": [
                "number"
              ],
              "sha256": "27c38d492507eb28a7458427129f656f36b54db5ede86005781e26c5608fc171"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "c40a02b1ab8a04cb0bd94ee0e23763b034fb77086c82be31fb9ec1591b6f844c",
        "evidenceSha256": "859e80d471bf574c94d3edef215551d775d197ad16654ba70a05ce290ddc4ccb",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"lessons-duck\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"lessons-duck\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Two half-hour rows close 11 kWh demand = 5 kWh renewable + 6 kWh net load, with 4 kW/h maximum upward ramp.",
          "testIds": [
            {
              "node_id": "tests/test_workflow_studies.py::test_duck_lesson_hand_calculates_net_load_ramp_and_energy_closure",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "interval_hours",
              "unit": "h",
              "minimum": 0.25,
              "maximum": 4,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "ramp_limit_kw_per_hour",
              "unit": "kW/h",
              "minimum": 0,
              "maximum": 10000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "ramp_percentile",
              "unit": "%",
              "minimum": 50,
              "maximum": 99.9,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "load_kw",
              "label": "Demand",
              "default": {
                "type": "array",
                "items": 24,
                "itemTypes": [
                  "number"
                ],
                "sha256": "0c82ad30214dafaeec9938c4bd816d27356e7f9c63d6c18e9bf6f53d559456ec"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "solar_kw",
              "label": "Solar output",
              "default": {
                "type": "array",
                "items": 24,
                "itemTypes": [
                  "number"
                ],
                "sha256": "10a94fc5f77b4920a95711906bdb20dd46c87663f47d2f68490a22aac7d999f3"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "wind_kw",
              "label": "Wind output",
              "default": {
                "type": "array",
                "items": 24,
                "itemTypes": [
                  "number"
                ],
                "sha256": "27c38d492507eb28a7458427129f656f36b54db5ede86005781e26c5608fc171"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/workflow_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "c40a02b1ab8a04cb0bd94ee0e23763b034fb77086c82be31fb9ec1591b6f844c",
          "evidenceSha256": "859e80d471bf574c94d3edef215551d775d197ad16654ba70a05ce290ddc4ccb",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"lessons-duck\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"lessons-duck\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Two half-hour rows close 11 kWh demand = 5 kWh renewable + 6 kWh net load, with 4 kW/h maximum upward ramp.",
            "testIds": [
              {
                "node_id": "tests/test_workflow_studies.py::test_duck_lesson_hand_calculates_net_load_ramp_and_energy_closure",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "interval_hours below declared minimum 0.25",
            "interval_hours above declared maximum 4",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "interval_hours=0.25 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Demand energy",
                  "value": 552,
                  "unit": "kWh"
                },
                {
                  "label": "Renewable energy",
                  "value": 285.23725,
                  "unit": "kWh"
                },
                {
                  "label": "Net-load energy",
                  "value": 266.76275,
                  "unit": "kWh"
                },
                {
                  "label": "Energy closure residual",
                  "value": 0,
                  "unit": "kWh"
                },
                {
                  "label": "Peak net load",
                  "value": 88.328,
                  "unit": "kW"
                },
                {
                  "label": "Maximum upward ramp",
                  "value": 90.74799999999999,
                  "unit": "kW/h"
                },
                {
                  "label": "P95 upward ramp",
                  "value": 82.61279999999998,
                  "unit": "kW/h"
                },
                {
                  "label": "Upward ramps above marker",
                  "value": 4,
                  "unit": "intervals"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "6841bc4c7c96bed10371a7f46264c5882556c6c124efea1d48afab69deee6b5c"
          }
        },
        "limitations": [
          "Synthetic equal-duration series; no dispatch, forecast, or grid-constraint model."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/workflow_studies.py",
          "tests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "lessons-duck",
            "workspace": "lessons",
            "title": "Net load and ramps",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_workflow_studies.py::test_duck_lesson_hand_calculates_net_load_ramp_and_energy_closure",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Two half-hour rows close 11 kWh demand = 5 kWh renewable + 6 kWh net load, with 4 kW/h maximum upward ramp.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Demand energy",
                        "value": 11,
                        "unit": "kWh"
                      },
                      {
                        "label": "Renewable energy",
                        "value": 5,
                        "unit": "kWh"
                      },
                      {
                        "label": "Net-load energy",
                        "value": 6,
                        "unit": "kWh"
                      },
                      {
                        "label": "Energy closure residual",
                        "value": 0,
                        "unit": "kWh"
                      },
                      {
                        "label": "Peak net load",
                        "value": 7,
                        "unit": "kW"
                      },
                      {
                        "label": "Maximum upward ramp",
                        "value": 4,
                        "unit": "kW/h"
                      },
                      {
                        "label": "P95 upward ramp",
                        "value": 4,
                        "unit": "kW/h"
                      },
                      {
                        "label": "Upward ramps above marker",
                        "value": 1,
                        "unit": "intervals"
                      }
                    ],
                    "scalar_details": {
                      "interval_hours": 0.5,
                      "interval_count": 2
                    },
                    "table_row_count": 2,
                    "result_sha256": "b7cd6c8da92dbf903f0f298f289de867e9cd1f5b7ddb3ea09c32b274ba44bd42"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Demand energy",
                    "value": 2208,
                    "unit": "kWh"
                  },
                  {
                    "label": "Renewable energy",
                    "value": 1140.949,
                    "unit": "kWh"
                  },
                  {
                    "label": "Net-load energy",
                    "value": 1067.051,
                    "unit": "kWh"
                  },
                  {
                    "label": "Energy closure residual",
                    "value": 0,
                    "unit": "kWh"
                  },
                  {
                    "label": "Peak net load",
                    "value": 88.328,
                    "unit": "kW"
                  },
                  {
                    "label": "Maximum upward ramp",
                    "value": 22.686999999999998,
                    "unit": "kW/h"
                  },
                  {
                    "label": "P95 upward ramp",
                    "value": 20.653199999999995,
                    "unit": "kW/h"
                  },
                  {
                    "label": "Upward ramps above marker",
                    "value": 0,
                    "unit": "intervals"
                  }
                ],
                "default_result_sha256": "6841bc4c7c96bed10371a7f46264c5882556c6c124efea1d48afab69deee6b5c"
              },
              "units": "kWh / kW/h",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 8
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "interval_hours=0.25 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Demand energy",
                    "value": 552,
                    "unit": "kWh"
                  },
                  {
                    "label": "Renewable energy",
                    "value": 285.23725,
                    "unit": "kWh"
                  },
                  {
                    "label": "Net-load energy",
                    "value": 266.76275,
                    "unit": "kWh"
                  },
                  {
                    "label": "Energy closure residual",
                    "value": 0,
                    "unit": "kWh"
                  },
                  {
                    "label": "Peak net load",
                    "value": 88.328,
                    "unit": "kW"
                  },
                  {
                    "label": "Maximum upward ramp",
                    "value": 90.74799999999999,
                    "unit": "kW/h"
                  },
                  {
                    "label": "P95 upward ramp",
                    "value": 82.61279999999998,
                    "unit": "kW/h"
                  },
                  {
                    "label": "Upward ramps above marker",
                    "value": 4,
                    "unit": "intervals"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "6841bc4c7c96bed10371a7f46264c5882556c6c124efea1d48afab69deee6b5c"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/workflow_studies.py",
              "engine_sha256": "021597dd1d8b016f15bffa8494fb23a553fb93b26f9ae48c7a17061ec19ca585",
              "test_file": "apps/api/tests/test_workflow_studies.py",
              "test_sha256": "5649c323bcfa2ade89e03a7c12417835938366fbc0444f5b4b2954d65eee0221",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Synthetic equal-duration series; no dispatch, forecast, or grid-constraint model.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-lessons-duck"
    },
    {
      "id": "lessons-blackstart",
      "title": "Black-start sequence exercise",
      "summary": "Check a finite-state energization sequence against active-power and inrush budgets.",
      "workspace": {
        "id": "lessons",
        "name": "Learning studio",
        "appRoute": "#lessons"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "learning",
        "equations": [
          "available budget = rated power * available fraction",
          "step passes if active power + inrush <= rated power * (available fraction - reserve fraction)",
          "states: dark -> grid-forming-ready -> bus-energized -> load-restored"
        ],
        "limitations": [
          "Procedural finite-state exercise only; no transient, protection, motor-start, or energization validation.",
          "Budgets and sequence require system-specific operator review."
        ],
        "sources": [
          {
            "title": "NERC documented black-start procedure practice guide",
            "url": "https://www.nerc.com/pa/comp/guidance/CMEPPracticeGuidesDL/CMEP%20Practice%20Guide%20-%20Evaluating%20Blackstart%20Documented%20Procedures.pdf"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "gfm_rated_kw",
            "label": "Grid-forming rated power",
            "default": 500,
            "min": 1,
            "max": 100000,
            "step": 10,
            "unit": "kW"
          },
          {
            "name": "available_power_fraction",
            "label": "Available fraction",
            "default": 0.8,
            "min": 0.01,
            "max": 1,
            "step": 0.01,
            "unit": "fraction"
          },
          {
            "name": "inrush_budget_kw",
            "label": "Allowed inrush proxy",
            "default": 250,
            "min": 0,
            "max": 100000,
            "step": 10,
            "unit": "kW"
          },
          {
            "name": "reserve_fraction",
            "label": "Held reserve fraction",
            "default": 0.15,
            "min": 0,
            "max": 0.99,
            "step": 0.01,
            "unit": "fraction"
          }
        ],
        "jsonDataFields": [
          {
            "name": "steps",
            "label": "Ordered exercise steps",
            "default": {
              "type": "array",
              "items": 3,
              "itemTypes": [
                "object"
              ],
              "sha256": "cab7cc7fb867753a7600370a8af4f3a1b2f5c8a6bf4d4a115d83918d40f39055"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "9584284a0d8d5f8c4a17e1b03cbde839d3d5a46d385a105ce252bc771dffb4fa",
        "evidenceSha256": "a94bfaff59524438a7f2c816d99703b6c123d0e27160fd52d3cf0593edcf818f",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"lessons-blackstart\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"lessons-blackstart\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Out-of-sequence energization and a 70+30 kW step against a 90 kW post-reserve budget are rejected.",
          "testIds": [
            {
              "node_id": "tests/test_workflow_studies.py::test_blackstart_procedure_requires_sequence_and_respects_active_plus_inrush_budgets",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "gfm_rated_kw",
              "unit": "kW",
              "minimum": 1,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "available_power_fraction",
              "unit": "fraction",
              "minimum": 0.01,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "inrush_budget_kw",
              "unit": "kW",
              "minimum": 0,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "reserve_fraction",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 0.99,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "steps",
              "label": "Ordered exercise steps",
              "default": {
                "type": "array",
                "items": 3,
                "itemTypes": [
                  "object"
                ],
                "sha256": "cab7cc7fb867753a7600370a8af4f3a1b2f5c8a6bf4d4a115d83918d40f39055"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/workflow_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "9584284a0d8d5f8c4a17e1b03cbde839d3d5a46d385a105ce252bc771dffb4fa",
          "evidenceSha256": "a94bfaff59524438a7f2c816d99703b6c123d0e27160fd52d3cf0593edcf818f",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"lessons-blackstart\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"lessons-blackstart\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Out-of-sequence energization and a 70+30 kW step against a 90 kW post-reserve budget are rejected.",
            "testIds": [
              {
                "node_id": "tests/test_workflow_studies.py::test_blackstart_procedure_requires_sequence_and_respects_active_plus_inrush_budgets",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "gfm_rated_kw below declared minimum 1",
            "gfm_rated_kw above declared maximum 100000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "gfm_rated_kw=1 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Final exercise state",
                  "value": "dark",
                  "unit": "state"
                },
                {
                  "label": "Accepted steps",
                  "value": 0,
                  "unit": "steps"
                },
                {
                  "label": "Rejected steps",
                  "value": 3,
                  "unit": "steps"
                },
                {
                  "label": "Available active-power budget",
                  "value": 0.8,
                  "unit": "kW"
                },
                {
                  "label": "Post-reserve step budget",
                  "value": 0.65,
                  "unit": "kW"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "aa5f481154dadfffd9463ccc15269a048379263c3b519dc05edb5f7bfd530d1a"
          }
        },
        "limitations": [
          "Procedural finite-state exercise only; no transient, protection, motor-start, or energization validation.",
          "Budgets and sequence require system-specific operator review."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/workflow_studies.py",
          "tests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "lessons-blackstart",
            "workspace": "lessons",
            "title": "Black-start sequence exercise",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_workflow_studies.py::test_blackstart_procedure_requires_sequence_and_respects_active_plus_inrush_budgets",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Out-of-sequence energization and a 70+30 kW step against a 90 kW post-reserve budget are rejected.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Final exercise state",
                        "value": "dark",
                        "unit": "state"
                      },
                      {
                        "label": "Accepted steps",
                        "value": 0,
                        "unit": "steps"
                      },
                      {
                        "label": "Rejected steps",
                        "value": 1,
                        "unit": "steps"
                      },
                      {
                        "label": "Available active-power budget",
                        "value": 400,
                        "unit": "kW"
                      },
                      {
                        "label": "Post-reserve step budget",
                        "value": 325,
                        "unit": "kW"
                      }
                    ],
                    "scalar_details": {
                      "available_kw": 400,
                      "post_reserve_budget_kw": 325,
                      "complete": false
                    },
                    "table_row_count": 1,
                    "result_sha256": "d2584868bc922880dc562de1b2c9cf8f4d56614849398c17c5516298acdd478a"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Final exercise state",
                        "value": "grid_forming_ready",
                        "unit": "state"
                      },
                      {
                        "label": "Accepted steps",
                        "value": 1,
                        "unit": "steps"
                      },
                      {
                        "label": "Rejected steps",
                        "value": 1,
                        "unit": "steps"
                      },
                      {
                        "label": "Available active-power budget",
                        "value": 100,
                        "unit": "kW"
                      },
                      {
                        "label": "Post-reserve step budget",
                        "value": 90,
                        "unit": "kW"
                      }
                    ],
                    "scalar_details": {
                      "available_kw": 100,
                      "post_reserve_budget_kw": 90,
                      "complete": false
                    },
                    "table_row_count": 2,
                    "result_sha256": "4c708ce506a43bb272cf496ca5523f3c9ee43613628f244eb9945059bd93be3a"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Final exercise state",
                    "value": "load_restored",
                    "unit": "state"
                  },
                  {
                    "label": "Accepted steps",
                    "value": 3,
                    "unit": "steps"
                  },
                  {
                    "label": "Rejected steps",
                    "value": 0,
                    "unit": "steps"
                  },
                  {
                    "label": "Available active-power budget",
                    "value": 400,
                    "unit": "kW"
                  },
                  {
                    "label": "Post-reserve step budget",
                    "value": 325,
                    "unit": "kW"
                  }
                ],
                "default_result_sha256": "aa5f481154dadfffd9463ccc15269a048379263c3b519dc05edb5f7bfd530d1a"
              },
              "units": "kW / state",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 5
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "gfm_rated_kw=1 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Final exercise state",
                    "value": "dark",
                    "unit": "state"
                  },
                  {
                    "label": "Accepted steps",
                    "value": 0,
                    "unit": "steps"
                  },
                  {
                    "label": "Rejected steps",
                    "value": 3,
                    "unit": "steps"
                  },
                  {
                    "label": "Available active-power budget",
                    "value": 0.8,
                    "unit": "kW"
                  },
                  {
                    "label": "Post-reserve step budget",
                    "value": 0.65,
                    "unit": "kW"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "aa5f481154dadfffd9463ccc15269a048379263c3b519dc05edb5f7bfd530d1a"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/workflow_studies.py",
              "engine_sha256": "021597dd1d8b016f15bffa8494fb23a553fb93b26f9ae48c7a17061ec19ca585",
              "test_file": "apps/api/tests/test_workflow_studies.py",
              "test_sha256": "5649c323bcfa2ade89e03a7c12417835938366fbc0444f5b4b2954d65eee0221",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Procedural finite-state exercise only; no transient, protection, motor-start, or energization validation.",
              "Budgets and sequence require system-specific operator review.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-lessons-blackstart"
    },
    {
      "id": "lessons-hydrogen",
      "title": "Hydrogen energy pathway",
      "summary": "Close the electrolyzer, compression, LHV storage, and fuel-cell energy ledger.",
      "workspace": {
        "id": "lessons",
        "name": "Learning studio",
        "appRoute": "#lessons"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "learning",
        "equations": [
          "chemical energy = hydrogen mass * 33.3 kWh/kg (LHV basis)",
          "fuel-cell electricity = chemical energy * electric efficiency",
          "electricity input = useful output + electrolysis + compression + fuel-cell losses"
        ],
        "limitations": [
          "Stage parameters are illustrative, not measured plant performance.",
          "Compression boundary, delivery pressure, leakage, and plant auxiliaries are not modeled."
        ],
        "sources": [
          {
            "title": "DOE hydrogen technical targets and LHV basis",
            "url": "https://www.energy.gov/cmei/fuels/doe-technical-targets-onboard-hydrogen-storage-light-duty-vehicles"
          },
          {
            "title": "DOE hydrogen compression energy requirements",
            "url": "https://www.hydrogen.energy.gov/docs/hydrogenprogramlibraries/pdfs/9013_energy_requirements_for_hydrogen_gas_compression.pdf"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "electrolyzer_kwh_per_kg",
            "label": "Electrolyzer electricity",
            "default": 52,
            "min": 33.3,
            "max": 200,
            "step": 0.5,
            "unit": "kWh/kg H2"
          },
          {
            "name": "compression_kwh_per_kg",
            "label": "Compression electricity",
            "default": 2,
            "min": 0,
            "max": 100,
            "step": 0.5,
            "unit": "kWh/kg H2"
          },
          {
            "name": "fuel_cell_electric_efficiency",
            "label": "Fuel-cell efficiency",
            "default": 0.5,
            "min": 0,
            "max": 1,
            "step": 0.01,
            "unit": "fraction"
          },
          {
            "name": "hydrogen_lhv_kwh_per_kg",
            "label": "Hydrogen LHV",
            "default": 33.3,
            "min": 33.3,
            "max": 33.3,
            "step": 0.1,
            "unit": "kWh/kg H2"
          }
        ],
        "jsonDataFields": [
          {
            "name": "batch",
            "label": "Hydrogen batch",
            "default": {
              "type": "object",
              "keys": [
                "batch_id",
                "hydrogen_kg"
              ],
              "sha256": "576500d0ef9c90dbfa6f097862e0186506043c8ab5fda9ccb5e460c69202e256"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "5c32abfe7e669a83892218dd80d1b40470274d16eb58c9d953df8ea4fe25145b",
        "evidenceSha256": "a8e3dd7e6715de6cc54143790fdf170cdb52594814ef8eca030ec596e5c8ca69",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"lessons-hydrogen\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"lessons-hydrogen\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "A 1 kg batch stores 33.3 kWh LHV and at 50% fuel-cell efficiency returns 16.65 kWh; zero hydrogen has null efficiency.",
          "testIds": [
            {
              "node_id": "tests/test_workflow_studies.py::test_hydrogen_stages_close_electric_and_lhv_energy_ledger",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "electrolyzer_kwh_per_kg",
              "unit": "kWh/kg H2",
              "minimum": 33.3,
              "maximum": 200,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "compression_kwh_per_kg",
              "unit": "kWh/kg H2",
              "minimum": 0,
              "maximum": 100,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "fuel_cell_electric_efficiency",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "hydrogen_lhv_kwh_per_kg",
              "unit": "kWh/kg H2",
              "minimum": 33.3,
              "maximum": 33.3,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "batch",
              "label": "Hydrogen batch",
              "default": {
                "type": "object",
                "keys": [
                  "batch_id",
                  "hydrogen_kg"
                ],
                "sha256": "576500d0ef9c90dbfa6f097862e0186506043c8ab5fda9ccb5e460c69202e256"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/workflow_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "5c32abfe7e669a83892218dd80d1b40470274d16eb58c9d953df8ea4fe25145b",
          "evidenceSha256": "a8e3dd7e6715de6cc54143790fdf170cdb52594814ef8eca030ec596e5c8ca69",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"lessons-hydrogen\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"lessons-hydrogen\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "A 1 kg batch stores 33.3 kWh LHV and at 50% fuel-cell efficiency returns 16.65 kWh; zero hydrogen has null efficiency.",
            "testIds": [
              {
                "node_id": "tests/test_workflow_studies.py::test_hydrogen_stages_close_electric_and_lhv_energy_ledger",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "electrolyzer_kwh_per_kg below declared minimum 33.3",
            "electrolyzer_kwh_per_kg above declared maximum 200",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "electrolyzer_kwh_per_kg=33.3 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Electrolyzer electricity",
                  "value": 3329.9999999999995,
                  "unit": "kWh"
                },
                {
                  "label": "Compression electricity",
                  "value": 200,
                  "unit": "kWh"
                },
                {
                  "label": "Stored hydrogen chemical energy",
                  "value": 3329.9999999999995,
                  "unit": "kWh LHV"
                },
                {
                  "label": "Fuel-cell electricity",
                  "value": 1664.9999999999998,
                  "unit": "kWh"
                },
                {
                  "label": "Electric round-trip efficiency",
                  "value": 0.471671388101983,
                  "unit": "fraction"
                },
                {
                  "label": "Energy closure residual",
                  "value": 0,
                  "unit": "kWh"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "ac3b1b23e378d129f385700538327c9f0083d096b429c2f7ffea4dae502f65b1"
          }
        },
        "limitations": [
          "Stage parameters are illustrative, not measured plant performance.",
          "Compression boundary, delivery pressure, leakage, and plant auxiliaries are not modeled."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/workflow_studies.py",
          "tests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "lessons-hydrogen",
            "workspace": "lessons",
            "title": "Hydrogen energy pathway",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_workflow_studies.py::test_hydrogen_stages_close_electric_and_lhv_energy_ledger",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "A 1 kg batch stores 33.3 kWh LHV and at 50% fuel-cell efficiency returns 16.65 kWh; zero hydrogen has null efficiency.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Electrolyzer electricity",
                        "value": 52,
                        "unit": "kWh"
                      },
                      {
                        "label": "Compression electricity",
                        "value": 2,
                        "unit": "kWh"
                      },
                      {
                        "label": "Stored hydrogen chemical energy",
                        "value": 33.3,
                        "unit": "kWh LHV"
                      },
                      {
                        "label": "Fuel-cell electricity",
                        "value": 16.65,
                        "unit": "kWh"
                      },
                      {
                        "label": "Electric round-trip efficiency",
                        "value": 0.3083333333333333,
                        "unit": "fraction"
                      },
                      {
                        "label": "Energy closure residual",
                        "value": 0,
                        "unit": "kWh"
                      }
                    ],
                    "scalar_details": {
                      "batch_id": "hand-case",
                      "hydrogen_kg": 1,
                      "lhv_basis_kwh_per_kg": 33.3,
                      "total_electricity_input_kwh": 54,
                      "useful_electricity_output_kwh": 16.65,
                      "accounted_losses_kwh": 37.35,
                      "energy_closure_residual_kwh": 0
                    },
                    "table_row_count": 3,
                    "result_sha256": "e12b02f38edd93071e968e8a3025b757669c92b11c86398a4c82379bf6fe04e0"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Electrolyzer electricity",
                        "value": 0,
                        "unit": "kWh"
                      },
                      {
                        "label": "Compression electricity",
                        "value": 0,
                        "unit": "kWh"
                      },
                      {
                        "label": "Stored hydrogen chemical energy",
                        "value": 0,
                        "unit": "kWh LHV"
                      },
                      {
                        "label": "Fuel-cell electricity",
                        "value": 0,
                        "unit": "kWh"
                      },
                      {
                        "label": "Electric round-trip efficiency",
                        "value": null,
                        "unit": "fraction"
                      },
                      {
                        "label": "Energy closure residual",
                        "value": 0,
                        "unit": "kWh"
                      }
                    ],
                    "scalar_details": {
                      "batch_id": "synthetic-batch-01",
                      "hydrogen_kg": 0,
                      "lhv_basis_kwh_per_kg": 33.3,
                      "total_electricity_input_kwh": 0,
                      "useful_electricity_output_kwh": 0,
                      "accounted_losses_kwh": 0,
                      "energy_closure_residual_kwh": 0
                    },
                    "table_row_count": 3,
                    "result_sha256": "54fce6191df318de3b33b2e7b92797e02b1f550045039344a8fd735c2f7c861e"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Electrolyzer electricity",
                    "value": 5200,
                    "unit": "kWh"
                  },
                  {
                    "label": "Compression electricity",
                    "value": 200,
                    "unit": "kWh"
                  },
                  {
                    "label": "Stored hydrogen chemical energy",
                    "value": 3329.9999999999995,
                    "unit": "kWh LHV"
                  },
                  {
                    "label": "Fuel-cell electricity",
                    "value": 1664.9999999999998,
                    "unit": "kWh"
                  },
                  {
                    "label": "Electric round-trip efficiency",
                    "value": 0.3083333333333333,
                    "unit": "fraction"
                  },
                  {
                    "label": "Energy closure residual",
                    "value": 0,
                    "unit": "kWh"
                  }
                ],
                "default_result_sha256": "ac3b1b23e378d129f385700538327c9f0083d096b429c2f7ffea4dae502f65b1"
              },
              "units": "kWh / kWh LHV / fraction",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 6
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "electrolyzer_kwh_per_kg=33.3 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Electrolyzer electricity",
                    "value": 3329.9999999999995,
                    "unit": "kWh"
                  },
                  {
                    "label": "Compression electricity",
                    "value": 200,
                    "unit": "kWh"
                  },
                  {
                    "label": "Stored hydrogen chemical energy",
                    "value": 3329.9999999999995,
                    "unit": "kWh LHV"
                  },
                  {
                    "label": "Fuel-cell electricity",
                    "value": 1664.9999999999998,
                    "unit": "kWh"
                  },
                  {
                    "label": "Electric round-trip efficiency",
                    "value": 0.471671388101983,
                    "unit": "fraction"
                  },
                  {
                    "label": "Energy closure residual",
                    "value": 0,
                    "unit": "kWh"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "ac3b1b23e378d129f385700538327c9f0083d096b429c2f7ffea4dae502f65b1"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/workflow_studies.py",
              "engine_sha256": "021597dd1d8b016f15bffa8494fb23a553fb93b26f9ae48c7a17061ec19ca585",
              "test_file": "apps/api/tests/test_workflow_studies.py",
              "test_sha256": "5649c323bcfa2ade89e03a7c12417835938366fbc0444f5b4b2954d65eee0221",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Stage parameters are illustrative, not measured plant performance.",
              "Compression boundary, delivery pressure, leakage, and plant auxiliaries are not modeled.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-lessons-hydrogen"
    },
    {
      "id": "compare-pareto",
      "title": "Comparable-run Pareto screen",
      "summary": "Show cost, service, SOC endpoints, sampled expected loss-of-load hours, dominance, and a disclosed weighted preference.",
      "workspace": {
        "id": "compare",
        "name": "Scenario compare",
        "appRoute": "#compare"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "comparison",
        "equations": [
          "a dominates b when it is no worse on all objectives and strictly better on one",
          "score = weighted sum of within-group min-max normalized objectives"
        ],
        "limitations": [
          "Comparison requires matching scenario, sample-set key, horizon, service basis, and sample count.",
          "Expected loss-of-load hours are supplied estimates; recommendation depends on user weights."
        ],
        "sources": [
          {
            "title": "A fast and elitist multiobjective genetic algorithm (Pareto dominance)",
            "url": "https://doi.org/10.1109/4235.996017"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "cost_weight",
            "label": "Cost weight",
            "default": 0.4,
            "min": 0,
            "max": 1,
            "step": 0.05,
            "unit": "fraction"
          },
          {
            "name": "service_weight",
            "label": "Service weight",
            "default": 0.3,
            "min": 0,
            "max": 1,
            "step": 0.05,
            "unit": "fraction"
          },
          {
            "name": "loss_of_load_weight",
            "label": "Expected loss-of-load weight",
            "default": 0.3,
            "min": 0,
            "max": 1,
            "step": 0.05,
            "unit": "fraction"
          }
        ],
        "jsonDataFields": [
          {
            "name": "candidates",
            "label": "Scenario-tagged run results",
            "default": {
              "type": "array",
              "items": 3,
              "itemTypes": [
                "object"
              ],
              "sha256": "2a66d8ca20fa1409aed033e87b239e12c3cf564a838726febcc89ba3128683c0"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "4f296a339cebe21478c3d273ea0ae99ce1fe429331b902b55bdc7bdec85afcba",
        "evidenceSha256": "22f332e760056403db191bc62dd3d0e3adce59679220837f6574b1ca6d374f64",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"compare-pareto\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"compare-pareto\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Dominated run c is dominated by a and b; different scenario/sample sets are not compared or recommended.",
          "testIds": [
            {
              "node_id": "tests/test_workflow_studies.py::test_pareto_dominance_and_recommendation_are_limited_to_comparable_runs",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "cost_weight",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "service_weight",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "loss_of_load_weight",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "candidates",
              "label": "Scenario-tagged run results",
              "default": {
                "type": "array",
                "items": 3,
                "itemTypes": [
                  "object"
                ],
                "sha256": "2a66d8ca20fa1409aed033e87b239e12c3cf564a838726febcc89ba3128683c0"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/workflow_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "4f296a339cebe21478c3d273ea0ae99ce1fe429331b902b55bdc7bdec85afcba",
          "evidenceSha256": "22f332e760056403db191bc62dd3d0e3adce59679220837f6574b1ca6d374f64",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"compare-pareto\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"compare-pareto\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Dominated run c is dominated by a and b; different scenario/sample sets are not compared or recommended.",
            "testIds": [
              {
                "node_id": "tests/test_workflow_studies.py::test_pareto_dominance_and_recommendation_are_limited_to_comparable_runs",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "cost_weight below declared minimum 0",
            "cost_weight above declared maximum 1",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "candidates=valid prefix length 2",
              "metric_snapshot": [
                {
                  "label": "Comparable groups",
                  "value": 1,
                  "unit": "groups"
                },
                {
                  "label": "Pareto-nondominated runs",
                  "value": 2,
                  "unit": "runs"
                },
                {
                  "label": "Weighted recommendations",
                  "value": 1,
                  "unit": "groups"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "0687aa70731f6c37b6569cfdc72b0b7b5d34ab3996d93cebf55c486d0db52386"
          }
        },
        "limitations": [
          "Comparison requires matching scenario, sample-set key, horizon, service basis, and sample count.",
          "Expected loss-of-load hours are supplied estimates; recommendation depends on user weights."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/workflow_studies.py",
          "tests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "compare-pareto",
            "workspace": "compare",
            "title": "Comparable-run Pareto screen",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_workflow_studies.py::test_pareto_dominance_and_recommendation_are_limited_to_comparable_runs",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Dominated run c is dominated by a and b; different scenario/sample sets are not compared or recommended.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Comparable groups",
                        "value": 1,
                        "unit": "groups"
                      },
                      {
                        "label": "Pareto-nondominated runs",
                        "value": 2,
                        "unit": "runs"
                      },
                      {
                        "label": "Weighted recommendations",
                        "value": 1,
                        "unit": "groups"
                      }
                    ],
                    "scalar_details": {},
                    "table_row_count": 4,
                    "result_sha256": "c4a033c6ac618c6f8ba8509f93807ea7eef3e99ecd885684b27c94561cc7d50c"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Comparable groups",
                    "value": 1,
                    "unit": "groups"
                  },
                  {
                    "label": "Pareto-nondominated runs",
                    "value": 3,
                    "unit": "runs"
                  },
                  {
                    "label": "Weighted recommendations",
                    "value": 1,
                    "unit": "groups"
                  }
                ],
                "default_result_sha256": "0687aa70731f6c37b6569cfdc72b0b7b5d34ab3996d93cebf55c486d0db52386"
              },
              "units": "runs / groups",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 3
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "candidates=valid prefix length 2",
                "metric_snapshot": [
                  {
                    "label": "Comparable groups",
                    "value": 1,
                    "unit": "groups"
                  },
                  {
                    "label": "Pareto-nondominated runs",
                    "value": 2,
                    "unit": "runs"
                  },
                  {
                    "label": "Weighted recommendations",
                    "value": 1,
                    "unit": "groups"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "0687aa70731f6c37b6569cfdc72b0b7b5d34ab3996d93cebf55c486d0db52386"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/workflow_studies.py",
              "engine_sha256": "021597dd1d8b016f15bffa8494fb23a553fb93b26f9ae48c7a17061ec19ca585",
              "test_file": "apps/api/tests/test_workflow_studies.py",
              "test_sha256": "5649c323bcfa2ade89e03a7c12417835938366fbc0444f5b4b2954d65eee0221",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Comparison requires matching scenario, sample-set key, horizon, service basis, and sample count.",
              "Expected loss-of-load hours are supplied estimates; recommendation depends on user weights.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-compare-pareto"
    },
    {
      "id": "compare-dunkelflaute",
      "title": "Fourteen-day storage dispatch",
      "summary": "Dispatch storage over a non-repeating synthetic 336-hour path with fixed initial and terminal SOC.",
      "workspace": {
        "id": "compare",
        "name": "Scenario compare",
        "appRoute": "#compare"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "comparison",
        "equations": [
          "renewable + discharge + shortfall = load + charge + curtailment",
          "SOC[t+1] = SOC[t] + charge efficiency*charge - discharge/discharge efficiency",
          "charge <= power limit*mode; discharge <= power limit*(1-mode); mode is binary",
          "minimize shortfall subject to SOC[0] = SOC[336] = initial SOC"
        ],
        "limitations": [
          "One synthetic 336-hour weather path, not a repeated demo day or statistical LOLE estimate.",
          "Reduced mixed-integer dispatch enforces battery direction but omits degradation, reserves, network, and market constraints."
        ],
        "sources": [
          {
            "title": "SciPy mixed-integer linear programming with HiGHS",
            "url": "https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.milp.html"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "energy_capacity_kwh",
            "label": "Storage energy",
            "default": 500,
            "min": 0.001,
            "max": 10000000,
            "step": 10,
            "unit": "kWh"
          },
          {
            "name": "power_limit_kw",
            "label": "Charge/discharge limit",
            "default": 120,
            "min": 0.001,
            "max": 1000000,
            "step": 10,
            "unit": "kW"
          },
          {
            "name": "charge_efficiency",
            "label": "Charge efficiency",
            "default": 0.94,
            "min": 0.01,
            "max": 1,
            "step": 0.01,
            "unit": "fraction"
          },
          {
            "name": "discharge_efficiency",
            "label": "Discharge efficiency",
            "default": 0.94,
            "min": 0.01,
            "max": 1,
            "step": 0.01,
            "unit": "fraction"
          },
          {
            "name": "initial_soc_kwh",
            "label": "Fixed initial/terminal SOC",
            "default": 250,
            "min": 0,
            "max": 10000000,
            "step": 10,
            "unit": "kWh"
          }
        ],
        "jsonDataFields": [
          {
            "name": "load_kw",
            "label": "336 hourly demand values",
            "default": {
              "type": "array",
              "items": 336,
              "itemTypes": [
                "number"
              ],
              "sha256": "82c7687716acf5867c8ed176f4a9485d2712cea2107738134e7f9aed1645488d"
            }
          },
          {
            "name": "renewable_kw",
            "label": "336 hourly renewable values",
            "default": {
              "type": "array",
              "items": 336,
              "itemTypes": [
                "number"
              ],
              "sha256": "7591e40d252a1168119fad73b30ece299aa1c38315c2805ca08277e25cd4f7c3"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "8af60e8d4cf71a8d86659bb5e5c5ba2bf4d25d664bee3104b4f99dc22b87dec5",
        "evidenceSha256": "89a51d19bb519a2ef7d5c85c8930429869efb1fb73aa710bb87f67fe052b5eef",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"compare-dunkelflaute\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"compare-dunkelflaute\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "For a 100 kW, 100 kWh case, each interval is one-directional, energy closes, and terminal SOC returns to the zero initial target.",
          "testIds": [
            {
              "node_id": "tests/test_workflow_studies.py::test_dunkelflaute_dispatch_enforces_direction_and_physical_balance",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "energy_capacity_kwh",
              "unit": "kWh",
              "minimum": 0.001,
              "maximum": 10000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "power_limit_kw",
              "unit": "kW",
              "minimum": 0.001,
              "maximum": 1000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "charge_efficiency",
              "unit": "fraction",
              "minimum": 0.01,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "discharge_efficiency",
              "unit": "fraction",
              "minimum": 0.01,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "initial_soc_kwh",
              "unit": "kWh",
              "minimum": 0,
              "maximum": 10000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "load_kw",
              "label": "336 hourly demand values",
              "default": {
                "type": "array",
                "items": 336,
                "itemTypes": [
                  "number"
                ],
                "sha256": "82c7687716acf5867c8ed176f4a9485d2712cea2107738134e7f9aed1645488d"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "renewable_kw",
              "label": "336 hourly renewable values",
              "default": {
                "type": "array",
                "items": 336,
                "itemTypes": [
                  "number"
                ],
                "sha256": "7591e40d252a1168119fad73b30ece299aa1c38315c2805ca08277e25cd4f7c3"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/workflow_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "8af60e8d4cf71a8d86659bb5e5c5ba2bf4d25d664bee3104b4f99dc22b87dec5",
          "evidenceSha256": "89a51d19bb519a2ef7d5c85c8930429869efb1fb73aa710bb87f67fe052b5eef",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"compare-dunkelflaute\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"compare-dunkelflaute\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "For a 100 kW, 100 kWh case, each interval is one-directional, energy closes, and terminal SOC returns to the zero initial target.",
            "testIds": [
              {
                "node_id": "tests/test_workflow_studies.py::test_dunkelflaute_dispatch_enforces_direction_and_physical_balance",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "energy_capacity_kwh below declared minimum 0.001",
            "energy_capacity_kwh above declared maximum 10000000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "energy_capacity_kwh=10000000 (max bound)",
              "metric_snapshot": [
                {
                  "label": "Total unserved energy",
                  "value": 57521.9078188,
                  "unit": "kWh"
                },
                {
                  "label": "Shortfall intervals",
                  "value": 331,
                  "unit": "hours"
                },
                {
                  "label": "Renewable energy",
                  "value": 34968.876,
                  "unit": "kWh"
                },
                {
                  "label": "Curtailed renewable energy",
                  "value": 0,
                  "unit": "kWh"
                },
                {
                  "label": "Battery charge energy",
                  "value": 135.61700000000002,
                  "unit": "kWh"
                },
                {
                  "label": "Battery discharge energy",
                  "value": 119.8311812,
                  "unit": "kWh"
                },
                {
                  "label": "Terminal SOC",
                  "value": 250,
                  "unit": "kWh"
                },
                {
                  "label": "Electric balance residual",
                  "value": -1.6200374375330284e-11,
                  "unit": "kWh"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "PASS",
          "solver": "SciPy/HiGHS MILP",
          "feasibility_evidence": "The linked case checks interval direction, power/SOC bounds, energy balance, and terminal SOC.",
          "optimality_status": "U — not exposed in this result; feasibility checks do not independently prove global optimality."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "5181717ee2516b706483f77176d8e373e54ab5f143dcb9b96d2f6099bb9188f2"
          }
        },
        "limitations": [
          "One synthetic 336-hour weather path, not a repeated demo day or statistical LOLE estimate.",
          "Reduced mixed-integer dispatch enforces battery direction but omits degradation, reserves, network, and market constraints."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/workflow_studies.py",
          "tests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "compare-dunkelflaute",
            "workspace": "compare",
            "title": "Fourteen-day storage dispatch",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_workflow_studies.py::test_dunkelflaute_dispatch_enforces_direction_and_physical_balance",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "For a 100 kW, 100 kWh case, each interval is one-directional, energy closes, and terminal SOC returns to the zero initial target.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Total unserved energy",
                        "value": 0,
                        "unit": "kWh"
                      },
                      {
                        "label": "Shortfall intervals",
                        "value": 0,
                        "unit": "hours"
                      },
                      {
                        "label": "Renewable energy",
                        "value": 3360,
                        "unit": "kWh"
                      },
                      {
                        "label": "Curtailed renewable energy",
                        "value": 3360,
                        "unit": "kWh"
                      },
                      {
                        "label": "Battery charge energy",
                        "value": 1.7763568394002505e-15,
                        "unit": "kWh"
                      },
                      {
                        "label": "Battery discharge energy",
                        "value": 0,
                        "unit": "kWh"
                      },
                      {
                        "label": "Terminal SOC",
                        "value": 0,
                        "unit": "kWh"
                      },
                      {
                        "label": "Electric balance residual",
                        "value": 0,
                        "unit": "kWh"
                      }
                    ],
                    "scalar_details": {
                      "interval_count": 336,
                      "horizon_hours": 336,
                      "initial_soc_kwh": 0,
                      "terminal_soc_kwh": 0,
                      "soc_min_kwh": 0,
                      "soc_max_kwh": 0,
                      "storage_loss_kwh": 1.0658141036401512e-16,
                      "minimum_unserved_energy_kwh": 0,
                      "electric_balance_residual_kwh": 0,
                      "optimizer": "scipy.optimize.milp (HiGHS), with binary charge/discharge direction controls",
                      "tie_break": "minimize curtailment and then small throughput cost at fixed minimum shortfall"
                    },
                    "table_row_count": 336,
                    "result_sha256": "de612304806aae29822e8b49f14869cebc63ff7178d3ceb799001fd4d7a98730"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Total unserved energy",
                    "value": 57521.9078188,
                    "unit": "kWh"
                  },
                  {
                    "label": "Shortfall intervals",
                    "value": 331,
                    "unit": "hours"
                  },
                  {
                    "label": "Renewable energy",
                    "value": 34968.876,
                    "unit": "kWh"
                  },
                  {
                    "label": "Curtailed renewable energy",
                    "value": 0,
                    "unit": "kWh"
                  },
                  {
                    "label": "Battery charge energy",
                    "value": 135.61700000000002,
                    "unit": "kWh"
                  },
                  {
                    "label": "Battery discharge energy",
                    "value": 119.8311812,
                    "unit": "kWh"
                  },
                  {
                    "label": "Terminal SOC",
                    "value": 250,
                    "unit": "kWh"
                  },
                  {
                    "label": "Electric balance residual",
                    "value": -1.6200374375330284e-11,
                    "unit": "kWh"
                  }
                ],
                "default_result_sha256": "5181717ee2516b706483f77176d8e373e54ab5f143dcb9b96d2f6099bb9188f2"
              },
              "units": "kW / kWh",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 8
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "energy_capacity_kwh=10000000 (max bound)",
                "metric_snapshot": [
                  {
                    "label": "Total unserved energy",
                    "value": 57521.9078188,
                    "unit": "kWh"
                  },
                  {
                    "label": "Shortfall intervals",
                    "value": 331,
                    "unit": "hours"
                  },
                  {
                    "label": "Renewable energy",
                    "value": 34968.876,
                    "unit": "kWh"
                  },
                  {
                    "label": "Curtailed renewable energy",
                    "value": 0,
                    "unit": "kWh"
                  },
                  {
                    "label": "Battery charge energy",
                    "value": 135.61700000000002,
                    "unit": "kWh"
                  },
                  {
                    "label": "Battery discharge energy",
                    "value": 119.8311812,
                    "unit": "kWh"
                  },
                  {
                    "label": "Terminal SOC",
                    "value": 250,
                    "unit": "kWh"
                  },
                  {
                    "label": "Electric balance residual",
                    "value": -1.6200374375330284e-11,
                    "unit": "kWh"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "5181717ee2516b706483f77176d8e373e54ab5f143dcb9b96d2f6099bb9188f2"
              },
              "solver_feasibility": {
                "status": "PASS",
                "solver": "SciPy/HiGHS MILP",
                "feasibility_evidence": "The linked case checks interval direction, power/SOC bounds, energy balance, and terminal SOC.",
                "optimality_status": "U — not exposed in this result; feasibility checks do not independently prove global optimality."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/workflow_studies.py",
              "engine_sha256": "021597dd1d8b016f15bffa8494fb23a553fb93b26f9ae48c7a17061ec19ca585",
              "test_file": "apps/api/tests/test_workflow_studies.py",
              "test_sha256": "5649c323bcfa2ade89e03a7c12417835938366fbc0444f5b4b2954d65eee0221",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "One synthetic 336-hour weather path, not a repeated demo day or statistical LOLE estimate.",
              "Reduced mixed-integer dispatch enforces battery direction but omits degradation, reserves, network, and market constraints.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-compare-dunkelflaute"
    },
    {
      "id": "compare-thermal",
      "title": "Thermal and cycle-wear screen",
      "summary": "Compare utilization, equivalent full cycles, an explicit temperature multiplier, and three-axis radar data.",
      "workspace": {
        "id": "compare",
        "name": "Scenario compare",
        "appRoute": "#compare"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "comparison",
        "equations": [
          "EFC = years * 365 * cycles/day * utilization * depth of discharge",
          "thermal multiplier = exp(user factor * (temperature - reference temperature))",
          "screening wear index = EFC * user loss fraction / 1000 * thermal multiplier"
        ],
        "limitations": [
          "Temperature factor and cycle-loss rate are assumptions, not fitted cell-aging laws.",
          "Wear index may exceed 100%; it is not a capacity-fade forecast, warranty, or safety limit."
        ],
        "sources": [
          {
            "title": "NREL Battery Lifetime Analysis and Simulation Tool overview",
            "url": "https://www.nrel.gov/transportation/battery-lifespan.html"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "reference_temperature_c",
            "label": "Reference temperature",
            "default": 25,
            "min": -40,
            "max": 100,
            "step": 1,
            "unit": "°C"
          },
          {
            "name": "temperature_factor_per_c",
            "label": "Assumed exponential factor per °C",
            "default": 0.025,
            "min": 0,
            "max": 0.1,
            "step": 0.001,
            "unit": "1/°C"
          },
          {
            "name": "screening_loss_fraction_per_1000_efc",
            "label": "Assumed loss per 1,000 EFC",
            "default": 0.025,
            "min": 0,
            "max": 1,
            "step": 0.005,
            "unit": "fraction"
          },
          {
            "name": "years",
            "label": "Screening horizon",
            "default": 1,
            "min": 1,
            "max": 30,
            "step": 1,
            "unit": "year"
          }
        ],
        "jsonDataFields": [
          {
            "name": "regimes",
            "label": "Operating regimes",
            "default": {
              "type": "array",
              "items": 3,
              "itemTypes": [
                "object"
              ],
              "sha256": "80f6accecf99eba2f0b9eaf452917f80440544b4706368f04a9f4ab6d21e3ea2"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "def66335c4dbfa79561796b23366e2d2559bed9833b40065e22a3a4b73aa6720",
        "evidenceSha256": "9e7ebf8fafac1e98389c3b5a9de3afa154ae2df791e6b0423168b8d57b085d0c",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"compare-thermal\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"compare-thermal\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "For one year at 1 cycle/day, full utilization and depth, EFC is 365; at reference temperature multiplier is 1 and 0.1%/1000 EFC gives 3.65% screening index.",
          "testIds": [
            {
              "node_id": "tests/test_study_acceptance_contracts.py::test_thermal_comparison_matches_single_regime_hand_calculation",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "reference_temperature_c",
              "unit": "°C",
              "minimum": -40,
              "maximum": 100,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "temperature_factor_per_c",
              "unit": "1/°C",
              "minimum": 0,
              "maximum": 0.1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "screening_loss_fraction_per_1000_efc",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "years",
              "unit": "year",
              "minimum": 1,
              "maximum": 30,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "regimes",
              "label": "Operating regimes",
              "default": {
                "type": "array",
                "items": 3,
                "itemTypes": [
                  "object"
                ],
                "sha256": "80f6accecf99eba2f0b9eaf452917f80440544b4706368f04a9f4ab6d21e3ea2"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/workflow_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "def66335c4dbfa79561796b23366e2d2559bed9833b40065e22a3a4b73aa6720",
          "evidenceSha256": "9e7ebf8fafac1e98389c3b5a9de3afa154ae2df791e6b0423168b8d57b085d0c",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"compare-thermal\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"compare-thermal\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "For one year at 1 cycle/day, full utilization and depth, EFC is 365; at reference temperature multiplier is 1 and 0.1%/1000 EFC gives 3.65% screening index.",
            "testIds": [
              {
                "node_id": "tests/test_study_acceptance_contracts.py::test_thermal_comparison_matches_single_regime_hand_calculation",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "reference_temperature_c below declared minimum -40",
            "reference_temperature_c above declared maximum 100",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "reference_temperature_c=-40 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Operating regimes",
                  "value": 3,
                  "unit": "regimes"
                },
                {
                  "label": "Maximum screening wear index",
                  "value": 9.157430821396591,
                  "unit": "% index"
                },
                {
                  "label": "Maximum equivalent full cycles",
                  "value": 561.735,
                  "unit": "EFC"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "5a82a2b408920cf6f880372e8e5293b81d83c7f386cbb00e435a5e281ab0ff70"
          }
        },
        "limitations": [
          "Temperature factor and cycle-loss rate are assumptions, not fitted cell-aging laws.",
          "Wear index may exceed 100%; it is not a capacity-fade forecast, warranty, or safety limit."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/workflow_studies.py",
          "tests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "compare-thermal",
            "workspace": "compare",
            "title": "Thermal and cycle-wear screen",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_study_acceptance_contracts.py::test_thermal_comparison_matches_single_regime_hand_calculation",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "For one year at 1 cycle/day, full utilization and depth, EFC is 365; at reference temperature multiplier is 1 and 0.1%/1000 EFC gives 3.65% screening index.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Operating regimes",
                        "value": 1,
                        "unit": "regimes"
                      },
                      {
                        "label": "Maximum screening wear index",
                        "value": 3.65,
                        "unit": "% index"
                      },
                      {
                        "label": "Maximum equivalent full cycles",
                        "value": 365,
                        "unit": "EFC"
                      }
                    ],
                    "scalar_details": {},
                    "table_row_count": 1,
                    "result_sha256": "7511ccef954986593323cf3e039cc630c286af4c73af66e07115f6133cdaeb99"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Operating regimes",
                    "value": 3,
                    "unit": "regimes"
                  },
                  {
                    "label": "Maximum screening wear index",
                    "value": 1.8032050436077212,
                    "unit": "% index"
                  },
                  {
                    "label": "Maximum equivalent full cycles",
                    "value": 561.735,
                    "unit": "EFC"
                  }
                ],
                "default_result_sha256": "5a82a2b408920cf6f880372e8e5293b81d83c7f386cbb00e435a5e281ab0ff70"
              },
              "units": "EFC / multiplier / % index",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 3
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "reference_temperature_c=-40 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Operating regimes",
                    "value": 3,
                    "unit": "regimes"
                  },
                  {
                    "label": "Maximum screening wear index",
                    "value": 9.157430821396591,
                    "unit": "% index"
                  },
                  {
                    "label": "Maximum equivalent full cycles",
                    "value": 561.735,
                    "unit": "EFC"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "5a82a2b408920cf6f880372e8e5293b81d83c7f386cbb00e435a5e281ab0ff70"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/workflow_studies.py",
              "engine_sha256": "021597dd1d8b016f15bffa8494fb23a553fb93b26f9ae48c7a17061ec19ca585",
              "test_file": "apps/api/tests/test_study_acceptance_contracts.py",
              "test_sha256": "62a64ad856c68d521f0468066f4a1d642e2b5324faa7848c22e0e509fd123f42",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Temperature factor and cycle-loss rate are assumptions, not fitted cell-aging laws.",
              "Wear index may exceed 100%; it is not a capacity-fade forecast, warranty, or safety limit.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-compare-thermal"
    },
    {
      "id": "pilots-photogrammetry",
      "title": "Paired as-built geometry check",
      "summary": "Compute 3D paired-point residuals, RMSE, maximum, and a selected percentile.",
      "workspace": {
        "id": "pilots",
        "name": "Construction & pilots",
        "appRoute": "#pilots"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "pilot",
        "equations": [
          "error[i] = norm(as-built XYZ[i] - design XYZ[i]) * coordinate scale",
          "3D RMSE = sqrt(mean(error[i]^2))",
          "selected error = empirical percentile of paired errors"
        ],
        "limitations": [
          "Pairs are matched by array index; no registration, nearest-neighbour search, camera model, or drone connector.",
          "The user reporting limit does not establish survey or construction acceptance."
        ],
        "sources": [
          {
            "title": "ASPRS Positional Accuracy Standards",
            "url": "https://www.asprs.org/Main/Main/Standards/Positional-Accuracy-Standards.aspx"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "coordinate_scale_m",
            "label": "Coordinate-to-metre scale",
            "default": 1,
            "min": 0.000001,
            "max": 100000,
            "step": 0.1,
            "unit": "m/unit"
          },
          {
            "name": "reporting_limit_m",
            "label": "User reporting limit",
            "default": 0.05,
            "min": 0,
            "max": 100000,
            "step": 0.01,
            "unit": "m"
          },
          {
            "name": "percentile",
            "label": "Error percentile",
            "default": 95,
            "min": 50,
            "max": 99.9,
            "step": 1,
            "unit": "%"
          }
        ],
        "jsonDataFields": [
          {
            "name": "design_points_xyz",
            "label": "Design XYZ points, index-paired",
            "default": {
              "type": "array",
              "items": 4,
              "itemTypes": [
                "array"
              ],
              "sha256": "6e9106a2fce695bf8b464f17641c8dd54e531b9f38ec1e444421c41f5949a69e"
            }
          },
          {
            "name": "asbuilt_points_xyz",
            "label": "As-built XYZ points, index-paired",
            "default": {
              "type": "array",
              "items": 4,
              "itemTypes": [
                "array"
              ],
              "sha256": "1d773ffb0ac6f68200d7106dfdcb01307dd29bc286e38cf9cad8a9dfd46cc887"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "75676277e4f66bf40a75f8e2dc2ecca75e6f9474083c5731d07f175859086f29",
        "evidenceSha256": "390ffec5d3cffcd21004820749913b489ff8a1ec3776ad06463e5ba48301f6b3",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pilots-photogrammetry\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pilots-photogrammetry\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Paired errors [0, 3, 4] m yield RMSE sqrt(25/3), maximum 4 m and P50 3 m; empty data preserves null RMSE.",
          "testIds": [
            {
              "node_id": "tests/test_workflow_studies.py::test_photogrammetry_hand_case_empty_cloud_and_mismatch",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "coordinate_scale_m",
              "unit": "m/unit",
              "minimum": 0.000001,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "reporting_limit_m",
              "unit": "m",
              "minimum": 0,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "percentile",
              "unit": "%",
              "minimum": 50,
              "maximum": 99.9,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "design_points_xyz",
              "label": "Design XYZ points, index-paired",
              "default": {
                "type": "array",
                "items": 4,
                "itemTypes": [
                  "array"
                ],
                "sha256": "6e9106a2fce695bf8b464f17641c8dd54e531b9f38ec1e444421c41f5949a69e"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "asbuilt_points_xyz",
              "label": "As-built XYZ points, index-paired",
              "default": {
                "type": "array",
                "items": 4,
                "itemTypes": [
                  "array"
                ],
                "sha256": "1d773ffb0ac6f68200d7106dfdcb01307dd29bc286e38cf9cad8a9dfd46cc887"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/workflow_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "75676277e4f66bf40a75f8e2dc2ecca75e6f9474083c5731d07f175859086f29",
          "evidenceSha256": "390ffec5d3cffcd21004820749913b489ff8a1ec3776ad06463e5ba48301f6b3",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pilots-photogrammetry\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pilots-photogrammetry\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Paired errors [0, 3, 4] m yield RMSE sqrt(25/3), maximum 4 m and P50 3 m; empty data preserves null RMSE.",
            "testIds": [
              {
                "node_id": "tests/test_workflow_studies.py::test_photogrammetry_hand_case_empty_cloud_and_mismatch",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "coordinate_scale_m below declared minimum 0.000001",
            "coordinate_scale_m above declared maximum 100000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "coordinate_scale_m=1e-06 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Paired point count",
                  "value": 4,
                  "unit": "points"
                },
                {
                  "label": "3D RMSE",
                  "value": 2.7386127875258317e-8,
                  "unit": "m"
                },
                {
                  "label": "Maximum paired error",
                  "value": 4.0000000000000034e-8,
                  "unit": "m"
                },
                {
                  "label": "P95 paired error",
                  "value": 3.850000000000003e-8,
                  "unit": "m"
                },
                {
                  "label": "Points above user reporting limit",
                  "value": 0,
                  "unit": "points"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "fe76e99f156ae6fbf629edc35e22568dd65b61486cbbd39ec562b6cf15d37e6b"
          }
        },
        "limitations": [
          "Pairs are matched by array index; no registration, nearest-neighbour search, camera model, or drone connector.",
          "The user reporting limit does not establish survey or construction acceptance."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/workflow_studies.py",
          "tests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "pilots-photogrammetry",
            "workspace": "pilots",
            "title": "Paired as-built geometry check",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_workflow_studies.py::test_photogrammetry_hand_case_empty_cloud_and_mismatch",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Paired errors [0, 3, 4] m yield RMSE sqrt(25/3), maximum 4 m and P50 3 m; empty data preserves null RMSE.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Paired point count",
                        "value": 3,
                        "unit": "points"
                      },
                      {
                        "label": "3D RMSE",
                        "value": 2.886751345948129,
                        "unit": "m"
                      },
                      {
                        "label": "Maximum paired error",
                        "value": 4,
                        "unit": "m"
                      },
                      {
                        "label": "P50 paired error",
                        "value": 3,
                        "unit": "m"
                      },
                      {
                        "label": "Points above user reporting limit",
                        "value": 2,
                        "unit": "points"
                      }
                    ],
                    "scalar_details": {
                      "coordinate_scale_m": 1,
                      "reporting_limit_m": 0.05,
                      "percentile": 50,
                      "pairing_assumption": "same array index"
                    },
                    "table_row_count": 3,
                    "result_sha256": "3472110334dc564bfe5afa1859f302aed4bf6df8cd95584504258f5d885a61bd"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Paired point count",
                        "value": 0,
                        "unit": "points"
                      },
                      {
                        "label": "3D RMSE",
                        "value": null,
                        "unit": "m"
                      },
                      {
                        "label": "Maximum paired error",
                        "value": null,
                        "unit": "m"
                      },
                      {
                        "label": "P95 paired error",
                        "value": null,
                        "unit": "m"
                      },
                      {
                        "label": "Points above user reporting limit",
                        "value": 0,
                        "unit": "points"
                      }
                    ],
                    "scalar_details": {
                      "coordinate_scale_m": 1,
                      "reporting_limit_m": 0.05,
                      "percentile": 95,
                      "pairing_assumption": "same array index"
                    },
                    "table_row_count": 0,
                    "result_sha256": "0b335361815a949d4e83ed715239d13f065e628f429982179c7124164ed14cb6"
                  },
                  {
                    "outcome": "raised",
                    "exception": "ValidationError",
                    "message": "1 validation error for PhotogrammetryInput\n  Value error, Design and as-built clouds must have equal index-paired lengths. [type=value_error, input_value={'design_points_xyz': [[0...asbuilt_points_xyz': []}, input_type=dict]\n    For further"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Paired point count",
                    "value": 4,
                    "unit": "points"
                  },
                  {
                    "label": "3D RMSE",
                    "value": 0.02738612787525832,
                    "unit": "m"
                  },
                  {
                    "label": "Maximum paired error",
                    "value": 0.040000000000000036,
                    "unit": "m"
                  },
                  {
                    "label": "P95 paired error",
                    "value": 0.03850000000000003,
                    "unit": "m"
                  },
                  {
                    "label": "Points above user reporting limit",
                    "value": 0,
                    "unit": "points"
                  }
                ],
                "default_result_sha256": "fe76e99f156ae6fbf629edc35e22568dd65b61486cbbd39ec562b6cf15d37e6b"
              },
              "units": "m / points",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 5
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "coordinate_scale_m=1e-06 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Paired point count",
                    "value": 4,
                    "unit": "points"
                  },
                  {
                    "label": "3D RMSE",
                    "value": 2.7386127875258317e-8,
                    "unit": "m"
                  },
                  {
                    "label": "Maximum paired error",
                    "value": 4.0000000000000034e-8,
                    "unit": "m"
                  },
                  {
                    "label": "P95 paired error",
                    "value": 3.850000000000003e-8,
                    "unit": "m"
                  },
                  {
                    "label": "Points above user reporting limit",
                    "value": 0,
                    "unit": "points"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "fe76e99f156ae6fbf629edc35e22568dd65b61486cbbd39ec562b6cf15d37e6b"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/workflow_studies.py",
              "engine_sha256": "021597dd1d8b016f15bffa8494fb23a553fb93b26f9ae48c7a17061ec19ca585",
              "test_file": "apps/api/tests/test_workflow_studies.py",
              "test_sha256": "5649c323bcfa2ade89e03a7c12417835938366fbc0444f5b4b2954d65eee0221",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Pairs are matched by array index; no registration, nearest-neighbour search, camera model, or drone connector.",
              "The user reporting limit does not establish survey or construction acceptance.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-pilots-photogrammetry"
    },
    {
      "id": "pilots-dts",
      "title": "Distributed temperature profile",
      "summary": "Report fresh hot spots, spatial gradients, missing samples, and stale observations.",
      "workspace": {
        "id": "pilots",
        "name": "Construction & pilots",
        "appRoute": "#pilots"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "pilot",
        "equations": [
          "gradient = abs(adjacent temperature difference) / adjacent distance",
          "fresh when current time - observed time <= user maximum age"
        ],
        "limitations": [
          "Sample temperatures and the user threshold are bounded to -50–200 °C; adjacent samples must be at least 0.1 m apart.",
          "Thresholds are user screening values; no cable rating or calibrated alarm is claimed. Missing and stale observations are excluded from current thermal summaries."
        ],
        "sources": [
          {
            "title": "Spatial patterns and limits in distributed temperature sensing",
            "url": "https://agupubs.onlinelibrary.wiley.com/doi/full/10.1002/wrcr.20144"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "hot_spot_threshold_c",
            "label": "User hot-spot threshold",
            "default": 75,
            "min": -50,
            "max": 200,
            "step": 1,
            "unit": "°C"
          },
          {
            "name": "gradient_threshold_c_per_m",
            "label": "User gradient threshold",
            "default": 0.25,
            "min": 0,
            "max": 100000,
            "step": 0.05,
            "unit": "°C/m"
          },
          {
            "name": "maximum_age_hours",
            "label": "Maximum data age",
            "default": 2,
            "min": 0,
            "max": 100000,
            "step": 0.25,
            "unit": "h"
          },
          {
            "name": "current_time_hour",
            "label": "Supplied current time",
            "default": 10,
            "min": 0,
            "max": 1000000,
            "step": 1,
            "unit": "h"
          }
        ],
        "jsonDataFields": [
          {
            "name": "samples",
            "label": "Spatial temperature samples with timestamps",
            "default": {
              "type": "array",
              "items": 5,
              "itemTypes": [
                "object"
              ],
              "sha256": "1bcf94d8bbd2a4c3b870a17302b07fbde25a88ce3e5a2918135dc0e15c29dd35"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "f59e798220b3d7b1be3368ff8c1cc719e24f83737ef58e0746ddc750bd062640",
        "evidenceSha256": "947c2de4f18bb9348d6a9f6a9b3e4add3b88a49b1f21724184b01b6692b18818",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pilots-dts\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pilots-dts\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "At now=10 h and max age=1 h, the 9 h reading is stale by the strict age rule and the null reading remains missing.",
          "testIds": [
            {
              "node_id": "tests/test_workflow_studies.py::test_dts_reports_stale_missing_and_only_fresh_gradient_data",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "hot_spot_threshold_c",
              "unit": "°C",
              "minimum": -50,
              "maximum": 200,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "gradient_threshold_c_per_m",
              "unit": "°C/m",
              "minimum": 0,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "maximum_age_hours",
              "unit": "h",
              "minimum": 0,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "current_time_hour",
              "unit": "h",
              "minimum": 0,
              "maximum": 1000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "samples",
              "label": "Spatial temperature samples with timestamps",
              "default": {
                "type": "array",
                "items": 5,
                "itemTypes": [
                  "object"
                ],
                "sha256": "1bcf94d8bbd2a4c3b870a17302b07fbde25a88ce3e5a2918135dc0e15c29dd35"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/workflow_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "f59e798220b3d7b1be3368ff8c1cc719e24f83737ef58e0746ddc750bd062640",
          "evidenceSha256": "947c2de4f18bb9348d6a9f6a9b3e4add3b88a49b1f21724184b01b6692b18818",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pilots-dts\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pilots-dts\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "At now=10 h and max age=1 h, the 9 h reading is stale by the strict age rule and the null reading remains missing.",
            "testIds": [
              {
                "node_id": "tests/test_workflow_studies.py::test_dts_reports_stale_missing_and_only_fresh_gradient_data",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "hot_spot_threshold_c below declared minimum -50",
            "hot_spot_threshold_c above declared maximum 200",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "hot_spot_threshold_c=-50 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Fresh samples",
                  "value": 4,
                  "unit": "samples"
                },
                {
                  "label": "Missing samples",
                  "value": 1,
                  "unit": "samples"
                },
                {
                  "label": "Stale samples",
                  "value": 0,
                  "unit": "samples"
                },
                {
                  "label": "Hot spots above user threshold",
                  "value": 4,
                  "unit": "samples"
                },
                {
                  "label": "Maximum fresh temperature",
                  "value": 81,
                  "unit": "°C"
                },
                {
                  "label": "Maximum adjacent fresh gradient",
                  "value": 4.4,
                  "unit": "°C/m"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "57ad130ade84a2dd582cd278d16cb4ec9b3e0c1c046768798fca1e89d7c3fdfe"
          }
        },
        "limitations": [
          "Sample temperatures and the user threshold are bounded to -50–200 °C; adjacent samples must be at least 0.1 m apart.",
          "Thresholds are user screening values; no cable rating or calibrated alarm is claimed. Missing and stale observations are excluded from current thermal summaries."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/workflow_studies.py",
          "tests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "pilots-dts",
            "workspace": "pilots",
            "title": "Distributed temperature profile",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_workflow_studies.py::test_dts_reports_stale_missing_and_only_fresh_gradient_data",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "At now=10 h and max age=1 h, the 9 h reading is stale by the strict age rule and the null reading remains missing.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Fresh samples",
                        "value": 0,
                        "unit": "samples"
                      },
                      {
                        "label": "Missing samples",
                        "value": 1,
                        "unit": "samples"
                      },
                      {
                        "label": "Stale samples",
                        "value": 1,
                        "unit": "samples"
                      },
                      {
                        "label": "Hot spots above user threshold",
                        "value": 0,
                        "unit": "samples"
                      },
                      {
                        "label": "Maximum fresh temperature",
                        "value": null,
                        "unit": "°C"
                      },
                      {
                        "label": "Maximum adjacent fresh gradient",
                        "value": null,
                        "unit": "°C/m"
                      }
                    ],
                    "scalar_details": {
                      "gradient_exceedance_count": 0
                    },
                    "table_row_count": 2,
                    "result_sha256": "2384c05f481e197884bf8ab7ee8d49b20fa9ed71afbde838b50ec752da3989e2"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Fresh samples",
                        "value": 2,
                        "unit": "samples"
                      },
                      {
                        "label": "Missing samples",
                        "value": 1,
                        "unit": "samples"
                      },
                      {
                        "label": "Stale samples",
                        "value": 0,
                        "unit": "samples"
                      },
                      {
                        "label": "Hot spots above user threshold",
                        "value": 1,
                        "unit": "samples"
                      },
                      {
                        "label": "Maximum fresh temperature",
                        "value": 100,
                        "unit": "°C"
                      },
                      {
                        "label": "Maximum adjacent fresh gradient",
                        "value": null,
                        "unit": "°C/m"
                      }
                    ],
                    "scalar_details": {
                      "gradient_exceedance_count": 0
                    },
                    "table_row_count": 3,
                    "result_sha256": "17b4ba4c31df4f2b4513646ab9d4ada76680cd3f9da60d426090f324f00f0933"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Fresh samples",
                    "value": 4,
                    "unit": "samples"
                  },
                  {
                    "label": "Missing samples",
                    "value": 1,
                    "unit": "samples"
                  },
                  {
                    "label": "Stale samples",
                    "value": 0,
                    "unit": "samples"
                  },
                  {
                    "label": "Hot spots above user threshold",
                    "value": 1,
                    "unit": "samples"
                  },
                  {
                    "label": "Maximum fresh temperature",
                    "value": 81,
                    "unit": "°C"
                  },
                  {
                    "label": "Maximum adjacent fresh gradient",
                    "value": 4.4,
                    "unit": "°C/m"
                  }
                ],
                "default_result_sha256": "57ad130ade84a2dd582cd278d16cb4ec9b3e0c1c046768798fca1e89d7c3fdfe"
              },
              "units": "hours / °C / °C/m",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 6
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "hot_spot_threshold_c=-50 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Fresh samples",
                    "value": 4,
                    "unit": "samples"
                  },
                  {
                    "label": "Missing samples",
                    "value": 1,
                    "unit": "samples"
                  },
                  {
                    "label": "Stale samples",
                    "value": 0,
                    "unit": "samples"
                  },
                  {
                    "label": "Hot spots above user threshold",
                    "value": 4,
                    "unit": "samples"
                  },
                  {
                    "label": "Maximum fresh temperature",
                    "value": 81,
                    "unit": "°C"
                  },
                  {
                    "label": "Maximum adjacent fresh gradient",
                    "value": 4.4,
                    "unit": "°C/m"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "57ad130ade84a2dd582cd278d16cb4ec9b3e0c1c046768798fca1e89d7c3fdfe"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/workflow_studies.py",
              "engine_sha256": "021597dd1d8b016f15bffa8494fb23a553fb93b26f9ae48c7a17061ec19ca585",
              "test_file": "apps/api/tests/test_workflow_studies.py",
              "test_sha256": "5649c323bcfa2ade89e03a7c12417835938366fbc0444f5b4b2954d65eee0221",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Sample temperatures and the user threshold are bounded to -50–200 °C; adjacent samples must be at least 0.1 m apart.",
              "Thresholds are user screening values; no cable rating or calibrated alarm is claimed. Missing and stale observations are excluded from current thermal summaries.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-pilots-dts"
    },
    {
      "id": "pilots-offgas",
      "title": "Hypothetical multisensor warning vote",
      "summary": "Apply temperature, CO, and VOC thresholds and a sensor-level voting rule.",
      "workspace": {
        "id": "pilots",
        "name": "Construction & pilots",
        "appRoute": "#pilots"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "pilot",
        "equations": [
          "sensor vote = any present channel meets or exceeds its user threshold",
          "warning state = sensor votes >= required votes"
        ],
        "limitations": [
          "Hypothetical logic only; thresholds are not calibrated alarms, exposure limits, or hazard controls.",
          "No gas analyser, sensor-health connector, cross-sensitivity model, or field alarm is implemented."
        ],
        "sources": [
          {
            "title": "NIOSH Pocket Guide to Chemical Hazards (reference information only)",
            "url": "https://www.cdc.gov/niosh/npg/"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "temperature_threshold_c",
            "label": "User temperature threshold",
            "default": 70,
            "min": -100,
            "max": 500,
            "step": 1,
            "unit": "°C"
          },
          {
            "name": "co_threshold_ppm",
            "label": "User CO threshold",
            "default": 35,
            "min": 0.001,
            "max": 100000,
            "step": 1,
            "unit": "ppm"
          },
          {
            "name": "voc_threshold_ppm",
            "label": "User VOC threshold",
            "default": 5,
            "min": 0.001,
            "max": 100000,
            "step": 0.5,
            "unit": "ppm"
          },
          {
            "name": "votes_required",
            "label": "Sensor votes required",
            "default": 2,
            "min": 1,
            "max": 32,
            "step": 1,
            "unit": "sensors"
          }
        ],
        "jsonDataFields": [
          {
            "name": "sensors",
            "label": "Temperature/CO/VOC readings",
            "default": {
              "type": "array",
              "items": 3,
              "itemTypes": [
                "object"
              ],
              "sha256": "02784d3a1195bfb3bf704df82bc03182c22d719079b271a971f4a9ad858e50bc"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "b91d1186ba0f6ff26716ae33b92353cc69d9be8d89716e0b4f3bb0280ba49d4a",
        "evidenceSha256": "3917f701da3cefd434c2eceb1548842944107e1e76ffea54389293e6a9b41456",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pilots-offgas\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pilots-offgas\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Two sensors each cast one vote when any present channel meets its threshold; state remains a hypothetical warning.",
          "testIds": [
            {
              "node_id": "tests/test_workflow_studies.py::test_offgas_warning_is_explicit_hypothetical_sensor_vote",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "temperature_threshold_c",
              "unit": "°C",
              "minimum": -100,
              "maximum": 500,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "co_threshold_ppm",
              "unit": "ppm",
              "minimum": 0.001,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "voc_threshold_ppm",
              "unit": "ppm",
              "minimum": 0.001,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "votes_required",
              "unit": "sensors",
              "minimum": 1,
              "maximum": 32,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "sensors",
              "label": "Temperature/CO/VOC readings",
              "default": {
                "type": "array",
                "items": 3,
                "itemTypes": [
                  "object"
                ],
                "sha256": "02784d3a1195bfb3bf704df82bc03182c22d719079b271a971f4a9ad858e50bc"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/workflow_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "b91d1186ba0f6ff26716ae33b92353cc69d9be8d89716e0b4f3bb0280ba49d4a",
          "evidenceSha256": "3917f701da3cefd434c2eceb1548842944107e1e76ffea54389293e6a9b41456",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pilots-offgas\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pilots-offgas\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Two sensors each cast one vote when any present channel meets its threshold; state remains a hypothetical warning.",
            "testIds": [
              {
                "node_id": "tests/test_workflow_studies.py::test_offgas_warning_is_explicit_hypothetical_sensor_vote",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "temperature_threshold_c below declared minimum -100",
            "temperature_threshold_c above declared maximum 500",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "temperature_threshold_c=-100 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Sensors voting",
                  "value": 3,
                  "unit": "sensors"
                },
                {
                  "label": "Required votes",
                  "value": 2,
                  "unit": "sensors"
                },
                {
                  "label": "Hypothetical warning state",
                  "value": "warning",
                  "unit": "state"
                },
                {
                  "label": "Missing channel readings",
                  "value": 2,
                  "unit": "channels"
                },
                {
                  "label": "Temperature exceedances",
                  "value": 3,
                  "unit": "sensors"
                },
                {
                  "label": "CO exceedances",
                  "value": 1,
                  "unit": "sensors"
                },
                {
                  "label": "VOC exceedances",
                  "value": 1,
                  "unit": "sensors"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "4dd48c37aeebd3257c3116515653bd7549508821b67d376bdf261ba2e416f0af"
          }
        },
        "limitations": [
          "Hypothetical logic only; thresholds are not calibrated alarms, exposure limits, or hazard controls.",
          "No gas analyser, sensor-health connector, cross-sensitivity model, or field alarm is implemented."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/workflow_studies.py",
          "tests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "pilots-offgas",
            "workspace": "pilots",
            "title": "Hypothetical multisensor warning vote",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_workflow_studies.py::test_offgas_warning_is_explicit_hypothetical_sensor_vote",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Two sensors each cast one vote when any present channel meets its threshold; state remains a hypothetical warning.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Sensors voting",
                        "value": 2,
                        "unit": "sensors"
                      },
                      {
                        "label": "Required votes",
                        "value": 2,
                        "unit": "sensors"
                      },
                      {
                        "label": "Hypothetical warning state",
                        "value": "warning",
                        "unit": "state"
                      },
                      {
                        "label": "Missing channel readings",
                        "value": 3,
                        "unit": "channels"
                      },
                      {
                        "label": "Temperature exceedances",
                        "value": 0,
                        "unit": "sensors"
                      },
                      {
                        "label": "CO exceedances",
                        "value": 1,
                        "unit": "sensors"
                      },
                      {
                        "label": "VOC exceedances",
                        "value": 1,
                        "unit": "sensors"
                      }
                    ],
                    "scalar_details": {
                      "logic": "one vote per sensor if any present channel meets or exceeds its threshold"
                    },
                    "table_row_count": 2,
                    "result_sha256": "6c394e4bfebc0683444a2001814b12da05ec483e0e2de086d0b209032ee8f701"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Sensors voting",
                    "value": 2,
                    "unit": "sensors"
                  },
                  {
                    "label": "Required votes",
                    "value": 2,
                    "unit": "sensors"
                  },
                  {
                    "label": "Hypothetical warning state",
                    "value": "warning",
                    "unit": "state"
                  },
                  {
                    "label": "Missing channel readings",
                    "value": 2,
                    "unit": "channels"
                  },
                  {
                    "label": "Temperature exceedances",
                    "value": 1,
                    "unit": "sensors"
                  },
                  {
                    "label": "CO exceedances",
                    "value": 1,
                    "unit": "sensors"
                  },
                  {
                    "label": "VOC exceedances",
                    "value": 1,
                    "unit": "sensors"
                  }
                ],
                "default_result_sha256": "4dd48c37aeebd3257c3116515653bd7549508821b67d376bdf261ba2e416f0af"
              },
              "units": "sensors / state",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 7
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "temperature_threshold_c=-100 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Sensors voting",
                    "value": 3,
                    "unit": "sensors"
                  },
                  {
                    "label": "Required votes",
                    "value": 2,
                    "unit": "sensors"
                  },
                  {
                    "label": "Hypothetical warning state",
                    "value": "warning",
                    "unit": "state"
                  },
                  {
                    "label": "Missing channel readings",
                    "value": 2,
                    "unit": "channels"
                  },
                  {
                    "label": "Temperature exceedances",
                    "value": 3,
                    "unit": "sensors"
                  },
                  {
                    "label": "CO exceedances",
                    "value": 1,
                    "unit": "sensors"
                  },
                  {
                    "label": "VOC exceedances",
                    "value": 1,
                    "unit": "sensors"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "4dd48c37aeebd3257c3116515653bd7549508821b67d376bdf261ba2e416f0af"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/workflow_studies.py",
              "engine_sha256": "021597dd1d8b016f15bffa8494fb23a553fb93b26f9ae48c7a17061ec19ca585",
              "test_file": "apps/api/tests/test_workflow_studies.py",
              "test_sha256": "5649c323bcfa2ade89e03a7c12417835938366fbc0444f5b4b2954d65eee0221",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Hypothetical logic only; thresholds are not calibrated alarms, exposure limits, or hazard controls.",
              "No gas analyser, sensor-health connector, cross-sensitivity model, or field alarm is implemented.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-pilots-offgas"
    },
    {
      "id": "council-debate",
      "title": "Finance versus storage debate",
      "summary": "Create deterministic proposer/challenger rounds and leave unresolved evidence conflicts visible.",
      "workspace": {
        "id": "council",
        "name": "Agent council",
        "appRoute": "#council"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "council",
        "equations": [
          "NPV = -capex + sum(annual net value / (1 + discount rate)^year)",
          "matched claims converge only when units agree and values fit declared tolerance"
        ],
        "limitations": [
          "Rounds are deterministic comparisons of supplied assertions; no LLM or external agent is called.",
          "Conflicts remain disputed; the NPV screen is not approval or a dispatch-finance bridge."
        ],
        "sources": [
          {
            "title": "NREL levelized cost of energy documentation",
            "url": "https://samrepo.nlr.gov/help/mtf_lcoe.html"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "capex_eur",
            "label": "Illustrative capex",
            "default": 1200000,
            "min": 0,
            "max": 1000000000000,
            "step": 10000,
            "unit": "EUR"
          },
          {
            "name": "annual_net_value_eur",
            "label": "Illustrative annual value",
            "default": 230000,
            "min": -1000000000,
            "max": 1000000000,
            "step": 1000,
            "unit": "EUR/year"
          },
          {
            "name": "life_years",
            "label": "Illustrative life",
            "default": 15,
            "min": 1,
            "max": 60,
            "step": 1,
            "unit": "year"
          },
          {
            "name": "discount_rate_fraction",
            "label": "Discount rate",
            "default": 0.08,
            "min": 0,
            "max": 0.5,
            "step": 0.01,
            "unit": "fraction"
          }
        ],
        "jsonDataFields": [
          {
            "name": "finance_evidence",
            "label": "Proposer evidence",
            "default": {
              "type": "array",
              "items": 2,
              "itemTypes": [
                "object"
              ],
              "sha256": "1fe862a73f360a5e2fba728e50ed9174d7a59d859b73173157cb9f616bbb55dd"
            }
          },
          {
            "name": "storage_evidence",
            "label": "Challenger evidence",
            "default": {
              "type": "array",
              "items": 2,
              "itemTypes": [
                "object"
              ],
              "sha256": "271546ffb361280af9676e04b76fcbc558b5b548c24ce6a3f8e9df2683de3437"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "b4d3ef0a82d47eb5426bbea1e0f487fc37b60f67eeafbacc7463f8e1c0a947a7",
        "evidenceSha256": "773495176ce865887aa32a44db0c1d68524c8265fc152d980b8174c257354866",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"council-debate\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"council-debate\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "The annual-value topic remains disputed; at zero discount the hand NPV is 230,000 x 15 - 1,200,000 EUR.",
          "testIds": [
            {
              "node_id": "tests/test_workflow_studies.py::test_council_debate_keeps_conflicting_assertions_and_disputes",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "capex_eur",
              "unit": "EUR",
              "minimum": 0,
              "maximum": 1000000000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "annual_net_value_eur",
              "unit": "EUR/year",
              "minimum": -1000000000,
              "maximum": 1000000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "life_years",
              "unit": "year",
              "minimum": 1,
              "maximum": 60,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "discount_rate_fraction",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 0.5,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "finance_evidence",
              "label": "Proposer evidence",
              "default": {
                "type": "array",
                "items": 2,
                "itemTypes": [
                  "object"
                ],
                "sha256": "1fe862a73f360a5e2fba728e50ed9174d7a59d859b73173157cb9f616bbb55dd"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "storage_evidence",
              "label": "Challenger evidence",
              "default": {
                "type": "array",
                "items": 2,
                "itemTypes": [
                  "object"
                ],
                "sha256": "271546ffb361280af9676e04b76fcbc558b5b548c24ce6a3f8e9df2683de3437"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/workflow_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "b4d3ef0a82d47eb5426bbea1e0f487fc37b60f67eeafbacc7463f8e1c0a947a7",
          "evidenceSha256": "773495176ce865887aa32a44db0c1d68524c8265fc152d980b8174c257354866",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"council-debate\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"council-debate\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "The annual-value topic remains disputed; at zero discount the hand NPV is 230,000 x 15 - 1,200,000 EUR.",
            "testIds": [
              {
                "node_id": "tests/test_workflow_studies.py::test_council_debate_keeps_conflicting_assertions_and_disputes",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "capex_eur below declared minimum 0",
            "capex_eur above declared maximum 1000000000000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "capex_eur=0 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Illustrative project NPV",
                  "value": 1968680.0982230667,
                  "unit": "EUR"
                },
                {
                  "label": "Evidence rounds",
                  "value": 2,
                  "unit": "rounds"
                },
                {
                  "label": "Unresolved or disputed topics",
                  "value": 2,
                  "unit": "topics"
                },
                {
                  "label": "Debate mode",
                  "value": "deterministic Python",
                  "unit": "mode"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {
            "dispute_status_persists": true
          },
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "d35bfe658e226479ac7f9a44f5786d5a5ae2b69970ab4429906177c36fe61d6e"
          }
        },
        "limitations": [
          "Rounds are deterministic comparisons of supplied assertions; no LLM or external agent is called.",
          "Conflicts remain disputed; the NPV screen is not approval or a dispatch-finance bridge."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/workflow_studies.py",
          "tests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "council-debate",
            "workspace": "council",
            "title": "Finance versus storage debate",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_workflow_studies.py::test_council_debate_keeps_conflicting_assertions_and_disputes",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "The annual-value topic remains disputed; at zero discount the hand NPV is 230,000 x 15 - 1,200,000 EUR.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Illustrative project NPV",
                        "value": 768680.0982230667,
                        "unit": "EUR"
                      },
                      {
                        "label": "Evidence rounds",
                        "value": 2,
                        "unit": "rounds"
                      },
                      {
                        "label": "Unresolved or disputed topics",
                        "value": 2,
                        "unit": "topics"
                      },
                      {
                        "label": "Debate mode",
                        "value": "deterministic Python",
                        "unit": "mode"
                      }
                    ],
                    "scalar_details": {
                      "npv_eur": 768680.0982230667,
                      "present_value_annual_value_eur": 1968680.0982230667,
                      "dispute_status_persists": true,
                      "proposer_evidence_count": 2,
                      "challenger_evidence_count": 2
                    },
                    "table_row_count": 2,
                    "result_sha256": "9a7cdf8ac1ad1b951ea56f316f19db3f8909cb24d0bccae031a307ff397d802a"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Illustrative project NPV",
                        "value": 2250000,
                        "unit": "EUR"
                      },
                      {
                        "label": "Evidence rounds",
                        "value": 2,
                        "unit": "rounds"
                      },
                      {
                        "label": "Unresolved or disputed topics",
                        "value": 2,
                        "unit": "topics"
                      },
                      {
                        "label": "Debate mode",
                        "value": "deterministic Python",
                        "unit": "mode"
                      }
                    ],
                    "scalar_details": {
                      "npv_eur": 2250000,
                      "present_value_annual_value_eur": 3450000,
                      "dispute_status_persists": true,
                      "proposer_evidence_count": 2,
                      "challenger_evidence_count": 2
                    },
                    "table_row_count": 2,
                    "result_sha256": "0b7a414f29a60df816c8aef19ca9f5e49045607ed2fd60722faf09ec67a715fd"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Illustrative project NPV",
                    "value": 768680.0982230667,
                    "unit": "EUR"
                  },
                  {
                    "label": "Evidence rounds",
                    "value": 2,
                    "unit": "rounds"
                  },
                  {
                    "label": "Unresolved or disputed topics",
                    "value": 2,
                    "unit": "topics"
                  },
                  {
                    "label": "Debate mode",
                    "value": "deterministic Python",
                    "unit": "mode"
                  }
                ],
                "default_result_sha256": "d35bfe658e226479ac7f9a44f5786d5a5ae2b69970ab4429906177c36fe61d6e"
              },
              "units": "EUR / rounds / topics",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 4
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "capex_eur=0 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Illustrative project NPV",
                    "value": 1968680.0982230667,
                    "unit": "EUR"
                  },
                  {
                    "label": "Evidence rounds",
                    "value": 2,
                    "unit": "rounds"
                  },
                  {
                    "label": "Unresolved or disputed topics",
                    "value": 2,
                    "unit": "topics"
                  },
                  {
                    "label": "Debate mode",
                    "value": "deterministic Python",
                    "unit": "mode"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "d35bfe658e226479ac7f9a44f5786d5a5ae2b69970ab4429906177c36fe61d6e"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/workflow_studies.py",
              "engine_sha256": "021597dd1d8b016f15bffa8494fb23a553fb93b26f9ae48c7a17061ec19ca585",
              "test_file": "apps/api/tests/test_workflow_studies.py",
              "test_sha256": "5649c323bcfa2ade89e03a7c12417835938366fbc0444f5b4b2954d65eee0221",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Rounds are deterministic comparisons of supplied assertions; no LLM or external agent is called.",
              "Conflicts remain disputed; the NPV screen is not approval or a dispatch-finance bridge.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-council-debate"
    },
    {
      "id": "council-evidence",
      "title": "Structured evidence manifest",
      "summary": "Check supplied tariff, warranty, and single-line JSON metadata for units, age, expiry, bounds, and provenance.",
      "workspace": {
        "id": "council",
        "name": "Agent council",
        "appRoute": "#council"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "council",
        "equations": [
          "age = as-of date - issue date",
          "expiry finding when expiry date < as-of date",
          "field check = expected field name and unit plus configured numeric bound"
        ],
        "limitations": [
          "Structured JSON metadata only; no OCR, retrieval, signature validation, or authenticity verification.",
          "Provenance references are user-supplied text and are not independently checked."
        ],
        "sources": [
          {
            "title": "Pydantic JSON validation behavior",
            "url": "https://docs.pydantic.dev/latest/concepts/json/"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "maximum_manifest_age_days",
            "label": "Maximum age",
            "default": 3650,
            "min": 0,
            "max": 36500,
            "step": 30,
            "unit": "days"
          },
          {
            "name": "maximum_tariff_eur_mwh",
            "label": "Tariff upper bound",
            "default": 2000,
            "min": 0.001,
            "max": 100000,
            "step": 10,
            "unit": "EUR/MWh"
          },
          {
            "name": "maximum_warranty_years",
            "label": "Warranty upper bound",
            "default": 50,
            "min": 0.001,
            "max": 100,
            "step": 1,
            "unit": "years"
          },
          {
            "name": "maximum_single_line_nodes",
            "label": "Node-count upper bound",
            "default": 10000,
            "min": 1,
            "max": 1000000,
            "step": 100,
            "unit": "nodes"
          }
        ],
        "jsonDataFields": [
          {
            "name": "manifest",
            "label": "Structured document manifest",
            "default": {
              "type": "object",
              "keys": [
                "as_of_date",
                "documents"
              ],
              "sha256": "13fab004e43f65a29ea4769e6cee62b6b8e59d59c87415ebda96bb5142ff2755"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "e181e55e9cb797e400b89ef29b9add70c3536e1bb1ad75327e280c26de8ae2b9",
        "evidenceSha256": "ca55f0d38e64683c1c029844bea50202dbfdd92d5548850ba884fa82cec7f598",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"council-evidence\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"council-evidence\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "A wrong-unit, expired document with missing issuer/source is reported with all three distinct issues.",
          "testIds": [
            {
              "node_id": "tests/test_workflow_studies.py::test_manifest_checks_expiry_units_bounds_and_supplied_provenance",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "maximum_manifest_age_days",
              "unit": "days",
              "minimum": 0,
              "maximum": 36500,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "maximum_tariff_eur_mwh",
              "unit": "EUR/MWh",
              "minimum": 0.001,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "maximum_warranty_years",
              "unit": "years",
              "minimum": 0.001,
              "maximum": 100,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "maximum_single_line_nodes",
              "unit": "nodes",
              "minimum": 1,
              "maximum": 1000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "manifest",
              "label": "Structured document manifest",
              "default": {
                "type": "object",
                "keys": [
                  "as_of_date",
                  "documents"
                ],
                "sha256": "13fab004e43f65a29ea4769e6cee62b6b8e59d59c87415ebda96bb5142ff2755"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/workflow_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "e181e55e9cb797e400b89ef29b9add70c3536e1bb1ad75327e280c26de8ae2b9",
          "evidenceSha256": "ca55f0d38e64683c1c029844bea50202dbfdd92d5548850ba884fa82cec7f598",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"council-evidence\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"council-evidence\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "A wrong-unit, expired document with missing issuer/source is reported with all three distinct issues.",
            "testIds": [
              {
                "node_id": "tests/test_workflow_studies.py::test_manifest_checks_expiry_units_bounds_and_supplied_provenance",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "maximum_manifest_age_days below declared minimum 0",
            "maximum_manifest_age_days above declared maximum 36500",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "maximum_manifest_age_days=0 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Documents inspected",
                  "value": 3,
                  "unit": "documents"
                },
                {
                  "label": "Records with findings",
                  "value": 3,
                  "unit": "documents"
                },
                {
                  "label": "Records with supplied provenance",
                  "value": 3,
                  "unit": "documents"
                },
                {
                  "label": "Input mode",
                  "value": "structured JSON manifest",
                  "unit": "mode"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "a7fa9ec14c49932e859e6dc65d9d51734251cd7f35d757130dbd6424715087ca"
          }
        },
        "limitations": [
          "Structured JSON metadata only; no OCR, retrieval, signature validation, or authenticity verification.",
          "Provenance references are user-supplied text and are not independently checked."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/workflow_studies.py",
          "tests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "council-evidence",
            "workspace": "council",
            "title": "Structured evidence manifest",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_workflow_studies.py::test_manifest_checks_expiry_units_bounds_and_supplied_provenance",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "A wrong-unit, expired document with missing issuer/source is reported with all three distinct issues.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Documents inspected",
                        "value": 1,
                        "unit": "documents"
                      },
                      {
                        "label": "Records with findings",
                        "value": 1,
                        "unit": "documents"
                      },
                      {
                        "label": "Records with supplied provenance",
                        "value": 0,
                        "unit": "documents"
                      },
                      {
                        "label": "Input mode",
                        "value": "structured JSON manifest",
                        "unit": "mode"
                      }
                    ],
                    "scalar_details": {
                      "as_of_date": "2026-10-17",
                      "authenticity_checked": false,
                      "ocr_used": false
                    },
                    "table_row_count": 1,
                    "result_sha256": "6f3cd4298d0d9a6cd75806a508da3b745dcc406e84e6b7849b3ecfbbb3481993"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Documents inspected",
                    "value": 3,
                    "unit": "documents"
                  },
                  {
                    "label": "Records with findings",
                    "value": 0,
                    "unit": "documents"
                  },
                  {
                    "label": "Records with supplied provenance",
                    "value": 3,
                    "unit": "documents"
                  },
                  {
                    "label": "Input mode",
                    "value": "structured JSON manifest",
                    "unit": "mode"
                  }
                ],
                "default_result_sha256": "a7fa9ec14c49932e859e6dc65d9d51734251cd7f35d757130dbd6424715087ca"
              },
              "units": "documents / issue flags",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 4
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "maximum_manifest_age_days=0 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Documents inspected",
                    "value": 3,
                    "unit": "documents"
                  },
                  {
                    "label": "Records with findings",
                    "value": 3,
                    "unit": "documents"
                  },
                  {
                    "label": "Records with supplied provenance",
                    "value": 3,
                    "unit": "documents"
                  },
                  {
                    "label": "Input mode",
                    "value": "structured JSON manifest",
                    "unit": "mode"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "a7fa9ec14c49932e859e6dc65d9d51734251cd7f35d757130dbd6424715087ca"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/workflow_studies.py",
              "engine_sha256": "021597dd1d8b016f15bffa8494fb23a553fb93b26f9ae48c7a17061ec19ca585",
              "test_file": "apps/api/tests/test_workflow_studies.py",
              "test_sha256": "5649c323bcfa2ade89e03a7c12417835938366fbc0444f5b4b2954d65eee0221",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Structured JSON metadata only; no OCR, retrieval, signature validation, or authenticity verification.",
              "Provenance references are user-supplied text and are not independently checked.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-council-evidence"
    },
    {
      "id": "council-consensus",
      "title": "Bayesian evidence toy model",
      "summary": "Update a binary prior from explicit likelihood ratios and show normalized probability and Shannon entropy.",
      "workspace": {
        "id": "council",
        "name": "Agent council",
        "appRoute": "#council"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "council",
        "equations": [
          "posterior odds = prior odds * product(likelihood ratios)",
          "posterior probability = odds / (1 + odds)",
          "binary Shannon entropy = -p log2(p) - (1-p) log2(1-p)"
        ],
        "limitations": [
          "Likelihood ratios are hypothetical; raw product assumes conditional independence.",
          "Repeated dependency groups are flagged, not deduplicated; no unanimity or approval is inferred."
        ],
        "sources": [
          {
            "title": "NIST Bayesian reliability evaluation",
            "url": "https://www.itl.nist.gov/div898/handbook/apr/section2/apr1a.htm"
          },
          {
            "title": "Claude Shannon, A Mathematical Theory of Communication",
            "url": "https://www.princeton.edu/~wbialek/rome/refs/shannon_48.pdf"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "prior_probability",
            "label": "Prior probability",
            "default": 0.35,
            "min": 0,
            "max": 1,
            "step": 0.01,
            "unit": "probability"
          },
          {
            "name": "maximum_absolute_log_likelihood_ratio",
            "label": "Per-item log-LR cap",
            "default": 4,
            "min": 0,
            "max": 20,
            "step": 0.25,
            "unit": "log ratio"
          },
          {
            "name": "evidence_weight",
            "label": "Illustrative evidence weight",
            "default": 1,
            "min": 0,
            "max": 1,
            "step": 0.05,
            "unit": "fraction"
          }
        ],
        "jsonDataFields": [
          {
            "name": "evidence",
            "label": "Likelihood ratios and dependency labels",
            "default": {
              "type": "array",
              "items": 3,
              "itemTypes": [
                "object"
              ],
              "sha256": "9d07bef887eaa2282ddf2d9637913f31e6032f6a06d5349dbc1841ee9c7c3bca"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "d96dd3dcd89a139aefc25cc6f7c6ac371c8e5f2e1eecc9b95615e4b45e907ed9",
        "evidenceSha256": "142ae1b303c71245e60acbc32b5e97e7dc674256c5b83fa4d0eaafb3db42a77a",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"council-consensus\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"council-consensus\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Prior 0.5 and likelihood ratio 2 yield posterior 2/3 and binary entropy; prior zero stays zero.",
          "testIds": [
            {
              "node_id": "tests/test_workflow_studies.py::test_bayesian_probability_normalization_entropy_and_boundary_cases",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "prior_probability",
              "unit": "probability",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "maximum_absolute_log_likelihood_ratio",
              "unit": "log ratio",
              "minimum": 0,
              "maximum": 20,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "evidence_weight",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "evidence",
              "label": "Likelihood ratios and dependency labels",
              "default": {
                "type": "array",
                "items": 3,
                "itemTypes": [
                  "object"
                ],
                "sha256": "9d07bef887eaa2282ddf2d9637913f31e6032f6a06d5349dbc1841ee9c7c3bca"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/workflow_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "d96dd3dcd89a139aefc25cc6f7c6ac371c8e5f2e1eecc9b95615e4b45e907ed9",
          "evidenceSha256": "142ae1b303c71245e60acbc32b5e97e7dc674256c5b83fa4d0eaafb3db42a77a",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"council-consensus\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"council-consensus\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Prior 0.5 and likelihood ratio 2 yield posterior 2/3 and binary entropy; prior zero stays zero.",
            "testIds": [
              {
                "node_id": "tests/test_workflow_studies.py::test_bayesian_probability_normalization_entropy_and_boundary_cases",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "prior_probability below declared minimum 0",
            "prior_probability above declared maximum 1",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "prior_probability=0 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Prior hypothesis probability",
                  "value": 0,
                  "unit": "probability"
                },
                {
                  "label": "Posterior hypothesis probability",
                  "value": 0,
                  "unit": "probability"
                },
                {
                  "label": "Complement probability",
                  "value": 1,
                  "unit": "probability"
                },
                {
                  "label": "Posterior Shannon entropy",
                  "value": 0,
                  "unit": "bits"
                },
                {
                  "label": "Raw independent-product posterior",
                  "value": 0,
                  "unit": "probability"
                },
                {
                  "label": "Evidence items",
                  "value": 3,
                  "unit": "items"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "466ee94b0da07475843f30bf0cf207589302fe00ceb1adab80d9c0ec2ad2a407"
          }
        },
        "limitations": [
          "Likelihood ratios are hypothetical; raw product assumes conditional independence.",
          "Repeated dependency groups are flagged, not deduplicated; no unanimity or approval is inferred."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/workflow_studies.py",
          "tests": [
            "apps/api/tests/test_workflow_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "council-consensus",
            "workspace": "council",
            "title": "Bayesian evidence toy model",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_workflow_studies.py::test_bayesian_probability_normalization_entropy_and_boundary_cases",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Prior 0.5 and likelihood ratio 2 yield posterior 2/3 and binary entropy; prior zero stays zero.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Prior hypothesis probability",
                        "value": 0.5,
                        "unit": "probability"
                      },
                      {
                        "label": "Posterior hypothesis probability",
                        "value": 0.6666666666666666,
                        "unit": "probability"
                      },
                      {
                        "label": "Complement probability",
                        "value": 0.33333333333333337,
                        "unit": "probability"
                      },
                      {
                        "label": "Posterior Shannon entropy",
                        "value": 0.9182958340544896,
                        "unit": "bits"
                      },
                      {
                        "label": "Raw independent-product posterior",
                        "value": 0.6666666666666666,
                        "unit": "probability"
                      },
                      {
                        "label": "Evidence items",
                        "value": 1,
                        "unit": "items"
                      }
                    ],
                    "scalar_details": {
                      "hypothesis_probability": 0.6666666666666666,
                      "complement_probability": 0.33333333333333337,
                      "probability_sum": 1,
                      "posterior_log_odds": 0.6931471805599453,
                      "raw_unclipped_log_odds": 0.6931471805599453,
                      "raw_independent_product_probability": 0.6666666666666666,
                      "binary_shannon_entropy_bits": 0.9182958340544896,
                      "log_lr_cap": 4,
                      "evidence_weight": 1
                    },
                    "table_row_count": 1,
                    "result_sha256": "9eba89ce0d853569ff9fa31002157d5d0994bb4b034c95584a2b448dad079a9c"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Prior hypothesis probability",
                        "value": 0,
                        "unit": "probability"
                      },
                      {
                        "label": "Posterior hypothesis probability",
                        "value": 0,
                        "unit": "probability"
                      },
                      {
                        "label": "Complement probability",
                        "value": 1,
                        "unit": "probability"
                      },
                      {
                        "label": "Posterior Shannon entropy",
                        "value": 0,
                        "unit": "bits"
                      },
                      {
                        "label": "Raw independent-product posterior",
                        "value": 0,
                        "unit": "probability"
                      },
                      {
                        "label": "Evidence items",
                        "value": 1,
                        "unit": "items"
                      }
                    ],
                    "scalar_details": {
                      "hypothesis_probability": 0,
                      "complement_probability": 1,
                      "probability_sum": 1,
                      "posterior_log_odds": null,
                      "raw_unclipped_log_odds": null,
                      "raw_independent_product_probability": 0,
                      "binary_shannon_entropy_bits": 0,
                      "log_lr_cap": 4,
                      "evidence_weight": 1
                    },
                    "table_row_count": 1,
                    "result_sha256": "6498e6d77cb15a12e4924fd6ab5237c5e0f8851639080b63c6859bee6bf7d5f4"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Prior hypothesis probability",
                    "value": 0.35,
                    "unit": "probability"
                  },
                  {
                    "label": "Posterior hypothesis probability",
                    "value": 0.5757180156657963,
                    "unit": "probability"
                  },
                  {
                    "label": "Complement probability",
                    "value": 0.4242819843342037,
                    "unit": "probability"
                  },
                  {
                    "label": "Posterior Shannon entropy",
                    "value": 0.9833936146096738,
                    "unit": "bits"
                  },
                  {
                    "label": "Raw independent-product posterior",
                    "value": 0.5757180156657963,
                    "unit": "probability"
                  },
                  {
                    "label": "Evidence items",
                    "value": 3,
                    "unit": "items"
                  }
                ],
                "default_result_sha256": "466ee94b0da07475843f30bf0cf207589302fe00ceb1adab80d9c0ec2ad2a407"
              },
              "units": "probability / bits",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 6
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "prior_probability=0 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Prior hypothesis probability",
                    "value": 0,
                    "unit": "probability"
                  },
                  {
                    "label": "Posterior hypothesis probability",
                    "value": 0,
                    "unit": "probability"
                  },
                  {
                    "label": "Complement probability",
                    "value": 1,
                    "unit": "probability"
                  },
                  {
                    "label": "Posterior Shannon entropy",
                    "value": 0,
                    "unit": "bits"
                  },
                  {
                    "label": "Raw independent-product posterior",
                    "value": 0,
                    "unit": "probability"
                  },
                  {
                    "label": "Evidence items",
                    "value": 3,
                    "unit": "items"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "466ee94b0da07475843f30bf0cf207589302fe00ceb1adab80d9c0ec2ad2a407"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/workflow_studies.py",
              "engine_sha256": "021597dd1d8b016f15bffa8494fb23a553fb93b26f9ae48c7a17061ec19ca585",
              "test_file": "apps/api/tests/test_workflow_studies.py",
              "test_sha256": "5649c323bcfa2ade89e03a7c12417835938366fbc0444f5b4b2954d65eee0221",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Likelihood ratios are hypothetical; raw product assumes conditional independence.",
              "Repeated dependency groups are flagged, not deduplicated; no unanimity or approval is inferred.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-council-consensus"
    },
    {
      "id": "storage-annual",
      "title": "8760-hour dispatch and tariff builder",
      "summary": "A complete seasonal year or supplied hourly series, with a transparent tariff policy and monthly billing.",
      "workspace": {
        "id": "storage",
        "name": "Storage & flexibility",
        "appRoute": "#storage"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "annual",
        "equations": [
          "SOC[t+1] = SOC[t] + sqrt(efficiency)*charge - discharge/sqrt(efficiency)",
          "Monthly demand cost = sum(monthly maximum import kW * monthly tariff)",
          "Both initial and terminal SOC = 50%; usable range = 10–90%."
        ],
        "limitations": [
          "Explicit non-leap 8760-hour calendar; no DST. Synthetic seasonal weather is not site-calibrated.",
          "Transparent tariff-threshold policy, NOT the short-horizon MILP or a globally optimal annual solution.",
          "Unconstrained grid connection; no reserve services, outages, demand ratchets, taxes or fixed fees.",
          "No daily result is multiplied by 365. Annual totals sum every generated or supplied hour."
        ],
        "sources": [
          {
            "title": "NREL REopt documentation and model boundary",
            "url": "https://reopt.nrel.gov/tool"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "capacity_kwh",
            "label": "Battery energy",
            "default": 10000,
            "min": 1,
            "max": 1000000,
            "step": 100,
            "unit": "kWh"
          },
          {
            "name": "power_kw",
            "label": "Battery power",
            "default": 5000,
            "min": 1,
            "max": 1000000,
            "step": 100,
            "unit": "kW"
          },
          {
            "name": "pv_kwp",
            "label": "Synthetic PV capacity",
            "default": 5900,
            "min": 0,
            "max": 1000000,
            "step": 100,
            "unit": "kWp"
          },
          {
            "name": "base_load_kw",
            "label": "Synthetic base load",
            "default": 3000,
            "min": 0,
            "max": 1000000,
            "step": 100,
            "unit": "kW"
          },
          {
            "name": "efficiency",
            "label": "Round-trip efficiency",
            "default": 0.9,
            "min": 0.5,
            "max": 1,
            "step": 0.01,
            "unit": ""
          },
          {
            "name": "wear",
            "label": "Discharge wear",
            "default": 0.015,
            "min": 0,
            "max": 1,
            "step": 0.001,
            "unit": "EUR/kWh"
          },
          {
            "name": "monthly_peak",
            "label": "Monthly peak charge",
            "default": 0,
            "min": 0,
            "max": 100,
            "step": 0.1,
            "unit": "EUR/kW"
          },
          {
            "name": "export_price",
            "label": "Export tariff",
            "default": 0.04,
            "min": 0,
            "max": 3,
            "step": 0.01,
            "unit": "EUR/kWh"
          },
          {
            "name": "seed",
            "label": "Weather seed",
            "default": 42,
            "min": 0,
            "max": 4294967295,
            "step": 1,
            "unit": ""
          }
        ],
        "jsonDataFields": [
          {
            "name": "tariff",
            "label": "24 hourly import tariffs (EUR/kWh)",
            "default": {
              "type": "array",
              "items": 24,
              "itemTypes": [
                "number"
              ],
              "sha256": "e6f1e10aab1b4a79ab1c7096d8bbbb39a396e2e228db4141f7cd2b5ad4d0b7a1"
            }
          },
          {
            "name": "hourly_load_kw",
            "label": "Optional full-year load (8760 kW values)",
            "default": {
              "type": "array",
              "items": 0,
              "itemTypes": [],
              "sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945"
            }
          },
          {
            "name": "hourly_pv_kw",
            "label": "Optional full-year PV (8760 kW values)",
            "default": {
              "type": "array",
              "items": 0,
              "itemTypes": [],
              "sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "67dc3f746227f6ac9d591f65a760c0efc1cbf87e936f0b981ca74ee7e186fa93",
        "evidenceSha256": "4828edd3b44c5d6c78b33b4797c2be206b17b9863bd306511e97b51ae0cdb53d",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-annual\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-annual\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Every hourly bus/SOC ledger is reconstructed and monthly energy+peak costs independently sum to the annual metric.",
          "testIds": [
            {
              "node_id": "tests/test_toolkit_studies.py::test_annual_nonnegative_flows_losses_limits_and_independent_cost_ledger",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "capacity_kwh",
              "unit": "kWh",
              "minimum": 1,
              "maximum": 1000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "power_kw",
              "unit": "kW",
              "minimum": 1,
              "maximum": 1000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "pv_kwp",
              "unit": "kWp",
              "minimum": 0,
              "maximum": 1000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "base_load_kw",
              "unit": "kW",
              "minimum": 0,
              "maximum": 1000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "efficiency",
              "unit": null,
              "minimum": 0.5,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "wear",
              "unit": "EUR/kWh",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "monthly_peak",
              "unit": "EUR/kW",
              "minimum": 0,
              "maximum": 100,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "export_price",
              "unit": "EUR/kWh",
              "minimum": 0,
              "maximum": 3,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "seed",
              "unit": null,
              "minimum": 0,
              "maximum": 4294967295,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "tariff",
              "label": "24 hourly import tariffs (EUR/kWh)",
              "default": {
                "type": "array",
                "items": 24,
                "itemTypes": [
                  "number"
                ],
                "sha256": "e6f1e10aab1b4a79ab1c7096d8bbbb39a396e2e228db4141f7cd2b5ad4d0b7a1"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "hourly_load_kw",
              "label": "Optional full-year load (8760 kW values)",
              "default": {
                "type": "array",
                "items": 0,
                "itemTypes": [],
                "sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "hourly_pv_kw",
              "label": "Optional full-year PV (8760 kW values)",
              "default": {
                "type": "array",
                "items": 0,
                "itemTypes": [],
                "sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/toolkit_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "67dc3f746227f6ac9d591f65a760c0efc1cbf87e936f0b981ca74ee7e186fa93",
          "evidenceSha256": "4828edd3b44c5d6c78b33b4797c2be206b17b9863bd306511e97b51ae0cdb53d",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-annual\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-annual\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Every hourly bus/SOC ledger is reconstructed and monthly energy+peak costs independently sum to the annual metric.",
            "testIds": [
              {
                "node_id": "tests/test_toolkit_studies.py::test_annual_nonnegative_flows_losses_limits_and_independent_cost_ledger",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "capacity_kwh below declared minimum 1",
            "capacity_kwh above declared maximum 1000000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_toolkit_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "capacity_kwh=1 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Annual policy cost",
                  "value": 3368298.742696335,
                  "unit": "EUR"
                },
                {
                  "label": "No-storage cost",
                  "value": 3368345.4208660657,
                  "unit": "EUR"
                },
                {
                  "label": "Policy savings (can be negative)",
                  "value": 46.67816973058507,
                  "unit": "EUR"
                },
                {
                  "label": "Terminal battery energy",
                  "value": 0.5,
                  "unit": "kWh"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [
            "seed"
          ],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "935c39bd455dc5fab3dabd64facceab79b3efbc43a603f12ff3481d135d7694b"
          }
        },
        "limitations": [
          "Explicit non-leap 8760-hour calendar; no DST. Synthetic seasonal weather is not site-calibrated.",
          "Transparent tariff-threshold policy, NOT the short-horizon MILP or a globally optimal annual solution.",
          "Unconstrained grid connection; no reserve services, outages, demand ratchets, taxes or fixed fees.",
          "No daily result is multiplied by 365. Annual totals sum every generated or supplied hour."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/toolkit_studies.py",
          "tests": [
            "apps/api/tests/test_toolkit_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "storage-annual",
            "workspace": "storage",
            "title": "8760-hour dispatch and tariff builder",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_toolkit_studies.py::test_annual_nonnegative_flows_losses_limits_and_independent_cost_ledger",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Every hourly bus/SOC ledger is reconstructed and monthly energy+peak costs independently sum to the annual metric.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Annual policy cost",
                        "value": 3067804.6904449174,
                        "unit": "EUR"
                      },
                      {
                        "label": "No-storage cost",
                        "value": 3368345.4208660657,
                        "unit": "EUR"
                      },
                      {
                        "label": "Policy savings (can be negative)",
                        "value": 300540.73042114824,
                        "unit": "EUR"
                      },
                      {
                        "label": "Terminal battery energy",
                        "value": 5000,
                        "unit": "kWh"
                      }
                    ],
                    "scalar_details": {
                      "initial_soc_kwh": 5000,
                      "terminal_soc_kwh": 5000,
                      "annual_grid_model": "unconstrained"
                    },
                    "table_row_count": 8760,
                    "result_sha256": "1a5cebabb5f1c1a1e39429d6eeaa8a09e9a71e1c3dc7b00260aacd105264aebd"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Annual policy cost",
                        "value": 3354282.0030700457,
                        "unit": "EUR"
                      },
                      {
                        "label": "No-storage cost",
                        "value": 3521016.81056296,
                        "unit": "EUR"
                      },
                      {
                        "label": "Policy savings (can be negative)",
                        "value": 166734.80749291414,
                        "unit": "EUR"
                      },
                      {
                        "label": "Terminal battery energy",
                        "value": 5000,
                        "unit": "kWh"
                      }
                    ],
                    "scalar_details": {
                      "initial_soc_kwh": 5000,
                      "terminal_soc_kwh": 5000,
                      "annual_grid_model": "unconstrained"
                    },
                    "table_row_count": 8760,
                    "result_sha256": "052b6d7c6be6a7f704c6e2ee73cb90380cde51a866996acf95845e821a9df3ce"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Annual policy cost",
                        "value": 3368259.5053527146,
                        "unit": "EUR"
                      },
                      {
                        "label": "No-storage cost",
                        "value": 3368345.4208660657,
                        "unit": "EUR"
                      },
                      {
                        "label": "Policy savings (can be negative)",
                        "value": 85.91551335109398,
                        "unit": "EUR"
                      },
                      {
                        "label": "Terminal battery energy",
                        "value": 61.728,
                        "unit": "kWh"
                      }
                    ],
                    "scalar_details": {
                      "initial_soc_kwh": 61.728,
                      "terminal_soc_kwh": 61.728,
                      "annual_grid_model": "unconstrained"
                    },
                    "table_row_count": 8760,
                    "result_sha256": "b20ddbdbefb02dfd09084f8aa7ed3e40aae070149d47db7074b031e8194189ff"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Annual policy cost",
                    "value": 3067804.6904449174,
                    "unit": "EUR"
                  },
                  {
                    "label": "No-storage cost",
                    "value": 3368345.4208660657,
                    "unit": "EUR"
                  },
                  {
                    "label": "Policy savings (can be negative)",
                    "value": 300540.73042114824,
                    "unit": "EUR"
                  },
                  {
                    "label": "Terminal battery energy",
                    "value": 5000,
                    "unit": "kWh"
                  }
                ],
                "default_result_sha256": "935c39bd455dc5fab3dabd64facceab79b3efbc43a603f12ff3481d135d7694b"
              },
              "units": "kW / kWh / EUR",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 4
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "capacity_kwh=1 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Annual policy cost",
                    "value": 3368298.742696335,
                    "unit": "EUR"
                  },
                  {
                    "label": "No-storage cost",
                    "value": 3368345.4208660657,
                    "unit": "EUR"
                  },
                  {
                    "label": "Policy savings (can be negative)",
                    "value": 46.67816973058507,
                    "unit": "EUR"
                  },
                  {
                    "label": "Terminal battery energy",
                    "value": 0.5,
                    "unit": "kWh"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "935c39bd455dc5fab3dabd64facceab79b3efbc43a603f12ff3481d135d7694b"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/toolkit_studies.py",
              "engine_sha256": "9da4e28db6bdc4b14bbdb3da9482d6167d696a808d7caa6f3f2c8b44e51f0136",
              "test_file": "apps/api/tests/test_toolkit_studies.py",
              "test_sha256": "ebb884d97685468ac53b1545b6ca8b5a56f37ba8d69af1dce62835a97e15ad12",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Explicit non-leap 8760-hour calendar; no DST. Synthetic seasonal weather is not site-calibrated.",
              "Transparent tariff-threshold policy, NOT the short-horizon MILP or a globally optimal annual solution.",
              "Unconstrained grid connection; no reserve services, outages, demand ratchets, taxes or fixed fees.",
              "No daily result is multiplied by 365. Annual totals sum every generated or supplied hour.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-storage-annual"
    },
    {
      "id": "pv-sensitivity",
      "title": "LCOE contour sensitivity",
      "summary": "A 9 × 9 CAPEX and yield surface evaluated by the existing discounted PV engine.",
      "workspace": {
        "id": "pv",
        "name": "PV economics",
        "appRoute": "#pv"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "heatmap",
        "equations": [
          "LCOE = discounted lifecycle costs / discounted lifetime MWh"
        ],
        "limitations": [
          "Constant money; the parent PV engine retains its 30-year life and year-15 replacement assumptions."
        ],
        "sources": [
          {
            "title": "NREL simple LCOE documentation",
            "url": "https://www.nrel.gov/analysis/tech-lcoe-documentation.html"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "capex_eur_kwp",
            "label": "Central CAPEX",
            "default": 650,
            "min": 100,
            "max": 5000,
            "step": 10,
            "unit": "EUR/kWp"
          },
          {
            "name": "yield_kwh_kwp",
            "label": "Central yield",
            "default": 1350,
            "min": 400,
            "max": 2200,
            "step": 10,
            "unit": "kWh/kWp"
          },
          {
            "name": "discount",
            "label": "Real discount rate",
            "default": 0.07,
            "min": 0,
            "max": 0.4,
            "step": 0.01,
            "unit": ""
          },
          {
            "name": "opex",
            "label": "Annual OPEX",
            "default": 12,
            "min": 0,
            "max": 100,
            "step": 1,
            "unit": "EUR/kWp"
          }
        ],
        "jsonDataFields": []
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "28d019d628356b52ba2e634b617fb4879f0531f42dd272f84373a245867026ed",
        "evidenceSha256": "14c9b5706212e0666e3175aaaf1754f8543b9d6bc156f62f08531372b868d934",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-sensitivity\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-sensitivity\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "LCOE increases with capex and decreases with yield for the same modeled assumptions.",
          "testIds": [
            {
              "node_id": "tests/test_toolkit_studies.py::test_lcoe_sensitivity_increases_with_capex_and_decreases_with_yield",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "capex_eur_kwp",
              "unit": "EUR/kWp",
              "minimum": 100,
              "maximum": 5000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "yield_kwh_kwp",
              "unit": "kWh/kWp",
              "minimum": 400,
              "maximum": 2200,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "discount",
              "unit": null,
              "minimum": 0,
              "maximum": 0.4,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "opex",
              "unit": "EUR/kWp",
              "minimum": 0,
              "maximum": 100,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [],
          "strictValidationReference": "apps/api/helioforge/engines/toolkit_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "28d019d628356b52ba2e634b617fb4879f0531f42dd272f84373a245867026ed",
          "evidenceSha256": "14c9b5706212e0666e3175aaaf1754f8543b9d6bc156f62f08531372b868d934",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-sensitivity\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-sensitivity\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "LCOE increases with capex and decreases with yield for the same modeled assumptions.",
            "testIds": [
              {
                "node_id": "tests/test_toolkit_studies.py::test_lcoe_sensitivity_increases_with_capex_and_decreases_with_yield",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "capex_eur_kwp below declared minimum 100",
            "capex_eur_kwp above declared maximum 5000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_toolkit_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "capex_eur_kwp=100 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Lowest LCOE",
                  "value": 12.749,
                  "unit": "EUR/MWh"
                },
                {
                  "label": "Highest LCOE",
                  "value": 21.718,
                  "unit": "EUR/MWh"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "8ba4fd9c1a072d0b781329912a24a86dbc9042c1dfbc7fca4412c9ec4d628457"
          }
        },
        "limitations": [
          "Constant money; the parent PV engine retains its 30-year life and year-15 replacement assumptions."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/toolkit_studies.py",
          "tests": [
            "apps/api/tests/test_toolkit_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "pv-sensitivity",
            "workspace": "pv",
            "title": "LCOE contour sensitivity",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_toolkit_studies.py::test_lcoe_sensitivity_increases_with_capex_and_decreases_with_yield",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "LCOE increases with capex and decreases with yield for the same modeled assumptions.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Lowest LCOE",
                        "value": 33.944,
                        "unit": "EUR/MWh"
                      },
                      {
                        "label": "Highest LCOE",
                        "value": 74.971,
                        "unit": "EUR/MWh"
                      }
                    ],
                    "scalar_details": {},
                    "table_row_count": 81,
                    "result_sha256": "84a781ab8c7856a61f69316a03fb0603b696ab3b62b061087de15c3d7baba15d"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Lowest LCOE",
                    "value": 33.944,
                    "unit": "EUR/MWh"
                  },
                  {
                    "label": "Highest LCOE",
                    "value": 74.971,
                    "unit": "EUR/MWh"
                  }
                ],
                "default_result_sha256": "8ba4fd9c1a072d0b781329912a24a86dbc9042c1dfbc7fca4412c9ec4d628457"
              },
              "units": "EUR/MWh",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 2
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "capex_eur_kwp=100 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Lowest LCOE",
                    "value": 12.749,
                    "unit": "EUR/MWh"
                  },
                  {
                    "label": "Highest LCOE",
                    "value": 21.718,
                    "unit": "EUR/MWh"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "8ba4fd9c1a072d0b781329912a24a86dbc9042c1dfbc7fca4412c9ec4d628457"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/toolkit_studies.py",
              "engine_sha256": "9da4e28db6bdc4b14bbdb3da9482d6167d696a808d7caa6f3f2c8b44e51f0136",
              "test_file": "apps/api/tests/test_toolkit_studies.py",
              "test_sha256": "ebb884d97685468ac53b1545b6ca8b5a56f37ba8d69af1dce62835a97e15ad12",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Constant money; the parent PV engine retains its 30-year life and year-15 replacement assumptions.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-pv-sensitivity"
    },
    {
      "id": "pv-reserve",
      "title": "Inverter repowering reserve",
      "summary": "Inflation-linked future replacement cost and equal end-of-year reserve contributions.",
      "workspace": {
        "id": "pv",
        "name": "PV economics",
        "appRoute": "#pv"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "waterfall",
        "equations": [
          "Future cost = present cost * (1+inflation)^years",
          "Annual deposit = future cost * return / ((1+return)^years - 1); zero return: cost/years"
        ],
        "limitations": [
          "Constant annual assumptions; end-of-year contributions, no tax, fees or investment-return guarantee.",
          "First-party algebraic convention; the source reference is not independent validation or a forecast of investment returns."
        ],
        "sources": [
          {
            "title": "First-party HelioForge reserve implementation and declared equations",
            "url": "https://github.com/mohammadrezwankhan/helioforge-energy-lab/blob/74d6730e38323b69c180d7f4b4945d11010203f3/apps/api/helioforge/engines/toolkit_studies.py"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "cost_eur",
            "label": "Replacement cost today",
            "default": 1750000,
            "min": 0,
            "max": 10000000000,
            "step": 1000,
            "unit": "EUR"
          },
          {
            "name": "inflation",
            "label": "Cost inflation",
            "default": 0.02,
            "min": 0,
            "max": 0.2,
            "step": 0.005,
            "unit": ""
          },
          {
            "name": "fund_return",
            "label": "Nominal fund return",
            "default": 0.03,
            "min": 0,
            "max": 0.2,
            "step": 0.005,
            "unit": ""
          },
          {
            "name": "years",
            "label": "Replacement year",
            "default": 15,
            "min": 1,
            "max": 50,
            "step": 1,
            "unit": ""
          }
        ],
        "jsonDataFields": []
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "fe97427ec80a38468af22b4f978a179a982260dbbdc7d78c41364df9974ad5fc",
        "evidenceSha256": "f828d0f7794f7d0657ef2f7c5feda826c6991edd0f19c63f43e68b24172e8dd1",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-reserve\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-reserve\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "A 1,000 EUR reserve spread evenly over 10 years at zero inflation and fund return requires 100 EUR/year.",
          "testIds": [
            {
              "node_id": "tests/test_toolkit_studies.py::test_reserve_zero_interest_hand_case",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "cost_eur",
              "unit": "EUR",
              "minimum": 0,
              "maximum": 10000000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "inflation",
              "unit": null,
              "minimum": 0,
              "maximum": 0.2,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "fund_return",
              "unit": null,
              "minimum": 0,
              "maximum": 0.2,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "years",
              "unit": null,
              "minimum": 1,
              "maximum": 50,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [],
          "strictValidationReference": "apps/api/helioforge/engines/toolkit_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "fe97427ec80a38468af22b4f978a179a982260dbbdc7d78c41364df9974ad5fc",
          "evidenceSha256": "f828d0f7794f7d0657ef2f7c5feda826c6991edd0f19c63f43e68b24172e8dd1",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-reserve\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-reserve\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "A 1,000 EUR reserve spread evenly over 10 years at zero inflation and fund return requires 100 EUR/year.",
            "testIds": [
              {
                "node_id": "tests/test_toolkit_studies.py::test_reserve_zero_interest_hand_case",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "cost_eur below declared minimum 0",
            "cost_eur above declared maximum 10000000000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_toolkit_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "cost_eur=0 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Inflated replacement cost",
                  "value": 0,
                  "unit": "EUR"
                },
                {
                  "label": "Annual contribution",
                  "value": 0,
                  "unit": "EUR"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "c04e93141c8cd3ab9163b5d13b79a47b1c0a432cfc147c99e17ddd5e36b6c658"
          }
        },
        "limitations": [
          "Constant annual assumptions; end-of-year contributions, no tax, fees or investment-return guarantee.",
          "First-party algebraic convention; the source reference is not independent validation or a forecast of investment returns."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/toolkit_studies.py",
          "tests": [
            "apps/api/tests/test_toolkit_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "pv-reserve",
            "workspace": "pv",
            "title": "Inverter repowering reserve",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_toolkit_studies.py::test_reserve_zero_interest_hand_case",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "A 1,000 EUR reserve spread evenly over 10 years at zero inflation and fund return requires 100 EUR/year.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Inflated replacement cost",
                        "value": 1000,
                        "unit": "EUR"
                      },
                      {
                        "label": "Annual contribution",
                        "value": 100,
                        "unit": "EUR"
                      }
                    ],
                    "scalar_details": {},
                    "table_row_count": 10,
                    "result_sha256": "1904add415d3b63121a86346666c2ee52c3e7570aa1ea87513117187e34665c0"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Inflated replacement cost",
                    "value": 2355269.5920672272,
                    "unit": "EUR"
                  },
                  {
                    "label": "Annual contribution",
                    "value": 126634.79203226288,
                    "unit": "EUR"
                  }
                ],
                "default_result_sha256": "c04e93141c8cd3ab9163b5d13b79a47b1c0a432cfc147c99e17ddd5e36b6c658"
              },
              "units": "EUR / year",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 2
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "cost_eur=0 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Inflated replacement cost",
                    "value": 0,
                    "unit": "EUR"
                  },
                  {
                    "label": "Annual contribution",
                    "value": 0,
                    "unit": "EUR"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "c04e93141c8cd3ab9163b5d13b79a47b1c0a432cfc147c99e17ddd5e36b6c658"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/toolkit_studies.py",
              "engine_sha256": "9da4e28db6bdc4b14bbdb3da9482d6167d696a808d7caa6f3f2c8b44e51f0136",
              "test_file": "apps/api/tests/test_toolkit_studies.py",
              "test_sha256": "ebb884d97685468ac53b1545b6ca8b5a56f37ba8d69af1dce62835a97e15ad12",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Constant annual assumptions; end-of-year contributions, no tax, fees or investment-return guarantee.",
              "First-party algebraic convention; the source reference is not independent validation or a forecast of investment returns.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-pv-reserve"
    },
    {
      "id": "hybrid-powerflow",
      "title": "Balanced AC nodal power flow",
      "summary": "Solve a bounded balanced PQ/slack network and inspect actual voltage phasors, branch complex powers, losses and ratings.",
      "workspace": {
        "id": "hybrid",
        "name": "Hybrid systems lab",
        "appRoute": "#hybrid"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "network",
        "equations": [
          "I = Ybus·V; Sbus = V·conj(I)",
          "PQ residuals are calculated P/Q plus specified positive loads; slack voltage magnitude and angle are fixed.",
          "Branch-end S = V·conj(Ibranch); real branch loss = Pfrom + Pto."
        ],
        "limitations": [
          "Balanced positive-sequence per-unit educational model; one slack bus and constant-power PQ loads only.",
          "No generator PV/Q limits, transformers, controls, protection, unbalanced phases or operational dispatch.",
          "Non-converged or non-finite solutions fail closed. Overloaded branch ratings are reported, not controlled."
        ],
        "sources": [
          {
            "title": "MATPOWER Newton power flow documentation",
            "url": "https://matpower.org/doc/ref-manual/legacy/functions/newtonpf.html"
          },
          {
            "title": "MATPOWER AC power flow reference manual",
            "url": "https://matpower.org/docs/manual.pdf"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "base_mva",
            "label": "System base",
            "default": 100,
            "min": 1,
            "max": 1000000,
            "step": 1,
            "unit": "MVA"
          },
          {
            "name": "tolerance_pu",
            "label": "Power mismatch tolerance",
            "default": 1e-9,
            "min": 1e-12,
            "max": 0.0001,
            "step": 1e-9,
            "unit": "p.u."
          },
          {
            "name": "max_iterations",
            "label": "Maximum solver iterations",
            "default": 40,
            "min": 2,
            "max": 200,
            "step": 1,
            "unit": "iterations"
          }
        ],
        "jsonDataFields": [
          {
            "name": "buses",
            "label": "Balanced slack and PQ buses",
            "default": {
              "type": "array",
              "items": 3,
              "itemTypes": [
                "object"
              ],
              "sha256": "a7a96238b8d127c619657a0d5d5369ccc06d7163a1cba24dcc27df2e9fd197de"
            }
          },
          {
            "name": "branches",
            "label": "Series branches and MVA limits",
            "default": {
              "type": "array",
              "items": 3,
              "itemTypes": [
                "object"
              ],
              "sha256": "e8cfa4e15e2be03e4304365aff45787fdf3c00e9b6be91340725ac13894f584c"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "75bd8bd8e2d3b29dcaa90947f359e43f163b85b6d115537ab52c2eb828b88e3e",
        "evidenceSha256": "1d9b3567ad8f35285ba73e25ef99164b960ef26f48e70b980a4018461aedc78c",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-powerflow\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-powerflow\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "For 0.1 pu resistance and 50 MW load on 100 MVA base, load voltage is (1+sqrt(0.8))/2 pu and branch I²R loss closes.",
          "testIds": [
            {
              "node_id": "tests/test_physical_studies.py::test_balanced_two_bus_powerflow_matches_resistive_hand_solution_and_branch_loss",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "base_mva",
              "unit": "MVA",
              "minimum": 1,
              "maximum": 1000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "tolerance_pu",
              "unit": "p.u.",
              "minimum": 1e-12,
              "maximum": 0.0001,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "max_iterations",
              "unit": "iterations",
              "minimum": 2,
              "maximum": 200,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "buses",
              "label": "Balanced slack and PQ buses",
              "default": {
                "type": "array",
                "items": 3,
                "itemTypes": [
                  "object"
                ],
                "sha256": "a7a96238b8d127c619657a0d5d5369ccc06d7163a1cba24dcc27df2e9fd197de"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "branches",
              "label": "Series branches and MVA limits",
              "default": {
                "type": "array",
                "items": 3,
                "itemTypes": [
                  "object"
                ],
                "sha256": "e8cfa4e15e2be03e4304365aff45787fdf3c00e9b6be91340725ac13894f584c"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/physical_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "75bd8bd8e2d3b29dcaa90947f359e43f163b85b6d115537ab52c2eb828b88e3e",
          "evidenceSha256": "1d9b3567ad8f35285ba73e25ef99164b960ef26f48e70b980a4018461aedc78c",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-powerflow\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-powerflow\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "For 0.1 pu resistance and 50 MW load on 100 MVA base, load voltage is (1+sqrt(0.8))/2 pu and branch I²R loss closes.",
            "testIds": [
              {
                "node_id": "tests/test_physical_studies.py::test_balanced_two_bus_powerflow_matches_resistive_hand_solution_and_branch_loss",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "base_mva below declared minimum 1",
            "base_mva above declared maximum 1000000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_physical_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "base_mva=1000000 (max bound)",
              "metric_snapshot": [
                {
                  "label": "Solved active branch losses",
                  "value": 0.992508267065574,
                  "unit": "MW"
                },
                {
                  "label": "Minimum bus voltage",
                  "value": 1,
                  "unit": "p.u."
                },
                {
                  "label": "Maximum branch loading",
                  "value": 150.31473233093374,
                  "unit": "fraction"
                },
                {
                  "label": "Maximum nodal power mismatch",
                  "value": 1.1753656180930536e-8,
                  "unit": "MVA"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {
            "converged": true
          },
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "PASS",
          "actual": "converged=True"
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "4694bf1a9d76f51e62330f7a065f7238557a415a2061b40023a49844637b9732"
          }
        },
        "limitations": [
          "Balanced positive-sequence per-unit educational model; one slack bus and constant-power PQ loads only.",
          "No generator PV/Q limits, transformers, controls, protection, unbalanced phases or operational dispatch.",
          "Non-converged or non-finite solutions fail closed. Overloaded branch ratings are reported, not controlled."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/physical_studies.py",
          "tests": [
            "apps/api/tests/test_physical_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "hybrid-powerflow",
            "workspace": "hybrid",
            "title": "Balanced AC nodal power flow",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_physical_studies.py::test_balanced_two_bus_powerflow_matches_resistive_hand_solution_and_branch_loss",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "For 0.1 pu resistance and 50 MW load on 100 MVA base, load voltage is (1+sqrt(0.8))/2 pu and branch I²R loss closes.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Solved active branch losses",
                        "value": 2.786404500042046,
                        "unit": "MW"
                      },
                      {
                        "label": "Minimum bus voltage",
                        "value": 0.9472135954999581,
                        "unit": "p.u."
                      },
                      {
                        "label": "Maximum branch loading",
                        "value": 0.5278640450004191,
                        "unit": "fraction"
                      },
                      {
                        "label": "Maximum nodal power mismatch",
                        "value": 1.8318679906315083e-13,
                        "unit": "MVA"
                      }
                    ],
                    "scalar_details": {
                      "converged": true,
                      "solver": "SciPy hybrid root on polar voltage variables",
                      "solver_function_evaluations": 10,
                      "residual_max_pu": 1.8318679906315083e-15,
                      "base_mva": 100,
                      "slack_bus_id": "grid"
                    },
                    "table_row_count": 1,
                    "result_sha256": "569f12d76fed5f196182be0dec542bfb60e0bebd93f3715f682c62c6ad3cd2d2"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Solved active branch losses",
                    "value": 0.05402459338257937,
                    "unit": "MW"
                  },
                  {
                    "label": "Minimum bus voltage",
                    "value": 0.9953182386809247,
                    "unit": "p.u."
                  },
                  {
                    "label": "Maximum branch loading",
                    "value": 0.2107209239545107,
                    "unit": "fraction"
                  },
                  {
                    "label": "Maximum nodal power mismatch",
                    "value": 1.4296461237073288e-8,
                    "unit": "MVA"
                  }
                ],
                "default_result_sha256": "4694bf1a9d76f51e62330f7a065f7238557a415a2061b40023a49844637b9732"
              },
              "units": "p.u. / MW / MVA",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 4
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "base_mva=1000000 (max bound)",
                "metric_snapshot": [
                  {
                    "label": "Solved active branch losses",
                    "value": 0.992508267065574,
                    "unit": "MW"
                  },
                  {
                    "label": "Minimum bus voltage",
                    "value": 1,
                    "unit": "p.u."
                  },
                  {
                    "label": "Maximum branch loading",
                    "value": 150.31473233093374,
                    "unit": "fraction"
                  },
                  {
                    "label": "Maximum nodal power mismatch",
                    "value": 1.1753656180930536e-8,
                    "unit": "MVA"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "4694bf1a9d76f51e62330f7a065f7238557a415a2061b40023a49844637b9732"
              },
              "solver_feasibility": {
                "status": "PASS",
                "actual": "converged=True"
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/physical_studies.py",
              "engine_sha256": "7414df30acfea75c77b1a4ed0e88c7b2b4ea9b32644f8f753a38de145bb0342a",
              "test_file": "apps/api/tests/test_physical_studies.py",
              "test_sha256": "734ebf9b04f0c67f9c95a5e3b4e1f1d85db5e6d7768356bbd99121acd1ccf4f6",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Balanced positive-sequence per-unit educational model; one slack bus and constant-power PQ loads only.",
              "No generator PV/Q limits, transformers, controls, protection, unbalanced phases or operational dispatch.",
              "Non-converged or non-finite solutions fail closed. Overloaded branch ratings are reported, not controlled.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-hybrid-powerflow"
    },
    {
      "id": "hybrid-switching",
      "title": "PWM inverter/filter transient",
      "summary": "Compare single-phase bipolar PWM switching with its averaged voltage in a series RL grid filter.",
      "workspace": {
        "id": "hybrid",
        "name": "Hybrid systems lab",
        "appRoute": "#hybrid"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "waveform",
        "equations": [
          "L·di/dt = vinverter(t) − vgrid(t) − R·i",
          "Switched bridge: vinverter ∈ {−Vdc/2, +Vdc/2} from sinusoidal-reference/carrier comparison.",
          "Averaged bridge: vinverter = (m·Vdc/2) sin(2πf1t); RL step uses the exact constant-input exponential update."
        ],
        "limitations": [
          "Single-phase series-RL filter, ideal bipolar switches and stiff sinusoidal grid; no semiconductor losses, dead time, PLL or control loops.",
          "Switching mode is explicitly sampled; the averaged mode removes carrier ripple. This is not a validated EMT product study.",
          "A half-step current-RMS comparison is reported as a numerical resolution check."
        ],
        "sources": [
          {
            "title": "NREL photovoltaic inverter detailed switching model report",
            "url": "https://www.nrel.gov/docs/fy20osti/74462.pdf"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "switching_enabled",
            "label": "0 averaged, 1 explicit PWM",
            "default": 1,
            "min": 0,
            "max": 1,
            "step": 1,
            "unit": ""
          },
          {
            "name": "dc_link_voltage_v",
            "label": "DC link voltage",
            "default": 650,
            "min": 100,
            "max": 2000,
            "step": 10,
            "unit": "V"
          },
          {
            "name": "modulation_index",
            "label": "Modulation index",
            "default": 0.85,
            "min": 0,
            "max": 0.95,
            "step": 0.01,
            "unit": ""
          },
          {
            "name": "fundamental_hz",
            "label": "Grid frequency",
            "default": 50,
            "min": 40,
            "max": 70,
            "step": 1,
            "unit": "Hz"
          },
          {
            "name": "switching_hz",
            "label": "PWM carrier frequency",
            "default": 2000,
            "min": 100,
            "max": 5000,
            "step": 100,
            "unit": "Hz"
          },
          {
            "name": "filter_inductance_h",
            "label": "Filter inductance",
            "default": 0.003,
            "min": 0.0001,
            "max": 0.1,
            "step": 0.0001,
            "unit": "H"
          },
          {
            "name": "filter_resistance_ohm",
            "label": "Filter resistance",
            "default": 0.5,
            "min": 0.05,
            "max": 20,
            "step": 0.05,
            "unit": "Ω"
          },
          {
            "name": "grid_voltage_rms_v",
            "label": "Grid voltage",
            "default": 230,
            "min": 10,
            "max": 1000,
            "step": 1,
            "unit": "V RMS"
          },
          {
            "name": "time_step_s",
            "label": "EMT time step",
            "default": 0.00002,
            "min": 0.000001,
            "max": 0.00005,
            "step": 0.000001,
            "unit": "s"
          },
          {
            "name": "simulation_cycles",
            "label": "Fundamental cycles",
            "default": 2,
            "min": 1,
            "max": 5,
            "step": 1,
            "unit": ""
          }
        ],
        "jsonDataFields": []
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "40cf32e2f140ef8600c12805d08b9416a762df6a45f75f88fe27bb5a2904546c",
        "evidenceSha256": "4740e5cfe1865f4f13882295edf9045f9844f0a513079bdb93428478922860e4",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-switching\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-switching\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Sampled switching mode has more carrier ripple than averaged mode and half-step relative error below 5%.",
          "testIds": [
            {
              "node_id": "tests/test_physical_studies.py::test_switching_and_averaged_inverter_modes_are_distinct_and_numerically_converged",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "switching_enabled",
              "unit": null,
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "dc_link_voltage_v",
              "unit": "V",
              "minimum": 100,
              "maximum": 2000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "modulation_index",
              "unit": null,
              "minimum": 0,
              "maximum": 0.95,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "fundamental_hz",
              "unit": "Hz",
              "minimum": 40,
              "maximum": 70,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "switching_hz",
              "unit": "Hz",
              "minimum": 100,
              "maximum": 5000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "filter_inductance_h",
              "unit": "H",
              "minimum": 0.0001,
              "maximum": 0.1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "filter_resistance_ohm",
              "unit": "Ω",
              "minimum": 0.05,
              "maximum": 20,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "grid_voltage_rms_v",
              "unit": "V RMS",
              "minimum": 10,
              "maximum": 1000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "time_step_s",
              "unit": "s",
              "minimum": 0.000001,
              "maximum": 0.00005,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "simulation_cycles",
              "unit": null,
              "minimum": 1,
              "maximum": 5,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [],
          "strictValidationReference": "apps/api/helioforge/engines/physical_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "40cf32e2f140ef8600c12805d08b9416a762df6a45f75f88fe27bb5a2904546c",
          "evidenceSha256": "4740e5cfe1865f4f13882295edf9045f9844f0a513079bdb93428478922860e4",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-switching\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-switching\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Sampled switching mode has more carrier ripple than averaged mode and half-step relative error below 5%.",
            "testIds": [
              {
                "node_id": "tests/test_physical_studies.py::test_switching_and_averaged_inverter_modes_are_distinct_and_numerically_converged",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "switching_enabled below declared minimum 0",
            "switching_enabled above declared maximum 1",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_physical_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "switching_enabled=0 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Grid current RMS",
                  "value": 32.50150364457969,
                  "unit": "A"
                },
                {
                  "label": "Grid current peak magnitude",
                  "value": 56.19814752169254,
                  "unit": "A"
                },
                {
                  "label": "PWM ripple RMS versus averaged case",
                  "value": 0,
                  "unit": "A"
                },
                {
                  "label": "Half-step relative RMS difference",
                  "value": 0.00012687849045049914,
                  "unit": "fraction"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {
            "convergence_relative_error": 0.002843372180812279
          },
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "4a9842d4e47fcb1347013934a39b8d1a7bff7213522bdca878cce3882ad7b7c8"
          }
        },
        "limitations": [
          "Single-phase series-RL filter, ideal bipolar switches and stiff sinusoidal grid; no semiconductor losses, dead time, PLL or control loops.",
          "Switching mode is explicitly sampled; the averaged mode removes carrier ripple. This is not a validated EMT product study.",
          "A half-step current-RMS comparison is reported as a numerical resolution check."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/physical_studies.py",
          "tests": [
            "apps/api/tests/test_physical_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "hybrid-switching",
            "workspace": "hybrid",
            "title": "PWM inverter/filter transient",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_physical_studies.py::test_switching_and_averaged_inverter_modes_are_distinct_and_numerically_converged",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Sampled switching mode has more carrier ripple than averaged mode and half-step relative error below 5%.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Grid current RMS",
                        "value": 32.61651324649872,
                        "unit": "A"
                      },
                      {
                        "label": "Grid current peak magnitude",
                        "value": 69.55929552776696,
                        "unit": "A"
                      },
                      {
                        "label": "PWM ripple RMS versus averaged case",
                        "value": 6.128786400625032,
                        "unit": "A"
                      },
                      {
                        "label": "Half-step relative RMS difference",
                        "value": 0.002843372180812279,
                        "unit": "fraction"
                      }
                    ],
                    "scalar_details": {
                      "mode": "switching",
                      "internal_time_step_s": 0.00002,
                      "internal_steps": 2000,
                      "convergence_relative_error": 0.002843372180812279,
                      "switching_ripple_rms_a": 6.128786400625032,
                      "switching_enabled": true
                    },
                    "table_row_count": 0,
                    "result_sha256": "7b8b905d7bf057fa02fcff82b7184464f5bb7e7a6b40b381d98c0838a721c1ae"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Grid current RMS",
                        "value": 32.50150364457969,
                        "unit": "A"
                      },
                      {
                        "label": "Grid current peak magnitude",
                        "value": 56.19814752169254,
                        "unit": "A"
                      },
                      {
                        "label": "PWM ripple RMS versus averaged case",
                        "value": 0,
                        "unit": "A"
                      },
                      {
                        "label": "Half-step relative RMS difference",
                        "value": 0.00012687849045049914,
                        "unit": "fraction"
                      }
                    ],
                    "scalar_details": {
                      "mode": "averaged",
                      "internal_time_step_s": 0.00002,
                      "internal_steps": 2000,
                      "convergence_relative_error": 0.00012687849045049914,
                      "switching_ripple_rms_a": 0,
                      "switching_enabled": false
                    },
                    "table_row_count": 0,
                    "result_sha256": "0bdca4956251713ab6ad9485cc68235afdc41966c6d090e05aa5af52e435609c"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Grid current RMS",
                    "value": 32.61651324649872,
                    "unit": "A"
                  },
                  {
                    "label": "Grid current peak magnitude",
                    "value": 69.55929552776696,
                    "unit": "A"
                  },
                  {
                    "label": "PWM ripple RMS versus averaged case",
                    "value": 6.128786400625032,
                    "unit": "A"
                  },
                  {
                    "label": "Half-step relative RMS difference",
                    "value": 0.002843372180812279,
                    "unit": "fraction"
                  }
                ],
                "default_result_sha256": "4a9842d4e47fcb1347013934a39b8d1a7bff7213522bdca878cce3882ad7b7c8"
              },
              "units": "A / fraction",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 4
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "switching_enabled=0 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Grid current RMS",
                    "value": 32.50150364457969,
                    "unit": "A"
                  },
                  {
                    "label": "Grid current peak magnitude",
                    "value": 56.19814752169254,
                    "unit": "A"
                  },
                  {
                    "label": "PWM ripple RMS versus averaged case",
                    "value": 0,
                    "unit": "A"
                  },
                  {
                    "label": "Half-step relative RMS difference",
                    "value": 0.00012687849045049914,
                    "unit": "fraction"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "4a9842d4e47fcb1347013934a39b8d1a7bff7213522bdca878cce3882ad7b7c8"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/physical_studies.py",
              "engine_sha256": "7414df30acfea75c77b1a4ed0e88c7b2b4ea9b32644f8f753a38de145bb0342a",
              "test_file": "apps/api/tests/test_physical_studies.py",
              "test_sha256": "734ebf9b04f0c67f9c95a5e3b4e1f1d85db5e6d7768356bbd99121acd1ccf4f6",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Single-phase series-RL filter, ideal bipolar switches and stiff sinusoidal grid; no semiconductor losses, dead time, PLL or control loops.",
              "Switching mode is explicitly sampled; the averaged mode removes carrier ripple. This is not a validated EMT product study.",
              "A half-step current-RMS comparison is reported as a numerical resolution check.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-hybrid-switching"
    },
    {
      "id": "lessons-energization",
      "title": "Timed LRC energization transient",
      "summary": "Inspect an R-L-C series circuit current and stored/dissipated energy around a single timed breaker closing step.",
      "workspace": {
        "id": "lessons",
        "name": "Learning studio",
        "appRoute": "#lessons"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "transient",
        "equations": [
          "L di/dt = vsource − R i − vC after breaker close",
          "C dvC/dt = i; before the close event, i and vC remain constant.",
          "E = ½Li² + ½CvC²; dE/dt = vsource·i − Ri² while connected."
        ],
        "limitations": [
          "One ideal single-phase series-RLC branch with a source-side close event; no transformer saturation, relay logic, arc or multi-step restoration.",
          "This lesson is not a black-start study or a certified energization/inrush limit assessment.",
          "A half-step peak-current comparison and circuit energy residual are reported."
        ],
        "sources": [
          {
            "title": "U.S. Department of Energy microgrid and black-start overview",
            "url": "https://www.energy.gov/cmei/systems/solar-integration-distributed-energy-resources-and-microgrids-basics"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "source_voltage_rms_v",
            "label": "Source voltage",
            "default": 230,
            "min": 1,
            "max": 1000,
            "step": 1,
            "unit": "V RMS"
          },
          {
            "name": "frequency_hz",
            "label": "Source frequency",
            "default": 50,
            "min": 40,
            "max": 70,
            "step": 1,
            "unit": "Hz"
          },
          {
            "name": "breaker_close_s",
            "label": "Breaker close time",
            "default": 0.005,
            "min": 0,
            "max": 0.1,
            "step": 0.0001,
            "unit": "s"
          },
          {
            "name": "resistance_ohm",
            "label": "Series resistance",
            "default": 5,
            "min": 0.1,
            "max": 100,
            "step": 0.1,
            "unit": "Ω"
          },
          {
            "name": "inductance_h",
            "label": "Series inductance",
            "default": 0.05,
            "min": 0.001,
            "max": 2,
            "step": 0.001,
            "unit": "H"
          },
          {
            "name": "capacitance_f",
            "label": "Series capacitance",
            "default": 0.001,
            "min": 0.0001,
            "max": 0.5,
            "step": 0.0001,
            "unit": "F"
          },
          {
            "name": "initial_capacitor_voltage_v",
            "label": "Initial capacitor voltage",
            "default": 0,
            "min": -1000,
            "max": 1000,
            "step": 1,
            "unit": "V"
          },
          {
            "name": "closing_phase_deg",
            "label": "Source phase",
            "default": 0,
            "min": -180,
            "max": 180,
            "step": 1,
            "unit": "degrees"
          },
          {
            "name": "time_step_s",
            "label": "Integration output step",
            "default": 0.00002,
            "min": 0.000001,
            "max": 0.00005,
            "step": 0.000001,
            "unit": "s"
          },
          {
            "name": "simulation_cycles",
            "label": "Source cycles",
            "default": 2,
            "min": 1,
            "max": 5,
            "step": 1,
            "unit": ""
          }
        ],
        "jsonDataFields": []
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "94d879bbb2569af4d4553c6451bcc27f7cb7482de76c493d732be9dbac1b49fa",
        "evidenceSha256": "4605f2da9463e436145e356516dc219608eb59c67e182996e2ea680db8294a69",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"lessons-energization\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"lessons-energization\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Current is zero before the 5 ms breaker close, then nonzero; circuit energy residual stays within 0.5 J.",
          "testIds": [
            {
              "node_id": "tests/test_physical_studies.py::test_energization_trace_starts_at_breaker_step_and_closes_circuit_energy_balance",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "source_voltage_rms_v",
              "unit": "V RMS",
              "minimum": 1,
              "maximum": 1000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "frequency_hz",
              "unit": "Hz",
              "minimum": 40,
              "maximum": 70,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "breaker_close_s",
              "unit": "s",
              "minimum": 0,
              "maximum": 0.1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "resistance_ohm",
              "unit": "Ω",
              "minimum": 0.1,
              "maximum": 100,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "inductance_h",
              "unit": "H",
              "minimum": 0.001,
              "maximum": 2,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "capacitance_f",
              "unit": "F",
              "minimum": 0.0001,
              "maximum": 0.5,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "initial_capacitor_voltage_v",
              "unit": "V",
              "minimum": -1000,
              "maximum": 1000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "closing_phase_deg",
              "unit": "degrees",
              "minimum": -180,
              "maximum": 180,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "time_step_s",
              "unit": "s",
              "minimum": 0.000001,
              "maximum": 0.00005,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "simulation_cycles",
              "unit": null,
              "minimum": 1,
              "maximum": 5,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [],
          "strictValidationReference": "apps/api/helioforge/engines/physical_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "94d879bbb2569af4d4553c6451bcc27f7cb7482de76c493d732be9dbac1b49fa",
          "evidenceSha256": "4605f2da9463e436145e356516dc219608eb59c67e182996e2ea680db8294a69",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"lessons-energization\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"lessons-energization\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Current is zero before the 5 ms breaker close, then nonzero; circuit energy residual stays within 0.5 J.",
            "testIds": [
              {
                "node_id": "tests/test_physical_studies.py::test_energization_trace_starts_at_breaker_step_and_closes_circuit_energy_balance",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "source_voltage_rms_v below declared minimum 1",
            "source_voltage_rms_v above declared maximum 1000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_physical_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "source_voltage_rms_v=1 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Peak inrush current",
                  "value": 0.11677616219395258,
                  "unit": "A"
                },
                {
                  "label": "Final inductor energy",
                  "value": 0.0002082281597485233,
                  "unit": "J"
                },
                {
                  "label": "Final capacitor energy",
                  "value": 0.00000476091204323826,
                  "unit": "J"
                },
                {
                  "label": "Resistive energy dissipated",
                  "value": 0.0009623579466458862,
                  "unit": "J"
                },
                {
                  "label": "Circuit energy residual",
                  "value": -2.347922095787512e-9,
                  "unit": "J"
                },
                {
                  "label": "Half-step peak-current difference",
                  "value": 1.6778245459647678e-14,
                  "unit": "fraction"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {
            "convergence_relative_error": 1.428571327891822e-13
          },
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "92db2c5231505ff98ce842aa5002108814286ee0f8a99962e449f083f3565a1a"
          }
        },
        "limitations": [
          "One ideal single-phase series-RLC branch with a source-side close event; no transformer saturation, relay logic, arc or multi-step restoration.",
          "This lesson is not a black-start study or a certified energization/inrush limit assessment.",
          "A half-step peak-current comparison and circuit energy residual are reported."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/physical_studies.py",
          "tests": [
            "apps/api/tests/test_physical_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "lessons-energization",
            "workspace": "lessons",
            "title": "Timed LRC energization transient",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_physical_studies.py::test_energization_trace_starts_at_breaker_step_and_closes_circuit_energy_balance",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Current is zero before the 5 ms breaker close, then nonzero; circuit energy residual stays within 0.5 J.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Peak inrush current",
                        "value": 26.858517304609094,
                        "unit": "A"
                      },
                      {
                        "label": "Final inductor energy",
                        "value": 11.015269650696887,
                        "unit": "J"
                      },
                      {
                        "label": "Final capacitor energy",
                        "value": 0.25185224708730075,
                        "unit": "J"
                      },
                      {
                        "label": "Resistive energy dissipated",
                        "value": 50.90873537756724,
                        "unit": "J"
                      },
                      {
                        "label": "Circuit energy residual",
                        "value": -0.00012420507867716424,
                        "unit": "J"
                      },
                      {
                        "label": "Half-step peak-current difference",
                        "value": 1.428571327891822e-13,
                        "unit": "fraction"
                      }
                    ],
                    "scalar_details": {
                      "breaker_close_s": 0.005,
                      "closing_phase_deg": 0,
                      "coarse_peak_current_a": 26.858517304609094,
                      "refined_peak_current_a": 26.858517304605257,
                      "convergence_relative_error": 1.428571327891822e-13,
                      "energy_balance_residual_j": -0.00012420507867716424,
                      "internal_samples": 2001
                    },
                    "table_row_count": 0,
                    "result_sha256": "4ed755624fcb6f228908386628f1b40f383feb11a9c5255717ea7f71b7b723ae"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Peak inrush current",
                    "value": 26.858517304609094,
                    "unit": "A"
                  },
                  {
                    "label": "Final inductor energy",
                    "value": 11.015269650696887,
                    "unit": "J"
                  },
                  {
                    "label": "Final capacitor energy",
                    "value": 0.25185224708730075,
                    "unit": "J"
                  },
                  {
                    "label": "Resistive energy dissipated",
                    "value": 50.90873537756724,
                    "unit": "J"
                  },
                  {
                    "label": "Circuit energy residual",
                    "value": -0.00012420507867716424,
                    "unit": "J"
                  },
                  {
                    "label": "Half-step peak-current difference",
                    "value": 1.428571327891822e-13,
                    "unit": "fraction"
                  }
                ],
                "default_result_sha256": "92db2c5231505ff98ce842aa5002108814286ee0f8a99962e449f083f3565a1a"
              },
              "units": "s / A / J",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 6
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "source_voltage_rms_v=1 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Peak inrush current",
                    "value": 0.11677616219395258,
                    "unit": "A"
                  },
                  {
                    "label": "Final inductor energy",
                    "value": 0.0002082281597485233,
                    "unit": "J"
                  },
                  {
                    "label": "Final capacitor energy",
                    "value": 0.00000476091204323826,
                    "unit": "J"
                  },
                  {
                    "label": "Resistive energy dissipated",
                    "value": 0.0009623579466458862,
                    "unit": "J"
                  },
                  {
                    "label": "Circuit energy residual",
                    "value": -2.347922095787512e-9,
                    "unit": "J"
                  },
                  {
                    "label": "Half-step peak-current difference",
                    "value": 1.6778245459647678e-14,
                    "unit": "fraction"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "92db2c5231505ff98ce842aa5002108814286ee0f8a99962e449f083f3565a1a"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/physical_studies.py",
              "engine_sha256": "7414df30acfea75c77b1a4ed0e88c7b2b4ea9b32644f8f753a38de145bb0342a",
              "test_file": "apps/api/tests/test_physical_studies.py",
              "test_sha256": "734ebf9b04f0c67f9c95a5e3b4e1f1d85db5e6d7768356bbd99121acd1ccf4f6",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "One ideal single-phase series-RLC branch with a source-side close event; no transformer saturation, relay logic, arc or multi-step restoration.",
              "This lesson is not a black-start study or a certified energization/inrush limit assessment.",
              "A half-step peak-current comparison and circuit energy residual are reported.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-lessons-energization"
    },
    {
      "id": "storage-response",
      "title": "Sub-second frequency droop response",
      "summary": "Apply frequency droop with a deadband, ramp, power cap, SOC limits and explicit interval energy integration.",
      "workspace": {
        "id": "storage",
        "name": "Storage & flexibility",
        "appRoute": "#storage"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "response",
        "equations": [
          "Pcommand = −droop·(f − fnominal) outside the selected deadband",
          "Power is bounded by ramp, rated kW and SOC energy headroom.",
          "ΔSOC = −P·Δt/(3600·ηdischarge) for discharge; charge uses ηcharge; delivered kWh = ΣP·Δt/3600."
        ],
        "limitations": [
          "Deterministic response to supplied frequency samples; each interval holds its measured frequency and output power constant.",
          "No grid-code, telemetry, controls, inverter current, reserve procurement, metering or service-qualification model."
        ],
        "sources": [
          {
            "title": "U.S. Department of Energy essential reliability services",
            "url": "https://www.energy.gov/cmei/articles/keeping-lights-essential-reliability-services"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "nominal_frequency_hz",
            "label": "Nominal frequency",
            "default": 50,
            "min": 45,
            "max": 65,
            "step": 0.1,
            "unit": "Hz"
          },
          {
            "name": "deadband_hz",
            "label": "Frequency deadband",
            "default": 0.02,
            "min": 0,
            "max": 1,
            "step": 0.01,
            "unit": "Hz"
          },
          {
            "name": "droop_kw_per_hz",
            "label": "Droop slope",
            "default": 2500,
            "min": 0,
            "max": 1000000,
            "step": 100,
            "unit": "kW/Hz"
          },
          {
            "name": "power_limit_kw",
            "label": "Power limit",
            "default": 1000,
            "min": 0,
            "max": 1000000,
            "step": 10,
            "unit": "kW"
          },
          {
            "name": "ramp_kw_per_s",
            "label": "Power ramp limit",
            "default": 1000,
            "min": 0,
            "max": 10000000,
            "step": 10,
            "unit": "kW/s"
          },
          {
            "name": "capacity_kwh",
            "label": "Storage capacity",
            "default": 2000,
            "min": 1,
            "max": 1000000,
            "step": 10,
            "unit": "kWh"
          },
          {
            "name": "initial_soc_fraction",
            "label": "Initial SOC",
            "default": 0.5,
            "min": 0.1,
            "max": 0.9,
            "step": 0.01,
            "unit": ""
          },
          {
            "name": "minimum_soc_fraction",
            "label": "Minimum SOC",
            "default": 0.1,
            "min": 0,
            "max": 0.89,
            "step": 0.01,
            "unit": ""
          },
          {
            "name": "maximum_soc_fraction",
            "label": "Maximum SOC",
            "default": 0.9,
            "min": 0.11,
            "max": 1,
            "step": 0.01,
            "unit": ""
          },
          {
            "name": "round_trip_efficiency",
            "label": "Round-trip efficiency",
            "default": 0.9,
            "min": 0.5,
            "max": 1,
            "step": 0.01,
            "unit": ""
          },
          {
            "name": "interval_s",
            "label": "Observation interval",
            "default": 0.1,
            "min": 0.001,
            "max": 0.5,
            "step": 0.01,
            "unit": "s"
          }
        ],
        "jsonDataFields": [
          {
            "name": "frequency_hz",
            "label": "Observed frequency samples",
            "default": {
              "type": "array",
              "items": 12,
              "itemTypes": [
                "number"
              ],
              "sha256": "530dfac6fa6eebdee7b67fa75a0e2a8ca77118ebeab4bc55a3f7de855249222b"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "f8e2972bdbefc8d10947470e43793990816dd4b9307e73ee665f86c2c089a120",
        "evidenceSha256": "0e8d70eeafe5070307b4b577a921be50f1b8ef111ad217346152834515f60ff7",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-response\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-response\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "For two 49.9 Hz samples, 1000 kW/Hz droop with 100 kW/s ramp produces [10,20] kW; energy is sum(P dt)/3600.",
          "testIds": [
            {
              "node_id": "tests/test_physical_studies.py::test_frequency_droop_obeys_ramp_soc_power_and_dt_energy_integration",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "nominal_frequency_hz",
              "unit": "Hz",
              "minimum": 45,
              "maximum": 65,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "deadband_hz",
              "unit": "Hz",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "droop_kw_per_hz",
              "unit": "kW/Hz",
              "minimum": 0,
              "maximum": 1000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "power_limit_kw",
              "unit": "kW",
              "minimum": 0,
              "maximum": 1000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "ramp_kw_per_s",
              "unit": "kW/s",
              "minimum": 0,
              "maximum": 10000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "capacity_kwh",
              "unit": "kWh",
              "minimum": 1,
              "maximum": 1000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "initial_soc_fraction",
              "unit": null,
              "minimum": 0.1,
              "maximum": 0.9,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "minimum_soc_fraction",
              "unit": null,
              "minimum": 0,
              "maximum": 0.89,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "maximum_soc_fraction",
              "unit": null,
              "minimum": 0.11,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "round_trip_efficiency",
              "unit": null,
              "minimum": 0.5,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "interval_s",
              "unit": "s",
              "minimum": 0.001,
              "maximum": 0.5,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "frequency_hz",
              "label": "Observed frequency samples",
              "default": {
                "type": "array",
                "items": 12,
                "itemTypes": [
                  "number"
                ],
                "sha256": "530dfac6fa6eebdee7b67fa75a0e2a8ca77118ebeab4bc55a3f7de855249222b"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/physical_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "f8e2972bdbefc8d10947470e43793990816dd4b9307e73ee665f86c2c089a120",
          "evidenceSha256": "0e8d70eeafe5070307b4b577a921be50f1b8ef111ad217346152834515f60ff7",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-response\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-response\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "For two 49.9 Hz samples, 1000 kW/Hz droop with 100 kW/s ramp produces [10,20] kW; energy is sum(P dt)/3600.",
            "testIds": [
              {
                "node_id": "tests/test_physical_studies.py::test_frequency_droop_obeys_ramp_soc_power_and_dt_energy_integration",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "nominal_frequency_hz below declared minimum 45",
            "nominal_frequency_hz above declared maximum 65",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_physical_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "nominal_frequency_hz=45 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Delivered response energy",
                  "value": 0,
                  "unit": "kWh"
                },
                {
                  "label": "Absorbed response energy",
                  "value": 0.20833333333333337,
                  "unit": "kWh"
                },
                {
                  "label": "Peak absolute response",
                  "value": 1000,
                  "unit": "kW"
                },
                {
                  "label": "Terminal state of charge",
                  "value": 1000.1976423537606,
                  "unit": "kWh"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "e2d9931479f5d794a0de3bafd37d295b86dc54caada05ee8f42f29b0f4e4d13d"
          }
        },
        "limitations": [
          "Deterministic response to supplied frequency samples; each interval holds its measured frequency and output power constant.",
          "No grid-code, telemetry, controls, inverter current, reserve procurement, metering or service-qualification model."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/physical_studies.py",
          "tests": [
            "apps/api/tests/test_physical_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "storage-response",
            "workspace": "storage",
            "title": "Sub-second frequency droop response",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_physical_studies.py::test_frequency_droop_obeys_ramp_soc_power_and_dt_energy_integration",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "For two 49.9 Hz samples, 1000 kW/Hz droop with 100 kW/s ramp produces [10,20] kW; energy is sum(P dt)/3600.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Delivered response energy",
                        "value": 0.0008333333333333333,
                        "unit": "kWh"
                      },
                      {
                        "label": "Absorbed response energy",
                        "value": 0,
                        "unit": "kWh"
                      },
                      {
                        "label": "Peak absolute response",
                        "value": 20,
                        "unit": "kW"
                      },
                      {
                        "label": "Terminal state of charge",
                        "value": 49.999121589538845,
                        "unit": "kWh"
                      }
                    ],
                    "scalar_details": {
                      "nominal_frequency_hz": 50,
                      "interval_s": 0.1,
                      "round_trip_efficiency": 0.9,
                      "integrated_dt_hours": 0.00002777777777777778
                    },
                    "table_row_count": 2,
                    "result_sha256": "85ba6056750120986046ad03613b5a9f7d0a27d5c9a25c926a92d801fbd77830"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Delivered response energy",
                    "value": 0.02361111111111111,
                    "unit": "kWh"
                  },
                  {
                    "label": "Absorbed response energy",
                    "value": 0,
                    "unit": "kWh"
                  },
                  {
                    "label": "Peak absolute response",
                    "value": 200.00000000000355,
                    "unit": "kW"
                  },
                  {
                    "label": "Terminal state of charge",
                    "value": 999.9751117036005,
                    "unit": "kWh"
                  }
                ],
                "default_result_sha256": "e2d9931479f5d794a0de3bafd37d295b86dc54caada05ee8f42f29b0f4e4d13d"
              },
              "units": "kW / kWh",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 4
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "nominal_frequency_hz=45 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Delivered response energy",
                    "value": 0,
                    "unit": "kWh"
                  },
                  {
                    "label": "Absorbed response energy",
                    "value": 0.20833333333333337,
                    "unit": "kWh"
                  },
                  {
                    "label": "Peak absolute response",
                    "value": 1000,
                    "unit": "kW"
                  },
                  {
                    "label": "Terminal state of charge",
                    "value": 1000.1976423537606,
                    "unit": "kWh"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "e2d9931479f5d794a0de3bafd37d295b86dc54caada05ee8f42f29b0f4e4d13d"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/physical_studies.py",
              "engine_sha256": "7414df30acfea75c77b1a4ed0e88c7b2b4ea9b32644f8f753a38de145bb0342a",
              "test_file": "apps/api/tests/test_physical_studies.py",
              "test_sha256": "734ebf9b04f0c67f9c95a5e3b4e1f1d85db5e6d7768356bbd99121acd1ccf4f6",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Deterministic response to supplied frequency samples; each interval holds its measured frequency and output power constant.",
              "No grid-code, telemetry, controls, inverter current, reserve procurement, metering or service-qualification model.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-storage-response"
    },
    {
      "id": "storage-agingfit",
      "title": "Capacity-fade coefficient fit",
      "summary": "Fit a small empirical cycle-and-temperature response on supplied records and score a held-out validation split.",
      "workspace": {
        "id": "storage",
        "name": "Storage & flexibility",
        "appRoute": "#storage"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "fit",
        "equations": [
          "retention = 1 − a·(cycles/1000)·exp(b·(temperature°C − 25))",
          "SciPy bounded least-squares fit uses records 0–3 of each fixed five-record group; every fifth record is held out."
        ],
        "limitations": [
          "The included records are a synthetic software fixture, not cell measurements or scientific validation.",
          "This two-coefficient empirical curve ignores chemistry, SOC, C-rate, calendar aging, path dependence, uncertainty and censoring.",
          "Held-out error describes only the supplied records and cannot qualify a warranty or predict field life."
        ],
        "sources": [
          {
            "title": "SciPy least_squares reference",
            "url": "https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.least_squares.html"
          },
          {
            "title": "Primary empirical Li-ion aging model study",
            "url": "https://doi.org/10.1016/j.apenergy.2016.03.119"
          }
        ]
      },
      "inputs": {
        "numericFields": [],
        "jsonDataFields": [
          {
            "name": "observations",
            "label": "Cycle, temperature and capacity observations",
            "default": {
              "type": "array",
              "items": 15,
              "itemTypes": [
                "object"
              ],
              "sha256": "0f13d2b5bfa24dc5757e602d4ebbd5e5d5efd76a0fd8e6ed0cb4f9c867f6e841"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "2b0cfcac393854bf1eea0621e4c05519a4f3a8dddd2f94fe50f0a0628b3bb29f",
        "evidenceSha256": "0fd55e00d3ce367e06ebb8d259d95216eb1d0263f0166a5bddbbe1e8b6aa6ac1",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-agingfit\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-agingfit\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Synthetic fixture fit recovers about 0.018 capacity fraction per 1,000 cycles within 2% and reports held-out RMSE below 0.003.",
          "testIds": [
            {
              "node_id": "tests/test_physical_studies.py::test_agingfit_recovers_synthetic_coefficient_and_reports_heldout_error",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [],
          "jsonDataFields": [
            {
              "name": "observations",
              "label": "Cycle, temperature and capacity observations",
              "default": {
                "type": "array",
                "items": 15,
                "itemTypes": [
                  "object"
                ],
                "sha256": "0f13d2b5bfa24dc5757e602d4ebbd5e5d5efd76a0fd8e6ed0cb4f9c867f6e841"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/physical_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "2b0cfcac393854bf1eea0621e4c05519a4f3a8dddd2f94fe50f0a0628b3bb29f",
          "evidenceSha256": "0fd55e00d3ce367e06ebb8d259d95216eb1d0263f0166a5bddbbe1e8b6aa6ac1",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-agingfit\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-agingfit\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Synthetic fixture fit recovers about 0.018 capacity fraction per 1,000 cycles within 2% and reports held-out RMSE below 0.003.",
            "testIds": [
              {
                "node_id": "tests/test_physical_studies.py::test_agingfit_recovers_synthetic_coefficient_and_reports_heldout_error",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "unknown input field, non-finite numeric input, and malformed/oversized JSON data"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_physical_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "observations=valid prefix length 10",
              "metric_snapshot": [
                {
                  "label": "Training RMSE",
                  "value": 0.00009537216060042586,
                  "unit": "capacity fraction"
                },
                {
                  "label": "Held-out RMSE",
                  "value": 0.000029124333874279695,
                  "unit": "capacity fraction"
                },
                {
                  "label": "Fitted cycle loss per 1000 cycles",
                  "value": 0.018016499014825906,
                  "unit": "fraction"
                },
                {
                  "label": "Fitted temperature sensitivity",
                  "value": 0.007630910876363972,
                  "unit": "1/°C"
                },
                {
                  "label": "Held-out observations",
                  "value": 2,
                  "unit": "records"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "PASS",
          "actual": "fit_success=True"
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "a38cea4f02d8e2a49f6ed2f1405733f244d6f9e0710a838b56d80821067ccabd"
          }
        },
        "limitations": [
          "The included records are a synthetic software fixture, not cell measurements or scientific validation.",
          "This two-coefficient empirical curve ignores chemistry, SOC, C-rate, calendar aging, path dependence, uncertainty and censoring.",
          "Held-out error describes only the supplied records and cannot qualify a warranty or predict field life."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/physical_studies.py",
          "tests": [
            "apps/api/tests/test_physical_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "storage-agingfit",
            "workspace": "storage",
            "title": "Capacity-fade coefficient fit",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_physical_studies.py::test_agingfit_recovers_synthetic_coefficient_and_reports_heldout_error",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Synthetic fixture fit recovers about 0.018 capacity fraction per 1,000 cycles within 2% and reports held-out RMSE below 0.003.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Training RMSE",
                        "value": 0.0001616030410786577,
                        "unit": "capacity fraction"
                      },
                      {
                        "label": "Held-out RMSE",
                        "value": 0.000152680463374377,
                        "unit": "capacity fraction"
                      },
                      {
                        "label": "Fitted cycle loss per 1000 cycles",
                        "value": 0.017998353899817823,
                        "unit": "fraction"
                      },
                      {
                        "label": "Fitted temperature sensitivity",
                        "value": 0.007966151246465077,
                        "unit": "1/°C"
                      },
                      {
                        "label": "Held-out observations",
                        "value": 3,
                        "unit": "records"
                      }
                    ],
                    "scalar_details": {
                      "dataset_label": "Synthetic fixture (software test only)",
                      "training_count": 12,
                      "heldout_count": 3,
                      "solver": "SciPy bounded nonlinear least_squares",
                      "fit_success": true
                    },
                    "table_row_count": 15,
                    "result_sha256": "720e345ee9dd1a1ff4b37ce42ed2c3577f9fa7552b93e77c90f1bf01979c6d0f"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Training RMSE",
                    "value": 0.0001616030410786577,
                    "unit": "capacity fraction"
                  },
                  {
                    "label": "Held-out RMSE",
                    "value": 0.000152680463374377,
                    "unit": "capacity fraction"
                  },
                  {
                    "label": "Fitted cycle loss per 1000 cycles",
                    "value": 0.017998353899817823,
                    "unit": "fraction"
                  },
                  {
                    "label": "Fitted temperature sensitivity",
                    "value": 0.007966151246465077,
                    "unit": "1/°C"
                  },
                  {
                    "label": "Held-out observations",
                    "value": 3,
                    "unit": "records"
                  }
                ],
                "default_result_sha256": "a38cea4f02d8e2a49f6ed2f1405733f244d6f9e0710a838b56d80821067ccabd"
              },
              "units": "fraction / records",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 5
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "observations=valid prefix length 10",
                "metric_snapshot": [
                  {
                    "label": "Training RMSE",
                    "value": 0.00009537216060042586,
                    "unit": "capacity fraction"
                  },
                  {
                    "label": "Held-out RMSE",
                    "value": 0.000029124333874279695,
                    "unit": "capacity fraction"
                  },
                  {
                    "label": "Fitted cycle loss per 1000 cycles",
                    "value": 0.018016499014825906,
                    "unit": "fraction"
                  },
                  {
                    "label": "Fitted temperature sensitivity",
                    "value": 0.007630910876363972,
                    "unit": "1/°C"
                  },
                  {
                    "label": "Held-out observations",
                    "value": 2,
                    "unit": "records"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "a38cea4f02d8e2a49f6ed2f1405733f244d6f9e0710a838b56d80821067ccabd"
              },
              "solver_feasibility": {
                "status": "PASS",
                "actual": "fit_success=True"
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/physical_studies.py",
              "engine_sha256": "7414df30acfea75c77b1a4ed0e88c7b2b4ea9b32644f8f753a38de145bb0342a",
              "test_file": "apps/api/tests/test_physical_studies.py",
              "test_sha256": "734ebf9b04f0c67f9c95a5e3b4e1f1d85db5e6d7768356bbd99121acd1ccf4f6",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "The included records are a synthetic software fixture, not cell measurements or scientific validation.",
              "This two-coefficient empirical curve ignores chemistry, SOC, C-rate, calendar aging, path dependence, uncertainty and censoring.",
              "Held-out error describes only the supplied records and cannot qualify a warranty or predict field life.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-storage-agingfit"
    },
    {
      "id": "compare-thermalphysics",
      "title": "Lumped thermal reaction screen",
      "summary": "Compare lumped cooling with a bounded Arrhenius heat-release term and explicit stop thresholds.",
      "workspace": {
        "id": "compare",
        "name": "Scenario compare",
        "appRoute": "#compare"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "thermal",
        "equations": [
          "k(T) = kref·exp[−Ea/R·(1/T − 1/Tref)] with absolute temperature in kelvin",
          "dα/dt = k(T)(1−α)",
          "Cth·dT/dt = Etotal·dα/dt − UA·(T − Tambient)."
        ],
        "limitations": [
          "Single effective thermal mass, constant cooling and one depleted reaction; no spatial gradients or material-specific chemistry.",
          "Temperature/reaction thresholds only stop this illustrative calculation; this is not a validated safety alarm or hazard assessment."
        ],
        "sources": [
          {
            "title": "IUPAC Gold Book Arrhenius equation",
            "url": "https://goldbook.iupac.org/terms/view/A00446"
          },
          {
            "title": "SciPy solve_ivp initial-value solver",
            "url": "https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.solve_ivp.html"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "initial_temperature_c",
            "label": "Initial temperature",
            "default": 25,
            "min": -20,
            "max": 120,
            "step": 1,
            "unit": "°C"
          },
          {
            "name": "ambient_temperature_c",
            "label": "Ambient temperature",
            "default": 25,
            "min": -30,
            "max": 80,
            "step": 1,
            "unit": "°C"
          },
          {
            "name": "thermal_mass_j_per_k",
            "label": "Effective thermal mass",
            "default": 10000,
            "min": 1000,
            "max": 1000000,
            "step": 100,
            "unit": "J/K"
          },
          {
            "name": "cooling_w_per_k",
            "label": "Lumped cooling coefficient",
            "default": 20,
            "min": 0,
            "max": 100000,
            "step": 1,
            "unit": "W/K"
          },
          {
            "name": "activation_energy_kj_mol",
            "label": "Activation energy",
            "default": 35,
            "min": 0,
            "max": 80,
            "step": 1,
            "unit": "kJ/mol"
          },
          {
            "name": "reaction_rate_ref_per_s",
            "label": "Reference reaction rate",
            "default": 0.002,
            "min": 0,
            "max": 0.005,
            "step": 0.0001,
            "unit": "1/s"
          },
          {
            "name": "reaction_energy_j",
            "label": "Total reaction energy",
            "default": 25000,
            "min": 0,
            "max": 10000000,
            "step": 100,
            "unit": "J"
          },
          {
            "name": "reference_temperature_c",
            "label": "Reference temperature",
            "default": 25,
            "min": -20,
            "max": 80,
            "step": 1,
            "unit": "°C"
          },
          {
            "name": "duration_s",
            "label": "Maximum duration",
            "default": 60,
            "min": 0.01,
            "max": 120,
            "step": 1,
            "unit": "s"
          },
          {
            "name": "time_step_s",
            "label": "Output interval",
            "default": 0.05,
            "min": 0.001,
            "max": 0.1,
            "step": 0.001,
            "unit": "s"
          },
          {
            "name": "termination_temperature_c",
            "label": "Stop temperature",
            "default": 100,
            "min": 30,
            "max": 150,
            "step": 1,
            "unit": "°C"
          },
          {
            "name": "reaction_fraction_trip",
            "label": "Stop conversion fraction",
            "default": 0.95,
            "min": 0.01,
            "max": 1,
            "step": 0.01,
            "unit": ""
          }
        ],
        "jsonDataFields": []
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "08523185b0074291bca13df53a5a252a29e86003a901b3c508fdd30483cec0ff",
        "evidenceSha256": "8b86353c3d394d440a10445dd787055e4c3f3c9f3bc1ca3ffd6f5b63db0584f7",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"compare-thermalphysics\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"compare-thermalphysics\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "The explicit Arrhenius fixture terminates at the 50°C configured event threshold with a positive sub-0.01 reaction fraction.",
          "testIds": [
            {
              "node_id": "tests/test_physical_studies.py::test_thermal_arrhenius_source_stops_at_temperature_event",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "initial_temperature_c",
              "unit": "°C",
              "minimum": -20,
              "maximum": 120,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "ambient_temperature_c",
              "unit": "°C",
              "minimum": -30,
              "maximum": 80,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "thermal_mass_j_per_k",
              "unit": "J/K",
              "minimum": 1000,
              "maximum": 1000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "cooling_w_per_k",
              "unit": "W/K",
              "minimum": 0,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "activation_energy_kj_mol",
              "unit": "kJ/mol",
              "minimum": 0,
              "maximum": 80,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "reaction_rate_ref_per_s",
              "unit": "1/s",
              "minimum": 0,
              "maximum": 0.005,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "reaction_energy_j",
              "unit": "J",
              "minimum": 0,
              "maximum": 10000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "reference_temperature_c",
              "unit": "°C",
              "minimum": -20,
              "maximum": 80,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "duration_s",
              "unit": "s",
              "minimum": 0.01,
              "maximum": 120,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "time_step_s",
              "unit": "s",
              "minimum": 0.001,
              "maximum": 0.1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "termination_temperature_c",
              "unit": "°C",
              "minimum": 30,
              "maximum": 150,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "reaction_fraction_trip",
              "unit": null,
              "minimum": 0.01,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [],
          "strictValidationReference": "apps/api/helioforge/engines/physical_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "08523185b0074291bca13df53a5a252a29e86003a901b3c508fdd30483cec0ff",
          "evidenceSha256": "8b86353c3d394d440a10445dd787055e4c3f3c9f3bc1ca3ffd6f5b63db0584f7",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"compare-thermalphysics\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"compare-thermalphysics\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "The explicit Arrhenius fixture terminates at the 50°C configured event threshold with a positive sub-0.01 reaction fraction.",
            "testIds": [
              {
                "node_id": "tests/test_physical_studies.py::test_thermal_arrhenius_source_stops_at_temperature_event",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "initial_temperature_c below declared minimum -20",
            "initial_temperature_c above declared maximum 120",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_physical_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "initial_temperature_c=-20 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Peak lumped temperature",
                  "value": -14.884156949798111,
                  "unit": "°C"
                },
                {
                  "label": "Final lumped temperature",
                  "value": -14.884156949798111,
                  "unit": "°C"
                },
                {
                  "label": "Final reaction fraction",
                  "value": 0.011535955586705324,
                  "unit": "fraction"
                },
                {
                  "label": "Reaction heat released",
                  "value": 288.3988896676331,
                  "unit": "J"
                },
                {
                  "label": "Half-step peak-temperature difference",
                  "value": 4.7738191565477795e-16,
                  "unit": "fraction"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {
            "convergence_relative_error": 3.233860233318603e-15
          },
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "122a2d9713950066c45017b3c2d435fa9b428745911bcbc34d26e6bc17e04daa"
          }
        },
        "limitations": [
          "Single effective thermal mass, constant cooling and one depleted reaction; no spatial gradients or material-specific chemistry.",
          "Temperature/reaction thresholds only stop this illustrative calculation; this is not a validated safety alarm or hazard assessment."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/physical_studies.py",
          "tests": [
            "apps/api/tests/test_physical_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "compare-thermalphysics",
            "workspace": "compare",
            "title": "Lumped thermal reaction screen",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_physical_studies.py::test_thermal_arrhenius_source_stops_at_temperature_event",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "The explicit Arrhenius fixture terminates at the 50°C configured event threshold with a positive sub-0.01 reaction fraction.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Peak lumped temperature",
                        "value": 49.999999999999986,
                        "unit": "°C"
                      },
                      {
                        "label": "Final lumped temperature",
                        "value": 49.999999999999986,
                        "unit": "°C"
                      },
                      {
                        "label": "Final reaction fraction",
                        "value": 0.0024999999999999988,
                        "unit": "fraction"
                      },
                      {
                        "label": "Reaction heat released",
                        "value": 24999.99999999999,
                        "unit": "J"
                      },
                      {
                        "label": "Half-step peak-temperature difference",
                        "value": 0,
                        "unit": "fraction"
                      }
                    ],
                    "scalar_details": {
                      "termination_reason": "temperature threshold reached",
                      "termination_time_s": 0.1783671892312461,
                      "final_temperature_c": 49.999999999999986,
                      "final_reaction_fraction": 0.0024999999999999988,
                      "convergence_relative_error": 0,
                      "heat_released_j": 24999.99999999999
                    },
                    "table_row_count": 0,
                    "result_sha256": "7ee1ce1b37fa2dd32ee775a1381576576437d214b03b9f4af42f1e0bcc128300"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Peak lumped temperature",
                    "value": 25.267763204644783,
                    "unit": "°C"
                  },
                  {
                    "label": "Final lumped temperature",
                    "value": 25.267763204644783,
                    "unit": "°C"
                  },
                  {
                    "label": "Final reaction fraction",
                    "value": 0.1137830492483632,
                    "unit": "fraction"
                  },
                  {
                    "label": "Reaction heat released",
                    "value": 2844.57623120908,
                    "unit": "J"
                  },
                  {
                    "label": "Half-step peak-temperature difference",
                    "value": 3.233860233318603e-15,
                    "unit": "fraction"
                  }
                ],
                "default_result_sha256": "122a2d9713950066c45017b3c2d435fa9b428745911bcbc34d26e6bc17e04daa"
              },
              "units": "°C / fraction / J",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 5
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "initial_temperature_c=-20 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Peak lumped temperature",
                    "value": -14.884156949798111,
                    "unit": "°C"
                  },
                  {
                    "label": "Final lumped temperature",
                    "value": -14.884156949798111,
                    "unit": "°C"
                  },
                  {
                    "label": "Final reaction fraction",
                    "value": 0.011535955586705324,
                    "unit": "fraction"
                  },
                  {
                    "label": "Reaction heat released",
                    "value": 288.3988896676331,
                    "unit": "J"
                  },
                  {
                    "label": "Half-step peak-temperature difference",
                    "value": 4.7738191565477795e-16,
                    "unit": "fraction"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "122a2d9713950066c45017b3c2d435fa9b428745911bcbc34d26e6bc17e04daa"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/physical_studies.py",
              "engine_sha256": "7414df30acfea75c77b1a4ed0e88c7b2b4ea9b32644f8f753a38de145bb0342a",
              "test_file": "apps/api/tests/test_physical_studies.py",
              "test_sha256": "734ebf9b04f0c67f9c95a5e3b4e1f1d85db5e6d7768356bbd99121acd1ccf4f6",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Single effective thermal mass, constant cooling and one depleted reaction; no spatial gradients or material-specific chemistry.",
              "Temperature/reaction thresholds only stop this illustrative calculation; this is not a validated safety alarm or hazard assessment.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-compare-thermalphysics"
    },
    {
      "id": "pv-raytrace",
      "title": "Panel-sample ray-triangle shading",
      "summary": "Intersect directional sun rays with supplied terrain/obstacle triangles and integrate direct-beam energy at panel samples.",
      "workspace": {
        "id": "pv",
        "name": "PV economics",
        "appRoute": "#pv"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "raytrace",
        "equations": [
          "incidence = max(0, n̂·ŝ); direct energy = DNI·panel area·incidence·duration.",
          "A ray is shaded when a supplied triangle intersects the half-line from a panel sample toward the sun."
        ],
        "limitations": [
          "Directional direct-beam only; no diffuse sky, ground reflection, spectral response, horizon/weather series or panel electrical mismatch.",
          "Triangles and panel samples are user-supplied local Cartesian geometry; results depend on mesh completeness and coordinate quality."
        ],
        "sources": [
          {
            "title": "Möller–Trumbore ray/triangle intersection paper",
            "url": "https://doi.org/10.1080/10867651.1997.10487468"
          },
          {
            "title": "NREL System Advisor Model 3D shading description",
            "url": "https://samrepo.nlr.gov/help/pv_shading.html"
          }
        ]
      },
      "inputs": {
        "numericFields": [],
        "jsonDataFields": [
          {
            "name": "panel_samples",
            "label": "Panel positions, normals and sample areas",
            "default": {
              "type": "array",
              "items": 1,
              "itemTypes": [
                "object"
              ],
              "sha256": "5a6cc1da801faa796c817be8d7a663ab52e8613989eefa7ce38b46b7657ce929"
            }
          },
          {
            "name": "sun_vectors",
            "label": "Sun directions, beam irradiance and duration",
            "default": {
              "type": "array",
              "items": 4,
              "itemTypes": [
                "object"
              ],
              "sha256": "cc98c685138dc56d58403c4cf5488fe95779a4d8d3c86c4bfa2b4478a7cfd693"
            }
          },
          {
            "name": "occluder_triangles",
            "label": "Terrain and obstacle mesh triangles",
            "default": {
              "type": "array",
              "items": 0,
              "itemTypes": [],
              "sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "07fc8b63015690a8a79483c530d31959f076fd2ece8a5c5d19c1631f0b693ea8",
        "evidenceSha256": "5f872d05000657728e1843edb375763a76550c22a0abe7c20475d446d21dbbf4",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-raytrace\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-raytrace\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "One 10 m² panel at 800 W/m² for one hour receives 8 kWh unshadowed and zero when shadowed, back-facing, or sunless.",
          "testIds": [
            {
              "node_id": "tests/test_physical_studies.py::test_ray_triangle_shading_handles_shadow_backface_and_zero_sun",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [],
          "jsonDataFields": [
            {
              "name": "panel_samples",
              "label": "Panel positions, normals and sample areas",
              "default": {
                "type": "array",
                "items": 1,
                "itemTypes": [
                  "object"
                ],
                "sha256": "5a6cc1da801faa796c817be8d7a663ab52e8613989eefa7ce38b46b7657ce929"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "sun_vectors",
              "label": "Sun directions, beam irradiance and duration",
              "default": {
                "type": "array",
                "items": 4,
                "itemTypes": [
                  "object"
                ],
                "sha256": "cc98c685138dc56d58403c4cf5488fe95779a4d8d3c86c4bfa2b4478a7cfd693"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "occluder_triangles",
              "label": "Terrain and obstacle mesh triangles",
              "default": {
                "type": "array",
                "items": 0,
                "itemTypes": [],
                "sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/physical_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "07fc8b63015690a8a79483c530d31959f076fd2ece8a5c5d19c1631f0b693ea8",
          "evidenceSha256": "5f872d05000657728e1843edb375763a76550c22a0abe7c20475d446d21dbbf4",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-raytrace\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-raytrace\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "One 10 m² panel at 800 W/m² for one hour receives 8 kWh unshadowed and zero when shadowed, back-facing, or sunless.",
            "testIds": [
              {
                "node_id": "tests/test_physical_studies.py::test_ray_triangle_shading_handles_shadow_backface_and_zero_sun",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "unknown input field, non-finite numeric input, and malformed/oversized JSON data"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_physical_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "sun_vectors=valid prefix length 1",
              "metric_snapshot": [
                {
                  "label": "Incident direct-beam energy",
                  "value": 6.062177819868303,
                  "unit": "kWh"
                },
                {
                  "label": "Shadowed panel/sun samples",
                  "value": 0,
                  "unit": "samples"
                },
                {
                  "label": "Front-side incidence samples",
                  "value": 1,
                  "unit": "samples"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "6c926f8cc64f61d450e542a169067f09bed5bf06a859a1032b052417b481937b"
          }
        },
        "limitations": [
          "Directional direct-beam only; no diffuse sky, ground reflection, spectral response, horizon/weather series or panel electrical mismatch.",
          "Triangles and panel samples are user-supplied local Cartesian geometry; results depend on mesh completeness and coordinate quality."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/physical_studies.py",
          "tests": [
            "apps/api/tests/test_physical_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "pv-raytrace",
            "workspace": "pv",
            "title": "Panel-sample ray-triangle shading",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_physical_studies.py::test_ray_triangle_shading_handles_shadow_backface_and_zero_sun",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "One 10 m² panel at 800 W/m² for one hour receives 8 kWh unshadowed and zero when shadowed, back-facing, or sunless.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Incident direct-beam energy",
                        "value": 19.485571563862404,
                        "unit": "kWh"
                      },
                      {
                        "label": "Shadowed panel/sun samples",
                        "value": 0,
                        "unit": "samples"
                      },
                      {
                        "label": "Front-side incidence samples",
                        "value": 3,
                        "unit": "samples"
                      }
                    ],
                    "scalar_details": {
                      "panel_sample_count": 1,
                      "sun_sample_count": 4,
                      "triangle_count": 0,
                      "energy_wh": 19485.571563862402,
                      "occluder_algorithm": "Moller-Trumbore directional ray/triangle half-line"
                    },
                    "table_row_count": 4,
                    "result_sha256": "ddc7a36a1f125c87da414e1581dcb981242396f0596949820e858fdc2776a19f"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Incident direct-beam energy",
                        "value": 0,
                        "unit": "kWh"
                      },
                      {
                        "label": "Shadowed panel/sun samples",
                        "value": 1,
                        "unit": "samples"
                      },
                      {
                        "label": "Front-side incidence samples",
                        "value": 1,
                        "unit": "samples"
                      }
                    ],
                    "scalar_details": {
                      "panel_sample_count": 1,
                      "sun_sample_count": 1,
                      "triangle_count": 1,
                      "energy_wh": 0,
                      "occluder_algorithm": "Moller-Trumbore directional ray/triangle half-line"
                    },
                    "table_row_count": 1,
                    "result_sha256": "87bcd1ff4ca3fb8887c9372888f8d6f0ae29cacf8165289da928bd5e2d4a2bd1"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Incident direct-beam energy",
                        "value": 0,
                        "unit": "kWh"
                      },
                      {
                        "label": "Shadowed panel/sun samples",
                        "value": 0,
                        "unit": "samples"
                      },
                      {
                        "label": "Front-side incidence samples",
                        "value": 0,
                        "unit": "samples"
                      }
                    ],
                    "scalar_details": {
                      "panel_sample_count": 1,
                      "sun_sample_count": 1,
                      "triangle_count": 0,
                      "energy_wh": 0,
                      "occluder_algorithm": "Moller-Trumbore directional ray/triangle half-line"
                    },
                    "table_row_count": 1,
                    "result_sha256": "7e95db48736b3fe71506601c855392ed55cc1271bd6a552f80001e55d993f3bd"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Incident direct-beam energy",
                        "value": 0,
                        "unit": "kWh"
                      },
                      {
                        "label": "Shadowed panel/sun samples",
                        "value": 0,
                        "unit": "samples"
                      },
                      {
                        "label": "Front-side incidence samples",
                        "value": 0,
                        "unit": "samples"
                      }
                    ],
                    "scalar_details": {
                      "panel_sample_count": 1,
                      "sun_sample_count": 1,
                      "triangle_count": 0,
                      "energy_wh": 0,
                      "occluder_algorithm": "Moller-Trumbore directional ray/triangle half-line"
                    },
                    "table_row_count": 1,
                    "result_sha256": "7e95db48736b3fe71506601c855392ed55cc1271bd6a552f80001e55d993f3bd"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Incident direct-beam energy",
                        "value": 8,
                        "unit": "kWh"
                      },
                      {
                        "label": "Shadowed panel/sun samples",
                        "value": 0,
                        "unit": "samples"
                      },
                      {
                        "label": "Front-side incidence samples",
                        "value": 1,
                        "unit": "samples"
                      }
                    ],
                    "scalar_details": {
                      "panel_sample_count": 1,
                      "sun_sample_count": 1,
                      "triangle_count": 0,
                      "energy_wh": 8000,
                      "occluder_algorithm": "Moller-Trumbore directional ray/triangle half-line"
                    },
                    "table_row_count": 1,
                    "result_sha256": "4c84651b315236bfb8a3700ca8fab0a18c8ad18f1a5daf476cb7abc032743e95"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Incident direct-beam energy",
                    "value": 19.485571563862404,
                    "unit": "kWh"
                  },
                  {
                    "label": "Shadowed panel/sun samples",
                    "value": 0,
                    "unit": "samples"
                  },
                  {
                    "label": "Front-side incidence samples",
                    "value": 3,
                    "unit": "samples"
                  }
                ],
                "default_result_sha256": "6c926f8cc64f61d450e542a169067f09bed5bf06a859a1032b052417b481937b"
              },
              "units": "kWh / samples",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 3
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "sun_vectors=valid prefix length 1",
                "metric_snapshot": [
                  {
                    "label": "Incident direct-beam energy",
                    "value": 6.062177819868303,
                    "unit": "kWh"
                  },
                  {
                    "label": "Shadowed panel/sun samples",
                    "value": 0,
                    "unit": "samples"
                  },
                  {
                    "label": "Front-side incidence samples",
                    "value": 1,
                    "unit": "samples"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "6c926f8cc64f61d450e542a169067f09bed5bf06a859a1032b052417b481937b"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/physical_studies.py",
              "engine_sha256": "7414df30acfea75c77b1a4ed0e88c7b2b4ea9b32644f8f753a38de145bb0342a",
              "test_file": "apps/api/tests/test_physical_studies.py",
              "test_sha256": "734ebf9b04f0c67f9c95a5e3b4e1f1d85db5e6d7768356bbd99121acd1ccf4f6",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Directional direct-beam only; no diffuse sky, ground reflection, spectral response, horizon/weather series or panel electrical mismatch.",
              "Triangles and panel samples are user-supplied local Cartesian geometry; results depend on mesh completeness and coordinate quality.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-pv-raytrace"
    },
    {
      "id": "pv-setbacks",
      "title": "Parcel setbacks and crop-light screen",
      "summary": "Apply planar polygon setbacks to multipolygons with holes, then expose a clearly uncalibrated PAR-to-light-response screen.",
      "workspace": {
        "id": "pv",
        "name": "PV economics",
        "appRoute": "#pv"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "geometry",
        "equations": [
          "usable geometry = parcel geometry buffered inward by setback distance",
          "crop DLI = baseline PAR·[1 − cover·(1 − module transmission)]",
          "illustrative response = Pmax·DLI/(DLI + half-saturation)."
        ],
        "limitations": [
          "Planar Shapely geometry in user-supplied metres; no georeferencing, survey, legal boundary, easement or access-road interpretation.",
          "Crop-light response is a generic saturating curve, not species-, cultivar-, weather- or farm-calibrated agronomy."
        ],
        "sources": [
          {
            "title": "Shapely 2.1 geometry and polygon buffer manual",
            "url": "https://shapely.readthedocs.io/en/stable/manual.html"
          },
          {
            "title": "NREL System Advisor Model shading scope",
            "url": "https://samrepo.nlr.gov/help/pv_shading.html"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "setback_m",
            "label": "Inward setback",
            "default": 2,
            "min": 0,
            "max": 100,
            "step": 0.5,
            "unit": "m"
          },
          {
            "name": "pv_cover_fraction",
            "label": "Crop area under PV cover",
            "default": 0.3,
            "min": 0,
            "max": 0.9,
            "step": 0.01,
            "unit": ""
          },
          {
            "name": "module_par_transmission_fraction",
            "label": "PAR transmitted beneath modules",
            "default": 0.1,
            "min": 0,
            "max": 0.8,
            "step": 0.01,
            "unit": ""
          },
          {
            "name": "baseline_par_mol_m2_day",
            "label": "Baseline daily PAR",
            "default": 35,
            "min": 0,
            "max": 100,
            "step": 1,
            "unit": "mol/m²/day"
          },
          {
            "name": "light_half_saturation_mol_m2_day",
            "label": "Illustrative light half-saturation",
            "default": 12,
            "min": 0.1,
            "max": 100,
            "step": 1,
            "unit": "mol/m²/day"
          },
          {
            "name": "photosynthesis_max_g_m2_day",
            "label": "Illustrative maximum response",
            "default": 20,
            "min": 0,
            "max": 1000,
            "step": 1,
            "unit": "g/m²/day"
          }
        ],
        "jsonDataFields": [
          {
            "name": "parcels",
            "label": "Planar parcels with optional interior holes",
            "default": {
              "type": "array",
              "items": 1,
              "itemTypes": [
                "object"
              ],
              "sha256": "c5e58b43e49cc7f4a68662dfa3a2b06ef34304c34e0f692b76a522d421181d37"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "4215dcbec657d1eb390085f90fc33b89204a95eaae472437cce5b04d77933933",
        "evidenceSha256": "46bcd9470a1a7c831ff8b78d5a8f44c0fd6e697fb9da7accb0438e0536e4cbf9",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-setbacks\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-setbacks\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Two parcel exteriors total 125 m², less a 4 m² hole gives 121 m²; 1 m setback leaves 57 m² usable.",
          "testIds": [
            {
              "node_id": "tests/test_physical_studies.py::test_polygon_setback_preserves_multipolygon_holes_and_crop_light_tradeoff",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "setback_m",
              "unit": "m",
              "minimum": 0,
              "maximum": 100,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "pv_cover_fraction",
              "unit": null,
              "minimum": 0,
              "maximum": 0.9,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "module_par_transmission_fraction",
              "unit": null,
              "minimum": 0,
              "maximum": 0.8,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "baseline_par_mol_m2_day",
              "unit": "mol/m²/day",
              "minimum": 0,
              "maximum": 100,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "light_half_saturation_mol_m2_day",
              "unit": "mol/m²/day",
              "minimum": 0.1,
              "maximum": 100,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "photosynthesis_max_g_m2_day",
              "unit": "g/m²/day",
              "minimum": 0,
              "maximum": 1000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "parcels",
              "label": "Planar parcels with optional interior holes",
              "default": {
                "type": "array",
                "items": 1,
                "itemTypes": [
                  "object"
                ],
                "sha256": "c5e58b43e49cc7f4a68662dfa3a2b06ef34304c34e0f692b76a522d421181d37"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/physical_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "4215dcbec657d1eb390085f90fc33b89204a95eaae472437cce5b04d77933933",
          "evidenceSha256": "46bcd9470a1a7c831ff8b78d5a8f44c0fd6e697fb9da7accb0438e0536e4cbf9",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-setbacks\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pv-setbacks\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Two parcel exteriors total 125 m², less a 4 m² hole gives 121 m²; 1 m setback leaves 57 m² usable.",
            "testIds": [
              {
                "node_id": "tests/test_physical_studies.py::test_polygon_setback_preserves_multipolygon_holes_and_crop_light_tradeoff",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "setback_m below declared minimum 0",
            "setback_m above declared maximum 100",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_physical_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "setback_m=0 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Parcel area",
                  "value": 6000,
                  "unit": "m²"
                },
                {
                  "label": "Usable area after setback",
                  "value": 6000,
                  "unit": "m²"
                },
                {
                  "label": "Usable land",
                  "value": 0.6,
                  "unit": "ha"
                },
                {
                  "label": "Estimated crop DLI",
                  "value": 25.55,
                  "unit": "mol/m²/day"
                },
                {
                  "label": "Illustrative crop light response",
                  "value": 13.608521970705727,
                  "unit": "g/m²/day"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "2d7d1570b49f24a175f9045d418f0792587eafc2c965fed56d2be8aa434ac4bd"
          }
        },
        "limitations": [
          "Planar Shapely geometry in user-supplied metres; no georeferencing, survey, legal boundary, easement or access-road interpretation.",
          "Crop-light response is a generic saturating curve, not species-, cultivar-, weather- or farm-calibrated agronomy."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/physical_studies.py",
          "tests": [
            "apps/api/tests/test_physical_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "pv-setbacks",
            "workspace": "pv",
            "title": "Parcel setbacks and crop-light screen",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_physical_studies.py::test_polygon_setback_preserves_multipolygon_holes_and_crop_light_tradeoff",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Two parcel exteriors total 125 m², less a 4 m² hole gives 121 m²; 1 m setback leaves 57 m² usable.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Parcel area",
                        "value": 121,
                        "unit": "m²"
                      },
                      {
                        "label": "Usable area after setback",
                        "value": 57,
                        "unit": "m²"
                      },
                      {
                        "label": "Usable land",
                        "value": 0.0057,
                        "unit": "ha"
                      },
                      {
                        "label": "Estimated crop DLI",
                        "value": 25.55,
                        "unit": "mol/m²/day"
                      },
                      {
                        "label": "Illustrative crop light response",
                        "value": 13.608521970705727,
                        "unit": "g/m²/day"
                      }
                    ],
                    "scalar_details": {
                      "setback_m": 1,
                      "usable_geometry_type": "MultiPolygon",
                      "usable_component_count": 2,
                      "holes_preserved": 1,
                      "pv_cover_fraction": 0.3,
                      "module_par_transmission_fraction": 0.1,
                      "crop_dli_mol_m2_day": 25.55,
                      "photosynthesis_response_g_m2_day": 13.608521970705727,
                      "geometry_library": "Shapely planar polygon buffer"
                    },
                    "table_row_count": 2,
                    "result_sha256": "5ba3c5b41bd1d31a0285c7f63cea84c442d0999d0a76757d3df1e523a9f04b7d"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Parcel area",
                        "value": 121,
                        "unit": "m²"
                      },
                      {
                        "label": "Usable area after setback",
                        "value": 57,
                        "unit": "m²"
                      },
                      {
                        "label": "Usable land",
                        "value": 0.0057,
                        "unit": "ha"
                      },
                      {
                        "label": "Estimated crop DLI",
                        "value": 35,
                        "unit": "mol/m²/day"
                      },
                      {
                        "label": "Illustrative crop light response",
                        "value": 14.893617021276595,
                        "unit": "g/m²/day"
                      }
                    ],
                    "scalar_details": {
                      "setback_m": 1,
                      "usable_geometry_type": "MultiPolygon",
                      "usable_component_count": 2,
                      "holes_preserved": 1,
                      "pv_cover_fraction": 0,
                      "module_par_transmission_fraction": 0.1,
                      "crop_dli_mol_m2_day": 35,
                      "photosynthesis_response_g_m2_day": 14.893617021276595,
                      "geometry_library": "Shapely planar polygon buffer"
                    },
                    "table_row_count": 2,
                    "result_sha256": "9c088e8eb86b78dde22125a9ed8d07f7eec098fd4cfae32157a22cbb56d1eb21"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Parcel area",
                    "value": 6000,
                    "unit": "m²"
                  },
                  {
                    "label": "Usable area after setback",
                    "value": 5376,
                    "unit": "m²"
                  },
                  {
                    "label": "Usable land",
                    "value": 0.5376,
                    "unit": "ha"
                  },
                  {
                    "label": "Estimated crop DLI",
                    "value": 25.55,
                    "unit": "mol/m²/day"
                  },
                  {
                    "label": "Illustrative crop light response",
                    "value": 13.608521970705727,
                    "unit": "g/m²/day"
                  }
                ],
                "default_result_sha256": "2d7d1570b49f24a175f9045d418f0792587eafc2c965fed56d2be8aa434ac4bd"
              },
              "units": "m² / ha / mol/m²/day",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 5
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "setback_m=0 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Parcel area",
                    "value": 6000,
                    "unit": "m²"
                  },
                  {
                    "label": "Usable area after setback",
                    "value": 6000,
                    "unit": "m²"
                  },
                  {
                    "label": "Usable land",
                    "value": 0.6,
                    "unit": "ha"
                  },
                  {
                    "label": "Estimated crop DLI",
                    "value": 25.55,
                    "unit": "mol/m²/day"
                  },
                  {
                    "label": "Illustrative crop light response",
                    "value": 13.608521970705727,
                    "unit": "g/m²/day"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "2d7d1570b49f24a175f9045d418f0792587eafc2c965fed56d2be8aa434ac4bd"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/physical_studies.py",
              "engine_sha256": "7414df30acfea75c77b1a4ed0e88c7b2b4ea9b32644f8f753a38de145bb0342a",
              "test_file": "apps/api/tests/test_physical_studies.py",
              "test_sha256": "734ebf9b04f0c67f9c95a5e3b4e1f1d85db5e6d7768356bbd99121acd1ccf4f6",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Planar Shapely geometry in user-supplied metres; no georeferencing, survey, legal boundary, easement or access-road interpretation.",
              "Crop-light response is a generic saturating curve, not species-, cultivar-, weather- or farm-calibrated agronomy.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-pv-setbacks"
    },
    {
      "id": "research-bearing",
      "title": "Bearing spectrum envelope comparison",
      "summary": "Build a robust per-frequency spectrum envelope from training records and flag threshold exceedances in held-out vibration records.",
      "workspace": {
        "id": "research",
        "name": "Research pillars",
        "appRoute": "#research"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "spectrum",
        "equations": [
          "one-sided amplitude spectrum = |rFFT((x−mean(x))·Hann)|·2/sum(Hann)",
          "per-bin envelope = median(training) + threshold·max(1.4826·MAD, 5% of median, floor)",
          "held-out record is flagged if any spectral bin exceeds its baseline envelope; maximum_envelope_exceedance_sigma is max((amplitude−envelope)/scale) over exceeded bins, or zero when no bin exceeds."
        ],
        "limitations": [
          "The fixture is synthetic; sampling, sensor mounting and training records determine the envelope.",
          "A spectral threshold exceedance is an anomaly screen only. It does not identify a bearing fault, root cause, severity or remaining life."
        ],
        "sources": [
          {
            "title": "SciPy real FFT reference",
            "url": "https://docs.scipy.org/doc/scipy/reference/generated/scipy.fft.rfft.html"
          },
          {
            "title": "ISO 13373-1 condition monitoring and diagnostics of machines",
            "url": "https://www.iso.org/standard/21832.html"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "sample_rate_hz",
            "label": "Sample rate",
            "default": 256,
            "min": 64,
            "max": 20000,
            "step": 1,
            "unit": "Hz"
          },
          {
            "name": "threshold_sigma",
            "label": "Robust envelope threshold",
            "default": 4,
            "min": 1,
            "max": 20,
            "step": 0.5,
            "unit": "scaled MAD"
          }
        ],
        "jsonDataFields": [
          {
            "name": "training_records",
            "label": "Baseline vibration records",
            "default": {
              "type": "array",
              "items": 6,
              "itemTypes": [
                "object"
              ],
              "sha256": "39afa40f4ed35aff229249f6337a8d0814a4c89452e1a4a07fe859dcd756d45f"
            }
          },
          {
            "name": "heldout_records",
            "label": "Held-out vibration records",
            "default": {
              "type": "array",
              "items": 2,
              "itemTypes": [
                "object"
              ],
              "sha256": "987a1e9e3d737f65484c604c59ed4e2e69612e38681662f5dbcd7e43a9fbb8cc"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "64a8f7bd96a1bd58e64532c0787e609a38d8b244218fcf177790f11674789934",
        "evidenceSha256": "42c23e10b2a4dc7898ba9b4ea4f7a349c880bbadf7d1fc1c74acb2140ab46cdd",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"research-bearing\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"research-bearing\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "A synthetic 62 Hz held-out component exceeds the training spectrum envelope while the nominal trace is not flagged.",
          "testIds": [
            {
              "node_id": "tests/test_physical_studies.py::test_bearing_spectrum_envelope_flags_heldout_frequency_outlier_without_diagnosis",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "sample_rate_hz",
              "unit": "Hz",
              "minimum": 64,
              "maximum": 20000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "threshold_sigma",
              "unit": "scaled MAD",
              "minimum": 1,
              "maximum": 20,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "training_records",
              "label": "Baseline vibration records",
              "default": {
                "type": "array",
                "items": 6,
                "itemTypes": [
                  "object"
                ],
                "sha256": "39afa40f4ed35aff229249f6337a8d0814a4c89452e1a4a07fe859dcd756d45f"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "heldout_records",
              "label": "Held-out vibration records",
              "default": {
                "type": "array",
                "items": 2,
                "itemTypes": [
                  "object"
                ],
                "sha256": "987a1e9e3d737f65484c604c59ed4e2e69612e38681662f5dbcd7e43a9fbb8cc"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/physical_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "64a8f7bd96a1bd58e64532c0787e609a38d8b244218fcf177790f11674789934",
          "evidenceSha256": "42c23e10b2a4dc7898ba9b4ea4f7a349c880bbadf7d1fc1c74acb2140ab46cdd",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"research-bearing\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"research-bearing\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "A synthetic 62 Hz held-out component exceeds the training spectrum envelope while the nominal trace is not flagged.",
            "testIds": [
              {
                "node_id": "tests/test_physical_studies.py::test_bearing_spectrum_envelope_flags_heldout_frequency_outlier_without_diagnosis",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "sample_rate_hz below declared minimum 64",
            "sample_rate_hz above declared maximum 20000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_physical_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "sample_rate_hz=64 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Held-out records flagged",
                  "value": 1,
                  "unit": "records"
                },
                {
                  "label": "Held-out records evaluated",
                  "value": 2,
                  "unit": "records"
                },
                {
                  "label": "Training records",
                  "value": 6,
                  "unit": "records"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "256f52c5128344de3543a23bee8aedfb610fe84c401e2bac114b72e5039032a6"
          }
        },
        "limitations": [
          "The fixture is synthetic; sampling, sensor mounting and training records determine the envelope.",
          "A spectral threshold exceedance is an anomaly screen only. It does not identify a bearing fault, root cause, severity or remaining life."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/physical_studies.py",
          "tests": [
            "apps/api/tests/test_physical_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "research-bearing",
            "workspace": "research",
            "title": "Bearing spectrum envelope comparison",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_physical_studies.py::test_bearing_spectrum_envelope_flags_heldout_frequency_outlier_without_diagnosis",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "A synthetic 62 Hz held-out component exceeds the training spectrum envelope while the nominal trace is not flagged.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Held-out records flagged",
                        "value": 1,
                        "unit": "records"
                      },
                      {
                        "label": "Held-out records evaluated",
                        "value": 2,
                        "unit": "records"
                      },
                      {
                        "label": "Training records",
                        "value": 6,
                        "unit": "records"
                      }
                    ],
                    "scalar_details": {
                      "threshold_sigma": 4,
                      "sample_rate_hz": 256,
                      "sample_count": 256
                    },
                    "table_row_count": 2,
                    "result_sha256": "7d7d5eb06a68825073d1de0b2ed0fe6e4e76b34e97945500b131f3877bb21c4d"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Held-out records flagged",
                    "value": 1,
                    "unit": "records"
                  },
                  {
                    "label": "Held-out records evaluated",
                    "value": 2,
                    "unit": "records"
                  },
                  {
                    "label": "Training records",
                    "value": 6,
                    "unit": "records"
                  }
                ],
                "default_result_sha256": "256f52c5128344de3543a23bee8aedfb610fe84c401e2bac114b72e5039032a6"
              },
              "units": "records / sigma",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 3
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "sample_rate_hz=64 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Held-out records flagged",
                    "value": 1,
                    "unit": "records"
                  },
                  {
                    "label": "Held-out records evaluated",
                    "value": 2,
                    "unit": "records"
                  },
                  {
                    "label": "Training records",
                    "value": 6,
                    "unit": "records"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "256f52c5128344de3543a23bee8aedfb610fe84c401e2bac114b72e5039032a6"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/physical_studies.py",
              "engine_sha256": "7414df30acfea75c77b1a4ed0e88c7b2b4ea9b32644f8f753a38de145bb0342a",
              "test_file": "apps/api/tests/test_physical_studies.py",
              "test_sha256": "734ebf9b04f0c67f9c95a5e3b4e1f1d85db5e6d7768356bbd99121acd1ccf4f6",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "The fixture is synthetic; sampling, sensor mounting and training records determine the envelope.",
              "A spectral threshold exceedance is an anomaly screen only. It does not identify a bearing fault, root cause, severity or remaining life.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-research-bearing"
    },
    {
      "id": "storage-annualopt",
      "title": "Full-year storage optimization",
      "summary": "Optimize 8,760 hourly intervals with battery direction, SOC, grid limits, and twelve monthly demand peaks.",
      "workspace": {
        "id": "storage",
        "name": "Storage & flexibility",
        "appRoute": "#storage"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "dispatch",
        "equations": [
          "SOC[t+1] = SOC[t] + ηcharge·charge[t] - discharge[t]/ηdischarge",
          "grid import + PV - curtailment + discharge = load + charge + grid export",
          "annual objective = hourly energy/wear costs + Σmonth demand_charge × monthly peak import",
          "SOC[0] = SOC[8760] = supplied initial SOC"
        ],
        "limitations": [
          "Non-leap year with equal one-hour intervals; 24-point tariffs repeat by hour of day.",
          "Import price must strictly exceed export price each hour; simultaneous grid import/export is then dominated.",
          "Synthetic defaults are not a meter record, billing tariff, reserve product, degradation model, or investment case.",
          "A time-limit, infeasible, or non-optimal HiGHS result is rejected; the returned schedule is only a proven optimum."
        ],
        "sources": [
          {
            "title": "SciPy mixed-integer linear programming (HiGHS) reference",
            "url": "https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.milp.html"
          },
          {
            "title": "NREL: Storage Futures Study—Economic Potential of Four-Hour Storage",
            "url": "https://www.nrel.gov/docs/fy21osti/77449.pdf"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "capacity_kwh",
            "label": "Battery capacity",
            "default": 600,
            "min": 0,
            "max": 10000000,
            "step": 50,
            "unit": "kWh"
          },
          {
            "name": "power_kw",
            "label": "Charge/discharge power",
            "default": 150,
            "min": 0,
            "max": 100000,
            "step": 10,
            "unit": "kW"
          },
          {
            "name": "round_trip_efficiency",
            "label": "Round-trip efficiency",
            "default": 0.9,
            "min": 0.01,
            "max": 1,
            "step": 0.01,
            "unit": "fraction"
          },
          {
            "name": "monthly_demand_charge_eur_kw",
            "label": "Monthly demand charge",
            "default": 0,
            "min": 0,
            "max": 100000,
            "step": 1,
            "unit": "EUR/kW-month"
          },
          {
            "name": "solver_time_limit_seconds",
            "label": "MILP time limit",
            "default": 4,
            "min": 0.25,
            "max": 10,
            "step": 0.25,
            "unit": "s"
          }
        ],
        "jsonDataFields": [
          {
            "name": "load_kw",
            "label": "Non-leap-year hourly load",
            "default": {
              "type": "array",
              "items": 8760,
              "itemTypes": [
                "number"
              ],
              "sha256": "19c38482774cc0362389aefac1a09847a72bb67013fef9792d167dbc36da7bc6"
            }
          },
          {
            "name": "pv_kw",
            "label": "Non-leap-year hourly PV",
            "default": {
              "type": "array",
              "items": 8760,
              "itemTypes": [
                "number"
              ],
              "sha256": "eb2eb582f84e5a5027853645ded6a2608c4d50b5e25a46883cdaa366983b81e4"
            }
          },
          {
            "name": "import_price_eur_kwh",
            "label": "Hourly import tariff (24 points or annual)",
            "default": {
              "type": "array",
              "items": 24,
              "itemTypes": [
                "number"
              ],
              "sha256": "568a0ae914630d1131e4b90f41c350ef18d217f0789ad7583e31e3d9e2885834"
            }
          },
          {
            "name": "export_price_eur_kwh",
            "label": "Hourly export tariff (24 points or annual)",
            "default": {
              "type": "array",
              "items": 24,
              "itemTypes": [
                "number"
              ],
              "sha256": "e871ea33962c8f9b5c9d50afd403cdcfa448e8b061866e5fd54b0876e57b16af"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "6d4028334ec7520fad95532938f96b2cf422dfec43ea3b0a3b45be09d5b7300e",
        "evidenceSha256": "ce23df9c5d2081bc61c238a338aad8d570e8e317b3c25c6a9f2698db90205900",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-annualopt\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-annualopt\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "With 80 kW residual load, zero storage power, 0.10 EUR/kWh and 2 EUR/kW-month, annual cost is 72,000 EUR.",
          "testIds": [
            {
              "node_id": "tests/test_planning_studies.py::test_annual_storage_matches_hand_calculated_energy_and_monthly_peak_cost",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "capacity_kwh",
              "unit": "kWh",
              "minimum": 0,
              "maximum": 10000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "power_kw",
              "unit": "kW",
              "minimum": 0,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "round_trip_efficiency",
              "unit": "fraction",
              "minimum": 0.01,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "monthly_demand_charge_eur_kw",
              "unit": "EUR/kW-month",
              "minimum": 0,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "solver_time_limit_seconds",
              "unit": "s",
              "minimum": 0.25,
              "maximum": 10,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "load_kw",
              "label": "Non-leap-year hourly load",
              "default": {
                "type": "array",
                "items": 8760,
                "itemTypes": [
                  "number"
                ],
                "sha256": "19c38482774cc0362389aefac1a09847a72bb67013fef9792d167dbc36da7bc6"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "pv_kw",
              "label": "Non-leap-year hourly PV",
              "default": {
                "type": "array",
                "items": 8760,
                "itemTypes": [
                  "number"
                ],
                "sha256": "eb2eb582f84e5a5027853645ded6a2608c4d50b5e25a46883cdaa366983b81e4"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "import_price_eur_kwh",
              "label": "Hourly import tariff (24 points or annual)",
              "default": {
                "type": "array",
                "items": 24,
                "itemTypes": [
                  "number"
                ],
                "sha256": "568a0ae914630d1131e4b90f41c350ef18d217f0789ad7583e31e3d9e2885834"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "export_price_eur_kwh",
              "label": "Hourly export tariff (24 points or annual)",
              "default": {
                "type": "array",
                "items": 24,
                "itemTypes": [
                  "number"
                ],
                "sha256": "e871ea33962c8f9b5c9d50afd403cdcfa448e8b061866e5fd54b0876e57b16af"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/planning_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "6d4028334ec7520fad95532938f96b2cf422dfec43ea3b0a3b45be09d5b7300e",
          "evidenceSha256": "ce23df9c5d2081bc61c238a338aad8d570e8e317b3c25c6a9f2698db90205900",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-annualopt\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"storage-annualopt\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "With 80 kW residual load, zero storage power, 0.10 EUR/kWh and 2 EUR/kW-month, annual cost is 72,000 EUR.",
            "testIds": [
              {
                "node_id": "tests/test_planning_studies.py::test_annual_storage_matches_hand_calculated_energy_and_monthly_peak_cost",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "capacity_kwh below declared minimum 0",
            "capacity_kwh above declared maximum 10000000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_planning_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "load_kw=zero vector at preserved length",
              "metric_snapshot": [
                {
                  "label": "Optimal annual objective",
                  "value": -8650.046159999984,
                  "unit": "EUR"
                },
                {
                  "label": "No-battery baseline objective",
                  "value": -8650.04616,
                  "unit": "EUR"
                },
                {
                  "label": "Objective change versus baseline",
                  "value": -1.6370904631912708e-11,
                  "unit": "EUR"
                },
                {
                  "label": "Battery charge energy",
                  "value": 0,
                  "unit": "kWh"
                },
                {
                  "label": "Battery discharge energy",
                  "value": 0,
                  "unit": "kWh"
                },
                {
                  "label": "PV curtailment",
                  "value": 0,
                  "unit": "kWh"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {
            "solver_status": "optimal",
            "solver_status_code": 0
          },
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "PASS",
          "actual": "optimal"
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "b3e719bbc56ccec499c4bc40b2dfca43305c9913d64e1f7b42f0affea91f8f32"
          }
        },
        "limitations": [
          "Non-leap year with equal one-hour intervals; 24-point tariffs repeat by hour of day.",
          "Import price must strictly exceed export price each hour; simultaneous grid import/export is then dominated.",
          "Synthetic defaults are not a meter record, billing tariff, reserve product, degradation model, or investment case.",
          "A time-limit, infeasible, or non-optimal HiGHS result is rejected; the returned schedule is only a proven optimum."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/planning_studies.py",
          "tests": [
            "apps/api/tests/test_planning_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "storage-annualopt",
            "workspace": "storage",
            "title": "Full-year storage optimization",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_planning_studies.py::test_annual_storage_matches_hand_calculated_energy_and_monthly_peak_cost",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "With 80 kW residual load, zero storage power, 0.10 EUR/kWh and 2 EUR/kW-month, annual cost is 72,000 EUR.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Optimal annual objective",
                        "value": 72000,
                        "unit": "EUR"
                      },
                      {
                        "label": "No-battery baseline objective",
                        "value": 72000,
                        "unit": "EUR"
                      },
                      {
                        "label": "Objective change versus baseline",
                        "value": 0,
                        "unit": "EUR"
                      },
                      {
                        "label": "Battery charge energy",
                        "value": 0,
                        "unit": "kWh"
                      },
                      {
                        "label": "Battery discharge energy",
                        "value": 0,
                        "unit": "kWh"
                      },
                      {
                        "label": "PV curtailment",
                        "value": 0,
                        "unit": "kWh"
                      }
                    ],
                    "scalar_details": {
                      "interval_count": 8760,
                      "horizon_hours": 8760,
                      "initial_soc_kwh": 300,
                      "terminal_soc_kwh": 300,
                      "minimum_soc_kwh": 300,
                      "maximum_soc_kwh": 300,
                      "maximum_monthly_peak_import_kw": 80,
                      "solver": "SciPy milp / HiGHS",
                      "solver_status": "optimal",
                      "solver_status_code": 0,
                      "solver_message": "Optimization terminated successfully. (HiGHS Status 7: Optimal)",
                      "relative_mip_gap": 0,
                      "solver_runtime_seconds": 0.21283340000081807,
                      "solver_time_limit_seconds": 4,
                      "mip_node_count": null,
                      "max_balance_residual_kw": 0,
                      "max_soc_residual_kwh": 0,
                      "max_simultaneous_charge_discharge_kw": 0,
                      "annual_energy_cost_eur": 70080,
                      "annual_demand_charge_eur": 1920,
                      "baseline_curtailment_kwh": 0,
                      "tariff_expansion": "24-hour vectors repeat for all 365 days; annual vectors are used as supplied",
                      "grid_direction": "No grid binary required because import tariff strictly exceeds export tariff in every hour; simultaneous flow is strictly dominated",
                      "battery_direction": "strict-cost dominance proof: with nonnegative tariffs, positive export value or positive discharge wear makes every same-hour lossy cycle more costly; no integer direction variables needed",
                      "binary_direction_required": false,
                      "integer_variable_count": 0,
                      "baseline_method": "exact analytical no-battery dispatch with identical energy, grid, PV-curtailment, and monthly-peak constraints"
                    },
                    "table_row_count": 8760,
                    "result_sha256": "63a7fb4b1c400770db472229f0472bf481de25f3117d65e9a838720389f13dde"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Optimal annual objective",
                    "value": 247361.86152000123,
                    "unit": "EUR"
                  },
                  {
                    "label": "No-battery baseline objective",
                    "value": 247361.86151999998,
                    "unit": "EUR"
                  },
                  {
                    "label": "Objective change versus baseline",
                    "value": -1.2514647096395493e-9,
                    "unit": "EUR"
                  },
                  {
                    "label": "Battery charge energy",
                    "value": 0,
                    "unit": "kWh"
                  },
                  {
                    "label": "Battery discharge energy",
                    "value": 0,
                    "unit": "kWh"
                  },
                  {
                    "label": "PV curtailment",
                    "value": 0,
                    "unit": "kWh"
                  }
                ],
                "default_result_sha256": "b3e719bbc56ccec499c4bc40b2dfca43305c9913d64e1f7b42f0affea91f8f32"
              },
              "units": "EUR / kW",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 6
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "load_kw=zero vector at preserved length",
                "metric_snapshot": [
                  {
                    "label": "Optimal annual objective",
                    "value": -8650.046159999984,
                    "unit": "EUR"
                  },
                  {
                    "label": "No-battery baseline objective",
                    "value": -8650.04616,
                    "unit": "EUR"
                  },
                  {
                    "label": "Objective change versus baseline",
                    "value": -1.6370904631912708e-11,
                    "unit": "EUR"
                  },
                  {
                    "label": "Battery charge energy",
                    "value": 0,
                    "unit": "kWh"
                  },
                  {
                    "label": "Battery discharge energy",
                    "value": 0,
                    "unit": "kWh"
                  },
                  {
                    "label": "PV curtailment",
                    "value": 0,
                    "unit": "kWh"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "b3e719bbc56ccec499c4bc40b2dfca43305c9913d64e1f7b42f0affea91f8f32"
              },
              "solver_feasibility": {
                "status": "PASS",
                "actual": "optimal"
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/planning_studies.py",
              "engine_sha256": "5334bb9e0dec5acbc4b57b10ca675d2157bdc89f0fb0620e903b676184d8376c",
              "test_file": "apps/api/tests/test_planning_studies.py",
              "test_sha256": "3e5e2eccbc88a1d0eff85a8f1d40dbef5fd6bcb203645b5ab2b185be796ce4ce",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Non-leap year with equal one-hour intervals; 24-point tariffs repeat by hour of day.",
              "Import price must strictly exceed export price each hour; simultaneous grid import/export is then dominated.",
              "Synthetic defaults are not a meter record, billing tariff, reserve product, degradation model, or investment case.",
              "A time-limit, infeasible, or non-optimal HiGHS result is rejected; the returned schedule is only a proven optimum.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-storage-annualopt"
    },
    {
      "id": "compare-reliability",
      "title": "Chronological reliability ensemble",
      "summary": "Seeded Monte Carlo samples weather variation and generator failures while preserving battery chronology.",
      "workspace": {
        "id": "compare",
        "name": "Scenario compare",
        "appRoute": "#compare"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "reliability",
        "equations": [
          "LOLE[horizon] = mean over trials of hours with unserved load",
          "EENS[horizon] = mean over trials of Σ unserved kWh",
          "annualized metric = horizon expectation × annualization_hours / horizon_hours",
          "SOC evolves once across each sampled chronology; it is initialized once per trial"
        ],
        "limitations": [
          "Weather is a mean-one lognormal AR(1) multiplier; a single thermal unit follows a geometric failure/repair process.",
          "Annualization assumes the supplied horizon represents a typical year; it does not stitch repeated horizons or certify a resource adequacy standard.",
          "Two-sided 95% Hoeffding intervals use independent bounded trial-level annualized outcomes and can be wide, especially with few or zero observed loss events; no weather-year sampling or network adequacy model is included."
        ],
        "sources": [
          {
            "title": "NREL Advanced Guide to Understanding Power System Model Results for Long-Term Resource Plans",
            "url": "https://docs.nrel.gov/docs/fy24osti/88337.pdf"
          },
          {
            "title": "Hoeffding: Probability Inequalities for Sums of Bounded Random Variables",
            "url": "https://doi.org/10.1080/01621459.1963.10500830"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "trials",
            "label": "Independent ensemble members",
            "default": 250,
            "min": 20,
            "max": 5000,
            "step": 10,
            "unit": "trials"
          },
          {
            "name": "weather_cv",
            "label": "Weather coefficient of variation",
            "default": 0.2,
            "min": 0,
            "max": 1,
            "step": 0.05,
            "unit": "fraction"
          },
          {
            "name": "battery_capacity_kwh",
            "label": "Battery capacity",
            "default": 80,
            "min": 0,
            "max": 10000000,
            "step": 10,
            "unit": "kWh"
          },
          {
            "name": "battery_power_kw",
            "label": "Battery power",
            "default": 40,
            "min": 0,
            "max": 100000,
            "step": 5,
            "unit": "kW"
          },
          {
            "name": "generator_failure_probability_per_hour",
            "label": "Generator failure probability",
            "default": 0.002,
            "min": 0,
            "max": 1,
            "step": 0.001,
            "unit": "per hour"
          },
          {
            "name": "generator_repair_probability_per_hour",
            "label": "Generator repair probability",
            "default": 0.08,
            "min": 0.001,
            "max": 1,
            "step": 0.01,
            "unit": "per hour"
          },
          {
            "name": "annualization_hours",
            "label": "Reporting year hours",
            "default": 8760,
            "min": 8760,
            "max": 8784,
            "step": 24,
            "unit": "hours"
          }
        ],
        "jsonDataFields": [
          {
            "name": "load_kw",
            "label": "Hourly demand",
            "default": {
              "type": "array",
              "items": 168,
              "itemTypes": [
                "number"
              ],
              "sha256": "6ae304a67cfff92d8d9bbffb9bb0b7a90b2870dd5356db19eeb06d8ebaaa45d4"
            }
          },
          {
            "name": "renewable_kw",
            "label": "Hourly weather-dependent renewable output",
            "default": {
              "type": "array",
              "items": 168,
              "itemTypes": [
                "number"
              ],
              "sha256": "e7c980528c1c0b49e0e7f6679ecb9b60b12c21b65686ea19a4627e9e4713598f"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "dcd927302399abfbfb70039b8f658f8065f372e8b8515ac572ef2720af755509",
        "evidenceSha256": "3443d2200bea3e467b6da7795c279ad33f7023d268bf56990a1e6032b5be4881",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"compare-reliability\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"compare-reliability\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "For stationary failure 1e-6 and repair 0.08, analytic outage=failure/(failure+repair) is contained in reported annualized 95% LOLE/EENS intervals.",
          "testIds": [
            {
              "node_id": "tests/test_planning_studies.py::test_reliability_zero_observed_events_keeps_analytic_rare_event_inside_ci",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "trials",
              "unit": "trials",
              "minimum": 20,
              "maximum": 5000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "weather_cv",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "battery_capacity_kwh",
              "unit": "kWh",
              "minimum": 0,
              "maximum": 10000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "battery_power_kw",
              "unit": "kW",
              "minimum": 0,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "generator_failure_probability_per_hour",
              "unit": "per hour",
              "minimum": 0,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "generator_repair_probability_per_hour",
              "unit": "per hour",
              "minimum": 0.001,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "annualization_hours",
              "unit": "hours",
              "minimum": 8760,
              "maximum": 8784,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "load_kw",
              "label": "Hourly demand",
              "default": {
                "type": "array",
                "items": 168,
                "itemTypes": [
                  "number"
                ],
                "sha256": "6ae304a67cfff92d8d9bbffb9bb0b7a90b2870dd5356db19eeb06d8ebaaa45d4"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "renewable_kw",
              "label": "Hourly weather-dependent renewable output",
              "default": {
                "type": "array",
                "items": 168,
                "itemTypes": [
                  "number"
                ],
                "sha256": "e7c980528c1c0b49e0e7f6679ecb9b60b12c21b65686ea19a4627e9e4713598f"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/planning_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "dcd927302399abfbfb70039b8f658f8065f372e8b8515ac572ef2720af755509",
          "evidenceSha256": "3443d2200bea3e467b6da7795c279ad33f7023d268bf56990a1e6032b5be4881",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"compare-reliability\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"compare-reliability\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "For stationary failure 1e-6 and repair 0.08, analytic outage=failure/(failure+repair) is contained in reported annualized 95% LOLE/EENS intervals.",
            "testIds": [
              {
                "node_id": "tests/test_planning_studies.py::test_reliability_zero_observed_events_keeps_analytic_rare_event_inside_ci",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "trials below declared minimum 20",
            "trials above declared maximum 5000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_planning_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "trials=20 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Expected annualized loss-of-load hours",
                  "value": 7891.821428571429,
                  "unit": "h/year"
                },
                {
                  "label": "Expected annualized unserved energy",
                  "value": 393853.10170182085,
                  "unit": "kWh/year"
                },
                {
                  "label": "Ensemble members",
                  "value": 20,
                  "unit": "trials"
                },
                {
                  "label": "Expected terminal battery energy",
                  "value": 0,
                  "unit": "kWh"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "0a886c43d3dcc06fcd81ff213e5121a618e3a2a0c8e04cc4b694a18d7aedb5b6"
          }
        },
        "limitations": [
          "Weather is a mean-one lognormal AR(1) multiplier; a single thermal unit follows a geometric failure/repair process.",
          "Annualization assumes the supplied horizon represents a typical year; it does not stitch repeated horizons or certify a resource adequacy standard.",
          "Two-sided 95% Hoeffding intervals use independent bounded trial-level annualized outcomes and can be wide, especially with few or zero observed loss events; no weather-year sampling or network adequacy model is included."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/planning_studies.py",
          "tests": [
            "apps/api/tests/test_planning_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "compare-reliability",
            "workspace": "compare",
            "title": "Chronological reliability ensemble",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_planning_studies.py::test_reliability_zero_observed_events_keeps_analytic_rare_event_inside_ci",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "For stationary failure 1e-6 and repair 0.08, analytic outage=failure/(failure+repair) is contained in reported annualized 95% LOLE/EENS intervals.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Expected annualized loss-of-load hours",
                        "value": 0,
                        "unit": "h/year"
                      },
                      {
                        "label": "Expected annualized unserved energy",
                        "value": 0,
                        "unit": "kWh/year"
                      },
                      {
                        "label": "Ensemble members",
                        "value": 250,
                        "unit": "trials"
                      },
                      {
                        "label": "Expected terminal battery energy",
                        "value": 0,
                        "unit": "kWh"
                      }
                    ],
                    "scalar_details": {
                      "horizon_hours": 24,
                      "annualization_hours": 8760,
                      "annualization_factor": 365,
                      "trial_count": 250,
                      "seed": 42,
                      "lole_horizon_mean_hours": 0,
                      "lole_outcome_upper_bound_hours_per_year": 8760,
                      "eens_outcome_upper_bound_kwh_per_year": 87600,
                      "lole_event_trial_count": 0,
                      "eens_event_trial_count": 0,
                      "low_event_warning_threshold_trials": 30,
                      "low_event_warning_triggered": true,
                      "mean_weather_multiplier": 1,
                      "soc_resets_within_trial": false,
                      "soc_initialization": "supplied initial SOC once at the beginning of each independent trial; chronological transitions thereafter",
                      "generator_state_model": "one thermal unit with stationary initial availability and geometric hourly failure/repair transitions",
                      "confidence_interval": "two-sided 95% Hoeffding interval: sample mean +/- R*sqrt(log(2/0.05)/(2*n)), clipped to [0,R] for independent trial-level annualized outcomes with support [0,R]"
                    },
                    "table_row_count": 250,
                    "result_sha256": "0ee3f0640a4d4ac1e26ba52390c343aee9ff45d2905e006755610d8b9bb5c5b6"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Expected annualized loss-of-load hours",
                    "value": 7908.194285714287,
                    "unit": "h/year"
                  },
                  {
                    "label": "Expected annualized unserved energy",
                    "value": 398212.4930192901,
                    "unit": "kWh/year"
                  },
                  {
                    "label": "Ensemble members",
                    "value": 250,
                    "unit": "trials"
                  },
                  {
                    "label": "Expected terminal battery energy",
                    "value": 0,
                    "unit": "kWh"
                  }
                ],
                "default_result_sha256": "0a886c43d3dcc06fcd81ff213e5121a618e3a2a0c8e04cc4b694a18d7aedb5b6"
              },
              "units": "h/year / kWh/year",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 4
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "trials=20 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Expected annualized loss-of-load hours",
                    "value": 7891.821428571429,
                    "unit": "h/year"
                  },
                  {
                    "label": "Expected annualized unserved energy",
                    "value": 393853.10170182085,
                    "unit": "kWh/year"
                  },
                  {
                    "label": "Ensemble members",
                    "value": 20,
                    "unit": "trials"
                  },
                  {
                    "label": "Expected terminal battery energy",
                    "value": 0,
                    "unit": "kWh"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "0a886c43d3dcc06fcd81ff213e5121a618e3a2a0c8e04cc4b694a18d7aedb5b6"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/planning_studies.py",
              "engine_sha256": "5334bb9e0dec5acbc4b57b10ca675d2157bdc89f0fb0620e903b676184d8376c",
              "test_file": "apps/api/tests/test_planning_studies.py",
              "test_sha256": "3e5e2eccbc88a1d0eff85a8f1d40dbef5fd6bcb203645b5ab2b185be796ce4ce",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Weather is a mean-one lognormal AR(1) multiplier; a single thermal unit follows a geometric failure/repair process.",
              "Annualization assumes the supplied horizon represents a typical year; it does not stitch repeated horizons or certify a resource adequacy standard.",
              "Two-sided 95% Hoeffding intervals use independent bounded trial-level annualized outcomes and can be wide, especially with few or zero observed loss events; no weather-year sampling or network adequacy model is included.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-compare-reliability"
    },
    {
      "id": "forecast-cointegration",
      "title": "Rank-assumed energy-price VECM",
      "summary": "Fit a gas/carbon/power long-run relation and short-run error-correction dynamics, then hold out later observations.",
      "workspace": {
        "id": "forecast",
        "name": "Forecast studio",
        "appRoute": "#forecast"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "forecast",
        "equations": [
          "ECT[t] = power[t] - α - βgas·gas[t] - βcarbon·carbon[t]",
          "Δy[t] = c + loading·ECT[t-1] + Σ Γj·Δy[t-j] + ε[t]",
          "long-run relation and short-run VECM coefficients are fit on the first chronological segment only"
        ],
        "limitations": [
          "Rank one is an explicit assumption; no Johansen rank test, unit-root test, or seasonal adjustment is performed.",
          "This OLS error-correction screen is not a likelihood-based Johansen estimator or a calibrated commodity forecast.",
          "Synthetic examples do not substitute for timestamp-aligned, currency-consistent, real market observations."
        ],
        "sources": [
          {
            "title": "Johansen and Juselius (1990), likelihood inference on cointegration",
            "url": "https://doi.org/10.1111/j.1468-0084.1990.mp52002003.x"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "training_fraction",
            "label": "Chronological training fraction",
            "default": 0.75,
            "min": 0.5,
            "max": 0.9,
            "step": 0.05,
            "unit": "fraction"
          },
          {
            "name": "rank_assumption",
            "label": "Assumed cointegration rank",
            "default": 1,
            "min": 1,
            "max": 1,
            "step": 1,
            "unit": "rank"
          },
          {
            "name": "lag_count",
            "label": "Lagged differences",
            "default": 1,
            "min": 1,
            "max": 4,
            "step": 1,
            "unit": "lags"
          },
          {
            "name": "forecast_steps",
            "label": "Forecast path length",
            "default": 12,
            "min": 1,
            "max": 48,
            "step": 1,
            "unit": "observations"
          }
        ],
        "jsonDataFields": [
          {
            "name": "gas_price_eur_mwh",
            "label": "Observed gas prices",
            "default": {
              "type": "array",
              "items": 120,
              "itemTypes": [
                "number"
              ],
              "sha256": "e3896729257dbb65eddb30181de73230bf79f019447b61f462394504fd88ccdc"
            }
          },
          {
            "name": "carbon_price_eur_t",
            "label": "Observed carbon prices",
            "default": {
              "type": "array",
              "items": 120,
              "itemTypes": [
                "number"
              ],
              "sha256": "038f5d0f8147f90b5846115343f0b19d955b7dcfdd6847ce489315cd733c75be"
            }
          },
          {
            "name": "power_price_eur_mwh",
            "label": "Observed power prices",
            "default": {
              "type": "array",
              "items": 120,
              "itemTypes": [
                "number"
              ],
              "sha256": "95afc7d77563ec545a0b2bcc0c66472f45cd9c6c75826dde00b5253bf1ec0b44"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "7d4537493146637d22ad2b6e70ea84513aaca87189d84c444180f31954ae3c5c",
        "evidenceSha256": "b41a38367b9f62b444b127367732935a075e99c6e3e1fec7feedfa8c4d721b76",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"forecast-cointegration\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"forecast-cointegration\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "The generated 120-row case splits 90 training and 30 chronological holdout rows, keeps rank as an explicit assumption, and reports holdout errors.",
          "testIds": [
            {
              "node_id": "tests/test_planning_studies.py::test_cointegration_fit_uses_chronological_holdout_and_assumed_rank",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "training_fraction",
              "unit": "fraction",
              "minimum": 0.5,
              "maximum": 0.9,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "rank_assumption",
              "unit": "rank",
              "minimum": 1,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "lag_count",
              "unit": "lags",
              "minimum": 1,
              "maximum": 4,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "forecast_steps",
              "unit": "observations",
              "minimum": 1,
              "maximum": 48,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "gas_price_eur_mwh",
              "label": "Observed gas prices",
              "default": {
                "type": "array",
                "items": 120,
                "itemTypes": [
                  "number"
                ],
                "sha256": "e3896729257dbb65eddb30181de73230bf79f019447b61f462394504fd88ccdc"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "carbon_price_eur_t",
              "label": "Observed carbon prices",
              "default": {
                "type": "array",
                "items": 120,
                "itemTypes": [
                  "number"
                ],
                "sha256": "038f5d0f8147f90b5846115343f0b19d955b7dcfdd6847ce489315cd733c75be"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "power_price_eur_mwh",
              "label": "Observed power prices",
              "default": {
                "type": "array",
                "items": 120,
                "itemTypes": [
                  "number"
                ],
                "sha256": "95afc7d77563ec545a0b2bcc0c66472f45cd9c6c75826dde00b5253bf1ec0b44"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/planning_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "7d4537493146637d22ad2b6e70ea84513aaca87189d84c444180f31954ae3c5c",
          "evidenceSha256": "b41a38367b9f62b444b127367732935a075e99c6e3e1fec7feedfa8c4d721b76",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"forecast-cointegration\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"forecast-cointegration\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "The generated 120-row case splits 90 training and 30 chronological holdout rows, keeps rank as an explicit assumption, and reports holdout errors.",
            "testIds": [
              {
                "node_id": "tests/test_planning_studies.py::test_cointegration_fit_uses_chronological_holdout_and_assumed_rank",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "training_fraction below declared minimum 0.5",
            "training_fraction above declared maximum 0.9",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_planning_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "training_fraction=0.5 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Assumed cointegration rank",
                  "value": 1,
                  "unit": "rank"
                },
                {
                  "label": "In-sample VECM residual RMSE",
                  "value": 0.025522731163397688,
                  "unit": "price units"
                },
                {
                  "label": "Chronological holdout RMSE (power)",
                  "value": 5.195154728212449,
                  "unit": "EUR/MWh"
                },
                {
                  "label": "Chronological holdout RMSE (all series)",
                  "value": 10.636186265037422,
                  "unit": "series units"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {
            "validation_status": "assumed_rank_one_fit_with_chronological_holdout"
          },
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "7aaa3a2e35fa701251c3063135fb817ca76506815f568b8d0bb8d09db9d77d20"
          }
        },
        "limitations": [
          "Rank one is an explicit assumption; no Johansen rank test, unit-root test, or seasonal adjustment is performed.",
          "This OLS error-correction screen is not a likelihood-based Johansen estimator or a calibrated commodity forecast.",
          "Synthetic examples do not substitute for timestamp-aligned, currency-consistent, real market observations."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/planning_studies.py",
          "tests": [
            "apps/api/tests/test_planning_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "forecast-cointegration",
            "workspace": "forecast",
            "title": "Rank-assumed energy-price VECM",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_planning_studies.py::test_cointegration_fit_uses_chronological_holdout_and_assumed_rank",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "The generated 120-row case splits 90 training and 30 chronological holdout rows, keeps rank as an explicit assumption, and reports holdout errors.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Assumed cointegration rank",
                        "value": 1,
                        "unit": "rank"
                      },
                      {
                        "label": "In-sample VECM residual RMSE",
                        "value": 0.04161051582144162,
                        "unit": "price units"
                      },
                      {
                        "label": "Chronological holdout RMSE (power)",
                        "value": 7.725442875845391,
                        "unit": "EUR/MWh"
                      },
                      {
                        "label": "Chronological holdout RMSE (all series)",
                        "value": 6.114032646844579,
                        "unit": "series units"
                      }
                    ],
                    "scalar_details": {
                      "rank_assumption": 1,
                      "rank_test_performed": false,
                      "cointegration_stationarity_test_performed": false,
                      "validation_status": "assumed_rank_one_fit_with_chronological_holdout",
                      "training_observations": 90,
                      "holdout_observations": 30,
                      "lag_count": 1,
                      "short_run_lag_terms": 3,
                      "in_sample_vecm_rmse_all_series": 0.04161051582144162,
                      "holdout_rmse_all_series": 6.114032646844579,
                      "holdout_rmse_power_eur_mwh": 7.725442875845391,
                      "training_fraction": 0.75,
                      "fitted_on_holdout": false
                    },
                    "table_row_count": 12,
                    "result_sha256": "661b047c9701209361f3630ca37a36753753fa9e920c0330cb05ac86942dcc35"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Assumed cointegration rank",
                    "value": 1,
                    "unit": "rank"
                  },
                  {
                    "label": "In-sample VECM residual RMSE",
                    "value": 0.0423126857024091,
                    "unit": "price units"
                  },
                  {
                    "label": "Chronological holdout RMSE (power)",
                    "value": 9.240234316761534,
                    "unit": "EUR/MWh"
                  },
                  {
                    "label": "Chronological holdout RMSE (all series)",
                    "value": 12.849988094977316,
                    "unit": "series units"
                  }
                ],
                "default_result_sha256": "7aaa3a2e35fa701251c3063135fb817ca76506815f568b8d0bb8d09db9d77d20"
              },
              "units": "price units / observations / RMSE",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 4
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "training_fraction=0.5 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Assumed cointegration rank",
                    "value": 1,
                    "unit": "rank"
                  },
                  {
                    "label": "In-sample VECM residual RMSE",
                    "value": 0.025522731163397688,
                    "unit": "price units"
                  },
                  {
                    "label": "Chronological holdout RMSE (power)",
                    "value": 5.195154728212449,
                    "unit": "EUR/MWh"
                  },
                  {
                    "label": "Chronological holdout RMSE (all series)",
                    "value": 10.636186265037422,
                    "unit": "series units"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "7aaa3a2e35fa701251c3063135fb817ca76506815f568b8d0bb8d09db9d77d20"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/planning_studies.py",
              "engine_sha256": "5334bb9e0dec5acbc4b57b10ca675d2157bdc89f0fb0620e903b676184d8376c",
              "test_file": "apps/api/tests/test_planning_studies.py",
              "test_sha256": "3e5e2eccbc88a1d0eff85a8f1d40dbef5fd6bcb203645b5ab2b185be796ce4ce",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Rank one is an explicit assumption; no Johansen rank test, unit-root test, or seasonal adjustment is performed.",
              "This OLS error-correction screen is not a likelihood-based Johansen estimator or a calibrated commodity forecast.",
              "Synthetic examples do not substitute for timestamp-aligned, currency-consistent, real market observations.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-forecast-cointegration"
    },
    {
      "id": "forecast-backtest",
      "title": "Chronological price forecast backtest",
      "summary": "Fit an ARX one-step model on early rows and score later rolling-origin forecasts with empirical interval coverage.",
      "workspace": {
        "id": "forecast",
        "name": "Forecast studio",
        "appRoute": "#forecast"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "forecast",
        "equations": [
          "target[t] = β0 + β1·target[t-1] + β2·gas[t-1] + β3·carbon[t-1] + ε[t]",
          "MAE = mean(|actual - forecast|); RMSE = sqrt(mean((actual - forecast)^2))",
          "interval coverage = held-out actuals inside fitted training residual interval / holdout count"
        ],
        "limitations": [
          "Uses rolling one-step actual target lags and the preceding observed gas/carbon values; multi-step future drivers are not forecast.",
          "Prediction intervals use a normal residual approximation and omit parameter uncertainty.",
          "A chronological holdout is an evaluation, not independent market calibration or a guarantee of future performance."
        ],
        "sources": [
          {
            "title": "Hyndman and Athanasopoulos, Forecasting: Principles and Practice—time-series cross-validation",
            "url": "https://otexts.com/fpp3/tscv.html"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "training_fraction",
            "label": "Chronological training fraction",
            "default": 0.75,
            "min": 0.5,
            "max": 0.9,
            "step": 0.05,
            "unit": "fraction"
          },
          {
            "name": "interval_z_score",
            "label": "Normal residual interval multiplier",
            "default": 1.96,
            "min": 0.5,
            "max": 4,
            "step": 0.05,
            "unit": "z"
          }
        ],
        "jsonDataFields": [
          {
            "name": "target_price_eur_mwh",
            "label": "Target price observations",
            "default": {
              "type": "array",
              "items": 120,
              "itemTypes": [
                "number"
              ],
              "sha256": "95afc7d77563ec545a0b2bcc0c66472f45cd9c6c75826dde00b5253bf1ec0b44"
            }
          },
          {
            "name": "gas_price_eur_mwh",
            "label": "Gas observations",
            "default": {
              "type": "array",
              "items": 120,
              "itemTypes": [
                "number"
              ],
              "sha256": "e3896729257dbb65eddb30181de73230bf79f019447b61f462394504fd88ccdc"
            }
          },
          {
            "name": "carbon_price_eur_t",
            "label": "Carbon observations",
            "default": {
              "type": "array",
              "items": 120,
              "itemTypes": [
                "number"
              ],
              "sha256": "038f5d0f8147f90b5846115343f0b19d955b7dcfdd6847ce489315cd733c75be"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "6a6896f8518a7cf0bd9075158a7054d44fc678244e22ea80a4b494ede7a49363",
        "evidenceSha256": "b7514eef2960733e4d188dbe7fd36976cc31556470b4fb1ab72446411e088f4e",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"forecast-backtest\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"forecast-backtest\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "A constant 100 EUR/MWh series has zero holdout MAE/RMSE and 100% interval coverage with a chronological 35/12 split.",
          "testIds": [
            {
              "node_id": "tests/test_planning_studies.py::test_forecast_backtest_fits_training_rows_and_reports_holdout_metrics",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "training_fraction",
              "unit": "fraction",
              "minimum": 0.5,
              "maximum": 0.9,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "interval_z_score",
              "unit": "z",
              "minimum": 0.5,
              "maximum": 4,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "target_price_eur_mwh",
              "label": "Target price observations",
              "default": {
                "type": "array",
                "items": 120,
                "itemTypes": [
                  "number"
                ],
                "sha256": "95afc7d77563ec545a0b2bcc0c66472f45cd9c6c75826dde00b5253bf1ec0b44"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "gas_price_eur_mwh",
              "label": "Gas observations",
              "default": {
                "type": "array",
                "items": 120,
                "itemTypes": [
                  "number"
                ],
                "sha256": "e3896729257dbb65eddb30181de73230bf79f019447b61f462394504fd88ccdc"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "carbon_price_eur_t",
              "label": "Carbon observations",
              "default": {
                "type": "array",
                "items": 120,
                "itemTypes": [
                  "number"
                ],
                "sha256": "038f5d0f8147f90b5846115343f0b19d955b7dcfdd6847ce489315cd733c75be"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/planning_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "6a6896f8518a7cf0bd9075158a7054d44fc678244e22ea80a4b494ede7a49363",
          "evidenceSha256": "b7514eef2960733e4d188dbe7fd36976cc31556470b4fb1ab72446411e088f4e",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"forecast-backtest\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"forecast-backtest\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "A constant 100 EUR/MWh series has zero holdout MAE/RMSE and 100% interval coverage with a chronological 35/12 split.",
            "testIds": [
              {
                "node_id": "tests/test_planning_studies.py::test_forecast_backtest_fits_training_rows_and_reports_holdout_metrics",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "training_fraction below declared minimum 0.5",
            "training_fraction above declared maximum 0.9",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_planning_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "training_fraction=0.5 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Held-out mean absolute error",
                  "value": 0.6967652125391747,
                  "unit": "EUR/MWh"
                },
                {
                  "label": "Held-out root mean square error",
                  "value": 0.8513784396986289,
                  "unit": "EUR/MWh"
                },
                {
                  "label": "Held-out prediction-interval coverage",
                  "value": 0.45,
                  "unit": "fraction"
                },
                {
                  "label": "Holdout observations",
                  "value": 60,
                  "unit": "observations"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "1e6548836d9d9c8536b32efac0243cbad452ffc691cd33a095c53856793fca17"
          }
        },
        "limitations": [
          "Uses rolling one-step actual target lags and the preceding observed gas/carbon values; multi-step future drivers are not forecast.",
          "Prediction intervals use a normal residual approximation and omit parameter uncertainty.",
          "A chronological holdout is an evaluation, not independent market calibration or a guarantee of future performance."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/planning_studies.py",
          "tests": [
            "apps/api/tests/test_planning_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "forecast-backtest",
            "workspace": "forecast",
            "title": "Chronological price forecast backtest",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_planning_studies.py::test_forecast_backtest_fits_training_rows_and_reports_holdout_metrics",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "A constant 100 EUR/MWh series has zero holdout MAE/RMSE and 100% interval coverage with a chronological 35/12 split.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Held-out mean absolute error",
                        "value": 2.842170943040401e-14,
                        "unit": "EUR/MWh"
                      },
                      {
                        "label": "Held-out root mean square error",
                        "value": 2.842170943040401e-14,
                        "unit": "EUR/MWh"
                      },
                      {
                        "label": "Held-out prediction-interval coverage",
                        "value": 1,
                        "unit": "fraction"
                      },
                      {
                        "label": "Holdout observations",
                        "value": 12,
                        "unit": "observations"
                      }
                    ],
                    "scalar_details": {
                      "training_observations": 35,
                      "holdout_observations": 12,
                      "first_training_target_index": 1,
                      "last_training_target_index": 35,
                      "first_holdout_target_index": 36,
                      "training_fraction": 0.75,
                      "model": "OLS ARX(1), rolling one-step: target[t] ~ const + target[t-1] + gas[t-1] + carbon[t-1]",
                      "training_design_rank": 1,
                      "training_design_columns": 4,
                      "coefficients_fully_identified": false,
                      "training_residual_standard_deviation_eur_mwh": 2.842170943040401e-14,
                      "interval_z_score": 1.96,
                      "mae_eur_mwh": 2.842170943040401e-14,
                      "rmse_eur_mwh": 2.842170943040401e-14,
                      "prediction_interval_coverage_fraction": 1,
                      "fit_uses_holdout_targets": false,
                      "future_exogenous_assumption": "preceding observed gas and carbon values are available at each one-step origin"
                    },
                    "table_row_count": 12,
                    "result_sha256": "f5eafb0062c54196942bf72e509c0210f7213250648fd236c283dd0367957286"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Held-out mean absolute error",
                    "value": 1.2462445759300005,
                    "unit": "EUR/MWh"
                  },
                  {
                    "label": "Held-out root mean square error",
                    "value": 1.349123596172081,
                    "unit": "EUR/MWh"
                  },
                  {
                    "label": "Held-out prediction-interval coverage",
                    "value": 0.16666666666666666,
                    "unit": "fraction"
                  },
                  {
                    "label": "Holdout observations",
                    "value": 30,
                    "unit": "observations"
                  }
                ],
                "default_result_sha256": "1e6548836d9d9c8536b32efac0243cbad452ffc691cd33a095c53856793fca17"
              },
              "units": "EUR/MWh / observations",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 4
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "training_fraction=0.5 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Held-out mean absolute error",
                    "value": 0.6967652125391747,
                    "unit": "EUR/MWh"
                  },
                  {
                    "label": "Held-out root mean square error",
                    "value": 0.8513784396986289,
                    "unit": "EUR/MWh"
                  },
                  {
                    "label": "Held-out prediction-interval coverage",
                    "value": 0.45,
                    "unit": "fraction"
                  },
                  {
                    "label": "Holdout observations",
                    "value": 60,
                    "unit": "observations"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "1e6548836d9d9c8536b32efac0243cbad452ffc691cd33a095c53856793fca17"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/planning_studies.py",
              "engine_sha256": "5334bb9e0dec5acbc4b57b10ca675d2157bdc89f0fb0620e903b676184d8376c",
              "test_file": "apps/api/tests/test_planning_studies.py",
              "test_sha256": "3e5e2eccbc88a1d0eff85a8f1d40dbef5fd6bcb203645b5ab2b185be796ce4ce",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Uses rolling one-step actual target lags and the preceding observed gas/carbon values; multi-step future drivers are not forecast.",
              "Prediction intervals use a normal residual approximation and omit parameter uncertainty.",
              "A chronological holdout is an evaluation, not independent market calibration or a guarantee of future performance.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-forecast-backtest"
    },
    {
      "id": "markets-network",
      "title": "Six-zone DC optimal power flow",
      "summary": "Dispatch six zonal generators through a connected linear DC network with branch limits and nodal prices.",
      "workspace": {
        "id": "markets",
        "name": "Market intelligence",
        "appRoute": "#markets"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "network",
        "equations": [
          "flow[i,j] = susceptance[i,j]·(angle[i] - angle[j])",
          "generation[i] + net_import[i] = demand[i]",
          "min Σ generator_cost[i]·generation[i] subject to nodal balance, capacity, and branch limits"
        ],
        "limitations": [
          "Linear DC active-power approximation with one aggregated generator per zone; no losses, reactive power, voltage, dynamics, or reserves.",
          "Network parameters and costs are user assumptions; no calibrated six-zone topology or real zonal market is supplied."
        ],
        "sources": [
          {
            "title": "MATPOWER mathematical model: linear DC power flow and OPF formulation",
            "url": "https://matpower.org/doc/dev-manual/math-model.html"
          }
        ]
      },
      "inputs": {
        "numericFields": [],
        "jsonDataFields": [
          {
            "name": "zone_names",
            "label": "Six zone names",
            "default": {
              "type": "array",
              "items": 6,
              "itemTypes": [
                "string"
              ],
              "sha256": "8be8e041255567ca9d0281045dbd0dff33546df05cecf735906113d83c2dc1ca"
            }
          },
          {
            "name": "demand_mw",
            "label": "Zonal demand",
            "default": {
              "type": "array",
              "items": 6,
              "itemTypes": [
                "number"
              ],
              "sha256": "ad466d9d56fc96afa7c8701ebe784ad0ce72fc36a05be4f1587bd599692b045f"
            }
          },
          {
            "name": "generator_capacity_mw",
            "label": "One generator capacity per zone",
            "default": {
              "type": "array",
              "items": 6,
              "itemTypes": [
                "number"
              ],
              "sha256": "84d97a2e95b8a98952f06a60726c9f0fca44312651da05b787349638bdf2ba4c"
            }
          },
          {
            "name": "generator_cost_eur_mwh",
            "label": "Marginal generation cost",
            "default": {
              "type": "array",
              "items": 6,
              "itemTypes": [
                "number"
              ],
              "sha256": "21702b464e9c841d1df98ebc0c4a85ef7af2b2e9030f7bd251e47a34f09bc138"
            }
          },
          {
            "name": "lines",
            "label": "Branch susceptance and thermal limits",
            "default": {
              "type": "array",
              "items": 7,
              "itemTypes": [
                "object"
              ],
              "sha256": "1fcbaab72ca6fd1437cc970c11e8ce12620c06e8bd74c93e0b08a17947a8c3a4"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "352cb15ff3677bce24a97e8505369fbd449df5a7d0d5971b41540e9b0528e691",
        "evidenceSha256": "00a574b4a7e187ab687dc394ee394d06d94591832e12d54ded14decb6908e9af",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"markets-network\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"markets-network\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "A 20 MW congested cut separates 10 EUR/MWh source and 100 EUR/MWh local marginal prices with zero balance mismatch.",
          "testIds": [
            {
              "node_id": "tests/test_planning_studies.py::test_six_zone_dcopf_prices_separate_across_congested_cut",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [],
          "jsonDataFields": [
            {
              "name": "zone_names",
              "label": "Six zone names",
              "default": {
                "type": "array",
                "items": 6,
                "itemTypes": [
                  "string"
                ],
                "sha256": "8be8e041255567ca9d0281045dbd0dff33546df05cecf735906113d83c2dc1ca"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "demand_mw",
              "label": "Zonal demand",
              "default": {
                "type": "array",
                "items": 6,
                "itemTypes": [
                  "number"
                ],
                "sha256": "ad466d9d56fc96afa7c8701ebe784ad0ce72fc36a05be4f1587bd599692b045f"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "generator_capacity_mw",
              "label": "One generator capacity per zone",
              "default": {
                "type": "array",
                "items": 6,
                "itemTypes": [
                  "number"
                ],
                "sha256": "84d97a2e95b8a98952f06a60726c9f0fca44312651da05b787349638bdf2ba4c"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "generator_cost_eur_mwh",
              "label": "Marginal generation cost",
              "default": {
                "type": "array",
                "items": 6,
                "itemTypes": [
                  "number"
                ],
                "sha256": "21702b464e9c841d1df98ebc0c4a85ef7af2b2e9030f7bd251e47a34f09bc138"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "lines",
              "label": "Branch susceptance and thermal limits",
              "default": {
                "type": "array",
                "items": 7,
                "itemTypes": [
                  "object"
                ],
                "sha256": "1fcbaab72ca6fd1437cc970c11e8ce12620c06e8bd74c93e0b08a17947a8c3a4"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/planning_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "352cb15ff3677bce24a97e8505369fbd449df5a7d0d5971b41540e9b0528e691",
          "evidenceSha256": "00a574b4a7e187ab687dc394ee394d06d94591832e12d54ded14decb6908e9af",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"markets-network\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"markets-network\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "A 20 MW congested cut separates 10 EUR/MWh source and 100 EUR/MWh local marginal prices with zero balance mismatch.",
            "testIds": [
              {
                "node_id": "tests/test_planning_studies.py::test_six_zone_dcopf_prices_separate_across_congested_cut",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "unknown input field, non-finite numeric input, and malformed/oversized JSON data"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_planning_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "demand_mw=zero vector at preserved length",
              "metric_snapshot": [
                {
                  "label": "Total generation",
                  "value": 0,
                  "unit": "MW"
                },
                {
                  "label": "Total demand",
                  "value": 0,
                  "unit": "MW"
                },
                {
                  "label": "System dispatch cost",
                  "value": 0,
                  "unit": "EUR/h"
                },
                {
                  "label": "Maximum zonal price",
                  "value": 0,
                  "unit": "EUR/MWh"
                },
                {
                  "label": "Maximum branch loading",
                  "value": 0,
                  "unit": "fraction"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {
            "solver_status": "optimal"
          },
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "PASS",
          "actual": "optimal"
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "56f956504f6f6c6424ae4ba7951c99124ca04dcdaf3358d9834fd5e553224578"
          }
        },
        "limitations": [
          "Linear DC active-power approximation with one aggregated generator per zone; no losses, reactive power, voltage, dynamics, or reserves.",
          "Network parameters and costs are user assumptions; no calibrated six-zone topology or real zonal market is supplied."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/planning_studies.py",
          "tests": [
            "apps/api/tests/test_planning_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "markets-network",
            "workspace": "markets",
            "title": "Six-zone DC optimal power flow",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_planning_studies.py::test_six_zone_dcopf_prices_separate_across_congested_cut",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "A 20 MW congested cut separates 10 EUR/MWh source and 100 EUR/MWh local marginal prices with zero balance mismatch.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Total generation",
                        "value": 80,
                        "unit": "MW"
                      },
                      {
                        "label": "Total demand",
                        "value": 80,
                        "unit": "MW"
                      },
                      {
                        "label": "System dispatch cost",
                        "value": 6200,
                        "unit": "EUR/h"
                      },
                      {
                        "label": "Maximum zonal price",
                        "value": 100,
                        "unit": "EUR/MWh"
                      },
                      {
                        "label": "Maximum branch loading",
                        "value": 0.9999999999999998,
                        "unit": "fraction"
                      }
                    ],
                    "scalar_details": {
                      "total_generation_mw": 80,
                      "total_demand_mw": 80,
                      "max_balance_residual_mw": 1.0658141036401503e-14,
                      "price_duals_source": "HiGHS nodal balance equality marginals",
                      "solver_status": "optimal",
                      "solver_message": "Optimization terminated successfully. (HiGHS Status 7: Optimal)",
                      "dispatch_cost_eur_per_hour": 6200
                    },
                    "table_row_count": 6,
                    "result_sha256": "7f398d160d5d509bc20c20638f24f42cbd72396a110c5d261afd1ee354146c94"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Total generation",
                    "value": 399.99999999999994,
                    "unit": "MW"
                  },
                  {
                    "label": "Total demand",
                    "value": 400,
                    "unit": "MW"
                  },
                  {
                    "label": "System dispatch cost",
                    "value": 9499.999999999998,
                    "unit": "EUR/h"
                  },
                  {
                    "label": "Maximum zonal price",
                    "value": 35,
                    "unit": "EUR/MWh"
                  },
                  {
                    "label": "Maximum branch loading",
                    "value": 0.4428571428571428,
                    "unit": "fraction"
                  }
                ],
                "default_result_sha256": "56f956504f6f6c6424ae4ba7951c99124ca04dcdaf3358d9834fd5e553224578"
              },
              "units": "MW / EUR/MWh",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 5
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "demand_mw=zero vector at preserved length",
                "metric_snapshot": [
                  {
                    "label": "Total generation",
                    "value": 0,
                    "unit": "MW"
                  },
                  {
                    "label": "Total demand",
                    "value": 0,
                    "unit": "MW"
                  },
                  {
                    "label": "System dispatch cost",
                    "value": 0,
                    "unit": "EUR/h"
                  },
                  {
                    "label": "Maximum zonal price",
                    "value": 0,
                    "unit": "EUR/MWh"
                  },
                  {
                    "label": "Maximum branch loading",
                    "value": 0,
                    "unit": "fraction"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "56f956504f6f6c6424ae4ba7951c99124ca04dcdaf3358d9834fd5e553224578"
              },
              "solver_feasibility": {
                "status": "PASS",
                "actual": "optimal"
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/planning_studies.py",
              "engine_sha256": "5334bb9e0dec5acbc4b57b10ca675d2157bdc89f0fb0620e903b676184d8376c",
              "test_file": "apps/api/tests/test_planning_studies.py",
              "test_sha256": "3e5e2eccbc88a1d0eff85a8f1d40dbef5fd6bcb203645b5ab2b185be796ce4ce",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Linear DC active-power approximation with one aggregated generator per zone; no losses, reactive power, voltage, dynamics, or reserves.",
              "Network parameters and costs are user assumptions; no calibrated six-zone topology or real zonal market is supplied.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-markets-network"
    },
    {
      "id": "investment-contracts",
      "title": "Contract, tax, and reserve cash flows",
      "summary": "Build separate project and equity cash flows with dated floor volumes, merchant sales, tax losses, DSRA, and working capital.",
      "workspace": {
        "id": "investment",
        "name": "Investment desk",
        "appRoute": "#investment"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "finance",
        "equations": [
          "contracted volume = min(generation, annual contract volume) while term is in force",
          "contracted settlement price = max(user floor price, supplied merchant price)",
          "taxable income = EBITDA - straight-line depreciation - interest (equity tax case); tax loss carries forward within this screen",
          "equity CF = EBITDA - cash tax - debt service - DSRA funding + DSRA release - Δworking capital",
          "project CF = -CAPEX - initial working capital; subsequent CF = EBITDA - project tax - Δworking capital"
        ],
        "limitations": [
          "Annual calendar-year floor and merchant-price screen; no partial-year settlement, take-or-pay, VAT, tax jurisdiction rules, loss caps, depreciation conventions, fees, or default.",
          "Debt is level-payment; DSRA equals configured months of scheduled service and is funded from equity at close, then released after the final debt year.",
          "Rates and terms are user assumptions; no signed contract or tax/legal advice is represented. Nonconventional IRR is null."
        ],
        "sources": [
          {
            "title": "IRS Publication 542: Corporations, losses, and tax provisions (jurisdictional reference only)",
            "url": "https://www.irs.gov/pub/irs-pdf/p542.pdf"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "capex_eur",
            "label": "Initial project CAPEX",
            "default": 8000000,
            "min": 0,
            "max": 1000000000000,
            "step": 100000,
            "unit": "EUR"
          },
          {
            "name": "debt_share",
            "label": "Initial debt share",
            "default": 0.55,
            "min": 0,
            "max": 0.9,
            "step": 0.01,
            "unit": "fraction"
          },
          {
            "name": "debt_interest_rate",
            "label": "Debt interest rate",
            "default": 0.055,
            "min": 0,
            "max": 0.5,
            "step": 0.005,
            "unit": "fraction"
          },
          {
            "name": "tax_rate",
            "label": "Illustrative tax rate",
            "default": 0.25,
            "min": 0,
            "max": 0.6,
            "step": 0.01,
            "unit": "fraction"
          },
          {
            "name": "dsra_months",
            "label": "Debt-service reserve coverage",
            "default": 6,
            "min": 0,
            "max": 12,
            "step": 1,
            "unit": "months"
          },
          {
            "name": "working_capital_fraction_of_revenue",
            "label": "Working capital / revenue",
            "default": 0.08,
            "min": 0,
            "max": 0.5,
            "step": 0.01,
            "unit": "fraction"
          }
        ],
        "jsonDataFields": [
          {
            "name": "operating_years",
            "label": "Dated annual operating observations",
            "default": {
              "type": "array",
              "items": 5,
              "itemTypes": [
                "object"
              ],
              "sha256": "c2e8a76d9cb343b1d52a6bef851945281f8b81e59c461ee1d12e16c16999c2f6"
            }
          },
          {
            "name": "contracts",
            "label": "Calendar-year floor volume terms",
            "default": {
              "type": "array",
              "items": 1,
              "itemTypes": [
                "object"
              ],
              "sha256": "eef80e2b46e4e29489add81f401360da597f4d3fdf629ce7dd0977f94e84a3a2"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "02bd4e8946ae89f9daabf4b18c33c2fe6187b105925a407048584382c2133798",
        "evidenceSha256": "b9e66691c614d34db10e4e0633e8383902b04439fde417870779fd93395abe33",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"investment-contracts\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"investment-contracts\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "A 2,000 EUR loss carries forward; later 10,000 EUR margin at 25% tax yields 1,750 EUR cash tax; zero-investment IRRs remain null.",
          "testIds": [
            {
              "node_id": "tests/test_planning_studies.py::test_contract_tax_losses_carry_forward_and_zero_investment_irr_stays_null",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "capex_eur",
              "unit": "EUR",
              "minimum": 0,
              "maximum": 1000000000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "debt_share",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 0.9,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "debt_interest_rate",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 0.5,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "tax_rate",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 0.6,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "dsra_months",
              "unit": "months",
              "minimum": 0,
              "maximum": 12,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "working_capital_fraction_of_revenue",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 0.5,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "operating_years",
              "label": "Dated annual operating observations",
              "default": {
                "type": "array",
                "items": 5,
                "itemTypes": [
                  "object"
                ],
                "sha256": "c2e8a76d9cb343b1d52a6bef851945281f8b81e59c461ee1d12e16c16999c2f6"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "contracts",
              "label": "Calendar-year floor volume terms",
              "default": {
                "type": "array",
                "items": 1,
                "itemTypes": [
                  "object"
                ],
                "sha256": "eef80e2b46e4e29489add81f401360da597f4d3fdf629ce7dd0977f94e84a3a2"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/planning_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "02bd4e8946ae89f9daabf4b18c33c2fe6187b105925a407048584382c2133798",
          "evidenceSha256": "b9e66691c614d34db10e4e0633e8383902b04439fde417870779fd93395abe33",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"investment-contracts\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"investment-contracts\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "A 2,000 EUR loss carries forward; later 10,000 EUR margin at 25% tax yields 1,750 EUR cash tax; zero-investment IRRs remain null.",
            "testIds": [
              {
                "node_id": "tests/test_planning_studies.py::test_contract_tax_losses_carry_forward_and_zero_investment_irr_stays_null",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "capex_eur below declared minimum 0",
            "capex_eur above declared maximum 1000000000000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_planning_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "capex_eur=0 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Contract-floor revenue",
                  "value": 3060000,
                  "unit": "EUR"
                },
                {
                  "label": "Merchant revenue",
                  "value": 4199280,
                  "unit": "EUR"
                },
                {
                  "label": "Project IRR",
                  "value": 712.8666428526005,
                  "unit": "%"
                },
                {
                  "label": "Equity IRR",
                  "value": 712.8666428526005,
                  "unit": "%"
                },
                {
                  "label": "Total equity cash tax",
                  "value": 1354891.2548828125,
                  "unit": "EUR"
                },
                {
                  "label": "Debt remaining",
                  "value": 0,
                  "unit": "EUR"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "83af36d20a9652a16b8e37c589aa3b44d1e81e2868ca2f34c1af618493d8c344"
          }
        },
        "limitations": [
          "Annual calendar-year floor and merchant-price screen; no partial-year settlement, take-or-pay, VAT, tax jurisdiction rules, loss caps, depreciation conventions, fees, or default.",
          "Debt is level-payment; DSRA equals configured months of scheduled service and is funded from equity at close, then released after the final debt year.",
          "Rates and terms are user assumptions; no signed contract or tax/legal advice is represented. Nonconventional IRR is null."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/planning_studies.py",
          "tests": [
            "apps/api/tests/test_planning_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "investment-contracts",
            "workspace": "investment",
            "title": "Contract, tax, and reserve cash flows",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_planning_studies.py::test_contract_tax_losses_carry_forward_and_zero_investment_irr_stays_null",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "A 2,000 EUR loss carries forward; later 10,000 EUR margin at 25% tax yields 1,750 EUR cash tax; zero-investment IRRs remain null.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Contract-floor revenue",
                        "value": 0,
                        "unit": "EUR"
                      },
                      {
                        "label": "Merchant revenue",
                        "value": 10000,
                        "unit": "EUR"
                      },
                      {
                        "label": "Project IRR",
                        "value": 104.13812651501448,
                        "unit": "%"
                      },
                      {
                        "label": "Equity IRR",
                        "value": 104.13812651501448,
                        "unit": "%"
                      },
                      {
                        "label": "Total equity cash tax",
                        "value": 1750,
                        "unit": "EUR"
                      },
                      {
                        "label": "Debt remaining",
                        "value": 0,
                        "unit": "EUR"
                      }
                    ],
                    "scalar_details": {
                      "project_irr_pct": 104.13812651501448,
                      "equity_irr_pct": 104.13812651501448,
                      "initial_debt_draw_eur": 0,
                      "initial_equity_at_close_eur": 1000,
                      "debt_service_eur_per_year": 0,
                      "debt_closing_balance_eur": 0,
                      "total_equity_cash_tax_eur": 1750,
                      "total_debt_interest_eur": 0,
                      "project_tax_loss_carryforward_eur": 0,
                      "tax_loss_carryforward_eur": 0,
                      "tax_loss_model": "unlimited within-model carryforward; only offsets later positive taxable income; no carryback",
                      "depreciation_model": "straight-line over configured years from first operating year",
                      "dsra_initial_funding_eur": 0,
                      "dsra_terminal_release_eur": 0,
                      "terminal_working_capital_eur": 0,
                      "working_capital_initial_funding_eur": 0,
                      "contract_revenue_eur": 0,
                      "merchant_revenue_eur": 10000,
                      "project_cashflow_reconciliation_eur": 0,
                      "project_cashflow_reconciliation": "every annual row equals EBITDA minus project cash tax minus the change in working capital",
                      "equity_irr_convention": "null unless cash flows have one sign change and an NPV root exists between -99.9% and 1,000%"
                    },
                    "table_row_count": 2,
                    "result_sha256": "45b949502d1c7b9800384018f7d01b2dc7a98340b32791e8c801e3a2fa4b877b"
                  },
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Contract-floor revenue",
                        "value": 0,
                        "unit": "EUR"
                      },
                      {
                        "label": "Merchant revenue",
                        "value": 0,
                        "unit": "EUR"
                      },
                      {
                        "label": "Project IRR",
                        "value": null,
                        "unit": "%"
                      },
                      {
                        "label": "Equity IRR",
                        "value": null,
                        "unit": "%"
                      },
                      {
                        "label": "Total equity cash tax",
                        "value": 0,
                        "unit": "EUR"
                      },
                      {
                        "label": "Debt remaining",
                        "value": 0,
                        "unit": "EUR"
                      }
                    ],
                    "scalar_details": {
                      "project_irr_pct": null,
                      "equity_irr_pct": null,
                      "initial_debt_draw_eur": 0,
                      "initial_equity_at_close_eur": 0,
                      "debt_service_eur_per_year": 0,
                      "debt_closing_balance_eur": 0,
                      "total_equity_cash_tax_eur": 0,
                      "total_debt_interest_eur": 0,
                      "project_tax_loss_carryforward_eur": 0,
                      "tax_loss_carryforward_eur": 0,
                      "tax_loss_model": "unlimited within-model carryforward; only offsets later positive taxable income; no carryback",
                      "depreciation_model": "straight-line over configured years from first operating year",
                      "dsra_initial_funding_eur": 0,
                      "dsra_terminal_release_eur": 0,
                      "terminal_working_capital_eur": 0,
                      "working_capital_initial_funding_eur": 0,
                      "contract_revenue_eur": 0,
                      "merchant_revenue_eur": 0,
                      "project_cashflow_reconciliation_eur": 0,
                      "project_cashflow_reconciliation": "every annual row equals EBITDA minus project cash tax minus the change in working capital",
                      "equity_irr_convention": "null unless cash flows have one sign change and an NPV root exists between -99.9% and 1,000%"
                    },
                    "table_row_count": 2,
                    "result_sha256": "edac549271832b1cde3af73cbddfd19cadedb5bcaa7c14af70d30f963ae5cf21"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Contract-floor revenue",
                    "value": 3060000,
                    "unit": "EUR"
                  },
                  {
                    "label": "Merchant revenue",
                    "value": 4199280,
                    "unit": "EUR"
                  },
                  {
                    "label": "Project IRR",
                    "value": -15.833472853837453,
                    "unit": "%"
                  },
                  {
                    "label": "Equity IRR",
                    "value": -27.546744477974027,
                    "unit": "%"
                  },
                  {
                    "label": "Total equity cash tax",
                    "value": 699595.5194271542,
                    "unit": "EUR"
                  },
                  {
                    "label": "Debt remaining",
                    "value": 0,
                    "unit": "EUR"
                  }
                ],
                "default_result_sha256": "83af36d20a9652a16b8e37c589aa3b44d1e81e2868ca2f34c1af618493d8c344"
              },
              "units": "EUR / %; null IRR",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 6
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "capex_eur=0 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Contract-floor revenue",
                    "value": 3060000,
                    "unit": "EUR"
                  },
                  {
                    "label": "Merchant revenue",
                    "value": 4199280,
                    "unit": "EUR"
                  },
                  {
                    "label": "Project IRR",
                    "value": 712.8666428526005,
                    "unit": "%"
                  },
                  {
                    "label": "Equity IRR",
                    "value": 712.8666428526005,
                    "unit": "%"
                  },
                  {
                    "label": "Total equity cash tax",
                    "value": 1354891.2548828125,
                    "unit": "EUR"
                  },
                  {
                    "label": "Debt remaining",
                    "value": 0,
                    "unit": "EUR"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "83af36d20a9652a16b8e37c589aa3b44d1e81e2868ca2f34c1af618493d8c344"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/planning_studies.py",
              "engine_sha256": "5334bb9e0dec5acbc4b57b10ca675d2157bdc89f0fb0620e903b676184d8376c",
              "test_file": "apps/api/tests/test_planning_studies.py",
              "test_sha256": "3e5e2eccbc88a1d0eff85a8f1d40dbef5fd6bcb203645b5ab2b185be796ce4ce",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Annual calendar-year floor and merchant-price screen; no partial-year settlement, take-or-pay, VAT, tax jurisdiction rules, loss caps, depreciation conventions, fees, or default.",
              "Debt is level-payment; DSRA equals configured months of scheduled service and is funded from equity at close, then released after the final debt year.",
              "Rates and terms are user assumptions; no signed contract or tax/legal advice is represented. Nonconventional IRR is null.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-investment-contracts"
    },
    {
      "id": "lessons-hydrogenaux",
      "title": "Hydrogen auxiliary energy ledger",
      "summary": "Account for demineralized water, electrolysis, idealized compression to 350 bar, fuel-cell auxiliaries, useful output, and losses.",
      "workspace": {
        "id": "lessons",
        "name": "Learning studio",
        "appRoute": "#lessons"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "learning",
        "equations": [
          "water stoichiometry = 9 kg feedwater per kg H₂ / recovery fraction",
          "ideal isothermal compression = m·R_H₂·T·ln(Pout/Pin) / compressor efficiency",
          "electricity input = useful electricity + useful recovered heat + explicit losses"
        ],
        "limitations": [
          "Isothermal ideal-gas compressor proxy from supplied outlet pressure; no real-gas, multistage, storage, leakage, purity, or pressure-drop model.",
          "Hydrogen LHV and 9 kg/kg stoichiometry are fixed screening conventions; plant-specific stack and water data are required."
        ],
        "sources": [
          {
            "title": "DOE Hydrogen Program Record 9013: energy requirements for hydrogen gas compression",
            "url": "https://www.hydrogen.energy.gov/docs/hydrogenprogramlibraries/pdfs/9013_energy_requirements_for_hydrogen_gas_compression.pdf"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "hydrogen_kg",
            "label": "Hydrogen mass",
            "default": 100,
            "min": 0,
            "max": 1000000,
            "step": 10,
            "unit": "kg"
          },
          {
            "name": "electrolyzer_kwh_per_kg",
            "label": "Electrolyzer electricity",
            "default": 52,
            "min": 33.3,
            "max": 200,
            "step": 0.5,
            "unit": "kWh/kg"
          },
          {
            "name": "water_recovery_fraction",
            "label": "Water treatment recovery",
            "default": 0.8,
            "min": 0.01,
            "max": 1,
            "step": 0.01,
            "unit": "fraction"
          },
          {
            "name": "demineralization_kwh_per_m3",
            "label": "Demineralization energy",
            "default": 3,
            "min": 0,
            "max": 10000,
            "step": 0.5,
            "unit": "kWh/m³"
          },
          {
            "name": "fuel_cell_gross_efficiency",
            "label": "Fuel-cell gross efficiency",
            "default": 0.52,
            "min": 0,
            "max": 0.8,
            "step": 0.01,
            "unit": "fraction"
          },
          {
            "name": "fuel_cell_auxiliary_fraction",
            "label": "Fuel-cell parasitic fraction",
            "default": 0.06,
            "min": 0,
            "max": 0.4,
            "step": 0.01,
            "unit": "fraction"
          }
        ],
        "jsonDataFields": []
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "ed71591eb49d25c6e5660897b7daa4ebe600b7488789383880669f3fe2b4b423",
        "evidenceSha256": "5d6726350869d822150e02cf2bf9b88537347cdddb2789d46b8a4c86dde51128",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"lessons-hydrogenaux\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"lessons-hydrogenaux\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "For 10 kg, water feed exceeds stoichiometric water, 350 bar compression and auxiliaries are included, and the full energy ledger closes.",
          "testIds": [
            {
              "node_id": "tests/test_planning_studies.py::test_hydrogen_ledger_accounts_for_treatment_compression_and_auxiliaries",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "hydrogen_kg",
              "unit": "kg",
              "minimum": 0,
              "maximum": 1000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "electrolyzer_kwh_per_kg",
              "unit": "kWh/kg",
              "minimum": 33.3,
              "maximum": 200,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "water_recovery_fraction",
              "unit": "fraction",
              "minimum": 0.01,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "demineralization_kwh_per_m3",
              "unit": "kWh/m³",
              "minimum": 0,
              "maximum": 10000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "fuel_cell_gross_efficiency",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 0.8,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "fuel_cell_auxiliary_fraction",
              "unit": "fraction",
              "minimum": 0,
              "maximum": 0.4,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [],
          "strictValidationReference": "apps/api/helioforge/engines/planning_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "ed71591eb49d25c6e5660897b7daa4ebe600b7488789383880669f3fe2b4b423",
          "evidenceSha256": "5d6726350869d822150e02cf2bf9b88537347cdddb2789d46b8a4c86dde51128",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"lessons-hydrogenaux\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"lessons-hydrogenaux\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "For 10 kg, water feed exceeds stoichiometric water, 350 bar compression and auxiliaries are included, and the full energy ledger closes.",
            "testIds": [
              {
                "node_id": "tests/test_planning_studies.py::test_hydrogen_ledger_accounts_for_treatment_compression_and_auxiliaries",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "hydrogen_kg below declared minimum 0",
            "hydrogen_kg above declared maximum 1000000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_planning_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "hydrogen_kg=0 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Electricity input including auxiliaries",
                  "value": 0,
                  "unit": "kWh"
                },
                {
                  "label": "Useful fuel-cell electricity",
                  "value": 0,
                  "unit": "kWh"
                },
                {
                  "label": "Recovered useful heat",
                  "value": 0,
                  "unit": "kWh"
                },
                {
                  "label": "Electricity round-trip efficiency",
                  "value": null,
                  "unit": "fraction"
                },
                {
                  "label": "Energy balance residual",
                  "value": 0,
                  "unit": "kWh"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "677d2b1216936143d2fbad7aabc72e358918414fb1c1f5aebb1866800bd85489"
          }
        },
        "limitations": [
          "Isothermal ideal-gas compressor proxy from supplied outlet pressure; no real-gas, multistage, storage, leakage, purity, or pressure-drop model.",
          "Hydrogen LHV and 9 kg/kg stoichiometry are fixed screening conventions; plant-specific stack and water data are required."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/planning_studies.py",
          "tests": [
            "apps/api/tests/test_planning_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "lessons-hydrogenaux",
            "workspace": "lessons",
            "title": "Hydrogen auxiliary energy ledger",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_planning_studies.py::test_hydrogen_ledger_accounts_for_treatment_compression_and_auxiliaries",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "For 10 kg, water feed exceeds stoichiometric water, 350 bar compression and auxiliaries are included, and the full energy ledger closes.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Electricity input including auxiliaries",
                        "value": 548.2472289460833,
                        "unit": "kWh"
                      },
                      {
                        "label": "Useful fuel-cell electricity",
                        "value": 162.7704,
                        "unit": "kWh"
                      },
                      {
                        "label": "Recovered useful heat",
                        "value": 47.952,
                        "unit": "kWh"
                      },
                      {
                        "label": "Electricity round-trip efficiency",
                        "value": 0.29689233507463375,
                        "unit": "fraction"
                      },
                      {
                        "label": "Energy balance residual",
                        "value": 0,
                        "unit": "kWh"
                      }
                    ],
                    "scalar_details": {
                      "hydrogen_mass_kg": 10,
                      "stoichiometric_water_kg": 90,
                      "water_feed_kg": 112.5,
                      "water_reject_kg": 22.5,
                      "water_treatment_electricity_kwh": 0.3375,
                      "electrolyzer_electricity_kwh": 520,
                      "hydrogen_chemical_energy_lhv_kwh": 333,
                      "electrolyzer_conversion_loss_kwh": 187,
                      "electrolyzer_auxiliary_kwh": 15,
                      "compressor_specific_work_kwh_per_kg": 1.2909728946083279,
                      "compression_electricity_kwh": 12.909728946083279,
                      "compression_pressure_in_bar": 30,
                      "compression_pressure_out_bar": 350,
                      "compression_thermodynamic_model": "ideal-gas reversible isothermal work divided by user efficiency",
                      "fuel_cell_gross_electricity_kwh": 173.16,
                      "fuel_cell_auxiliary_kwh": 10.3896,
                      "fuel_cell_useful_electricity_kwh": 162.7704,
                      "fuel_cell_conversion_loss_kwh": 159.84,
                      "fuel_cell_recovered_heat_kwh": 47.952,
                      "unrecovered_fuel_cell_loss_kwh": 111.888,
                      "total_energy_input_kwh": 548.2472289460833,
                      "total_losses_kwh": 337.5248289460833,
                      "energy_balance_residual_kwh": 0
                    },
                    "table_row_count": 5,
                    "result_sha256": "2c869526e93ae47dfd722621471fee08c4a4d9f83df8ec11dae0dd758a6bfd43"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Electricity input including auxiliaries",
                    "value": 5482.472289460833,
                    "unit": "kWh"
                  },
                  {
                    "label": "Useful fuel-cell electricity",
                    "value": 1627.704,
                    "unit": "kWh"
                  },
                  {
                    "label": "Recovered useful heat",
                    "value": 479.51999999999987,
                    "unit": "kWh"
                  },
                  {
                    "label": "Electricity round-trip efficiency",
                    "value": 0.29689233507463375,
                    "unit": "fraction"
                  },
                  {
                    "label": "Energy balance residual",
                    "value": 4.547473508864641e-13,
                    "unit": "kWh"
                  }
                ],
                "default_result_sha256": "677d2b1216936143d2fbad7aabc72e358918414fb1c1f5aebb1866800bd85489"
              },
              "units": "kg / kWh / bar",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 5
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "hydrogen_kg=0 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Electricity input including auxiliaries",
                    "value": 0,
                    "unit": "kWh"
                  },
                  {
                    "label": "Useful fuel-cell electricity",
                    "value": 0,
                    "unit": "kWh"
                  },
                  {
                    "label": "Recovered useful heat",
                    "value": 0,
                    "unit": "kWh"
                  },
                  {
                    "label": "Electricity round-trip efficiency",
                    "value": null,
                    "unit": "fraction"
                  },
                  {
                    "label": "Energy balance residual",
                    "value": 0,
                    "unit": "kWh"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "677d2b1216936143d2fbad7aabc72e358918414fb1c1f5aebb1866800bd85489"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/planning_studies.py",
              "engine_sha256": "5334bb9e0dec5acbc4b57b10ca675d2157bdc89f0fb0620e903b676184d8376c",
              "test_file": "apps/api/tests/test_planning_studies.py",
              "test_sha256": "3e5e2eccbc88a1d0eff85a8f1d40dbef5fd6bcb203645b5ab2b185be796ce4ce",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Isothermal ideal-gas compressor proxy from supplied outlet pressure; no real-gas, multistage, storage, leakage, purity, or pressure-drop model.",
              "Hydrogen LHV and 9 kg/kg stoichiometry are fixed screening conventions; plant-specific stack and water data are required.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-lessons-hydrogenaux"
    },
    {
      "id": "overview-risk",
      "title": "Portfolio evidence and threshold screen",
      "summary": "Compare supplied asset prices, debt, emissions, yield, or other numeric evidence with user-supplied limits and provenance.",
      "workspace": {
        "id": "overview",
        "name": "Command centre",
        "appRoute": "#overview"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "risk",
        "equations": [
          "status = missing_value if value is absent; otherwise compare to the supplied min/max threshold",
          "provenance is complete only when a source reference and observation date are both supplied"
        ],
        "limitations": [
          "No asset values, thresholds, or sources are fabricated; an empty dataset returns an explicit no-evidence state.",
          "Threshold comparison does not authenticate records, normalize units, or imply risk approval.",
          "The NIST publication provides materials-data evaluation context; the source-plus-date completeness rule is this application’s own checklist, not a NIST validation standard."
        ],
        "sources": [
          {
            "title": "NIST Interactive Data Evaluation Assessment Tool",
            "url": "https://www.nist.gov/publications/nist-interactive-data-evaluation-assessment-tool"
          }
        ]
      },
      "inputs": {
        "numericFields": [],
        "jsonDataFields": [
          {
            "name": "evidence",
            "label": "Asset metric values, limits, units, and source records",
            "default": {
              "type": "array",
              "items": 0,
              "itemTypes": [],
              "sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "8f3f9c27d9b4cfc3cf603c15667240f02309ef53bc498bede4c4fb71c87a0267",
        "evidenceSha256": "85828d40d7f64615125ed0ca21b2f5f7db6cf5c8fff132b56fee221bd34b422d",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"overview-risk\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"overview-risk\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Three rows produce one breach, one missing value and two incomplete-provenance records without converting unknowns into a clean result.",
          "testIds": [
            {
              "node_id": "tests/test_planning_studies.py::test_portfolio_risk_distinguishes_breach_missing_and_unprovenanced_data",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [],
          "jsonDataFields": [
            {
              "name": "evidence",
              "label": "Asset metric values, limits, units, and source records",
              "default": {
                "type": "array",
                "items": 0,
                "itemTypes": [],
                "sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/planning_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "8f3f9c27d9b4cfc3cf603c15667240f02309ef53bc498bede4c4fb71c87a0267",
          "evidenceSha256": "85828d40d7f64615125ed0ca21b2f5f7db6cf5c8fff132b56fee221bd34b422d",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"overview-risk\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"overview-risk\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Three rows produce one breach, one missing value and two incomplete-provenance records without converting unknowns into a clean result.",
            "testIds": [
              {
                "node_id": "tests/test_planning_studies.py::test_portfolio_risk_distinguishes_breach_missing_and_unprovenanced_data",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "unknown input field, non-finite numeric input, and malformed/oversized JSON data"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_planning_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "evidence=empty (declared zero-length boundary)",
              "metric_snapshot": [
                {
                  "label": "Supplied asset metric rows",
                  "value": 0,
                  "unit": "rows"
                },
                {
                  "label": "Threshold breaches",
                  "value": 0,
                  "unit": "rows"
                },
                {
                  "label": "Missing values",
                  "value": 0,
                  "unit": "rows"
                },
                {
                  "label": "Incomplete provenance",
                  "value": 0,
                  "unit": "rows"
                },
                {
                  "label": "Evidence state",
                  "value": "no supplied evidence",
                  "unit": "state"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "df05978b882c45cac5cc22858a0c7e5ffe4a571037e67e69d22f9e340ec6ca1c"
          }
        },
        "limitations": [
          "No asset values, thresholds, or sources are fabricated; an empty dataset returns an explicit no-evidence state.",
          "Threshold comparison does not authenticate records, normalize units, or imply risk approval.",
          "The NIST publication provides materials-data evaluation context; the source-plus-date completeness rule is this application’s own checklist, not a NIST validation standard."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/planning_studies.py",
          "tests": [
            "apps/api/tests/test_planning_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "overview-risk",
            "workspace": "overview",
            "title": "Portfolio evidence and threshold screen",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_planning_studies.py::test_portfolio_risk_distinguishes_breach_missing_and_unprovenanced_data",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Three rows produce one breach, one missing value and two incomplete-provenance records without converting unknowns into a clean result.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Supplied asset metric rows",
                        "value": 3,
                        "unit": "rows"
                      },
                      {
                        "label": "Threshold breaches",
                        "value": 1,
                        "unit": "rows"
                      },
                      {
                        "label": "Missing values",
                        "value": 1,
                        "unit": "rows"
                      },
                      {
                        "label": "Incomplete provenance",
                        "value": 2,
                        "unit": "rows"
                      },
                      {
                        "label": "Evidence state",
                        "value": "screened user evidence",
                        "unit": "state"
                      }
                    ],
                    "scalar_details": {
                      "breach_count": 1,
                      "missing_value_count": 1,
                      "missing_provenance_count": 2,
                      "asset_count": 3,
                      "evidence_row_count": 3,
                      "thresholds_and_values_are_user_supplied": true,
                      "external_sources_fetched": false,
                      "decision": "screen only; no portfolio approval inferred"
                    },
                    "table_row_count": 3,
                    "result_sha256": "34d3ac62a1198cd86a03038bf6e2f669e459abbfa4901d1dd73c47df77032404"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Supplied asset metric rows",
                    "value": 0,
                    "unit": "rows"
                  },
                  {
                    "label": "Threshold breaches",
                    "value": 0,
                    "unit": "rows"
                  },
                  {
                    "label": "Missing values",
                    "value": 0,
                    "unit": "rows"
                  },
                  {
                    "label": "Incomplete provenance",
                    "value": 0,
                    "unit": "rows"
                  },
                  {
                    "label": "Evidence state",
                    "value": "no supplied evidence",
                    "unit": "state"
                  }
                ],
                "default_result_sha256": "df05978b882c45cac5cc22858a0c7e5ffe4a571037e67e69d22f9e340ec6ca1c"
              },
              "units": "rows / status",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 5
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "evidence=empty (declared zero-length boundary)",
                "metric_snapshot": [
                  {
                    "label": "Supplied asset metric rows",
                    "value": 0,
                    "unit": "rows"
                  },
                  {
                    "label": "Threshold breaches",
                    "value": 0,
                    "unit": "rows"
                  },
                  {
                    "label": "Missing values",
                    "value": 0,
                    "unit": "rows"
                  },
                  {
                    "label": "Incomplete provenance",
                    "value": 0,
                    "unit": "rows"
                  },
                  {
                    "label": "Evidence state",
                    "value": "no supplied evidence",
                    "unit": "state"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "df05978b882c45cac5cc22858a0c7e5ffe4a571037e67e69d22f9e340ec6ca1c"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/planning_studies.py",
              "engine_sha256": "5334bb9e0dec5acbc4b57b10ca675d2157bdc89f0fb0620e903b676184d8376c",
              "test_file": "apps/api/tests/test_planning_studies.py",
              "test_sha256": "3e5e2eccbc88a1d0eff85a8f1d40dbef5fd6bcb203645b5ab2b185be796ce4ce",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "No asset values, thresholds, or sources are fabricated; an empty dataset returns an explicit no-evidence state.",
              "Threshold comparison does not authenticate records, normalize units, or imply risk approval.",
              "The NIST publication provides materials-data evaluation context; the source-plus-date completeness rule is this application’s own checklist, not a NIST validation standard.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-overview-risk"
    },
    {
      "id": "pilots-registration",
      "title": "Unpaired point-cloud registration",
      "summary": "Register unordered design and survey XYZ points with trimmed nearest-neighbor ICP and a rigid Kabsch transform.",
      "workspace": {
        "id": "pilots",
        "name": "Construction & pilots",
        "appRoute": "#pilots"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "survey",
        "equations": [
          "nearest-neighbor correspondences are recomputed after every transform",
          "Kabsch rigid fit minimizes Σ ||R·source[i] + t - target[i]||² with det(R)=+1",
          "trim high-residual correspondences; repeat until RMSE change ≤ tolerance or iteration cap"
        ],
        "limitations": [
          "Requires supplied XYZ point clouds; no drone imagery ingestion or 3D reconstruction is performed.",
          "Unpaired ICP can converge to a local alignment; coordinate frames, units, point density, outliers, and registration quality require survey review.",
          "No scale, non-rigid deformation, georeferencing, or survey certification is modeled."
        ],
        "sources": [
          {
            "title": "Kabsch (1976), a solution for the best rotation between vector sets",
            "url": "https://doi.org/10.1107/S0567739476001873"
          },
          {
            "title": "SciPy cKDTree nearest-neighbor query reference",
            "url": "https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.cKDTree.html"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "maximum_correspondence_distance_m",
            "label": "Maximum match distance",
            "default": 1,
            "min": 0.0001,
            "max": 1000000,
            "step": 0.1,
            "unit": "m"
          },
          {
            "name": "trim_fraction",
            "label": "Retained correspondence fraction",
            "default": 0.8,
            "min": 0.25,
            "max": 1,
            "step": 0.05,
            "unit": "fraction"
          },
          {
            "name": "maximum_iterations",
            "label": "Maximum ICP iterations",
            "default": 60,
            "min": 1,
            "max": 100,
            "step": 1,
            "unit": "iterations"
          },
          {
            "name": "convergence_tolerance_m",
            "label": "RMSE change tolerance",
            "default": 1e-7,
            "min": 1e-7,
            "max": 0.1,
            "step": 1e-7,
            "unit": "m"
          }
        ],
        "jsonDataFields": [
          {
            "name": "design_points_xyz",
            "label": "Design points (XYZ m)",
            "default": {
              "type": "array",
              "items": 6,
              "itemTypes": [
                "array"
              ],
              "sha256": "7aa7a0411b6b561b1526546a78cc46311da50c803b9d75840ccc3f193672c9ba"
            }
          },
          {
            "name": "survey_points_xyz",
            "label": "Unordered surveyed points (XYZ m)",
            "default": {
              "type": "array",
              "items": 6,
              "itemTypes": [
                "array"
              ],
              "sha256": "e463ddc6e523cc0e6475d39ab73dbb9f947c1ca8b4dab2eed062c84c014d539d"
            }
          }
        ]
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "367874488470d723caec10abe36fedca2e97198ff8fe0a753f2adb1d39fe018a",
        "evidenceSha256": "2781e5d644a0041bcfb805e79f0fd5f55a991a37ddbfd89f999ba4d7207837fd",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pilots-registration\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pilots-registration\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "An independently rotated/translated 3D point set is recovered with RMSE below 1e-6 m, all inliers retained, and no photo claim.",
          "testIds": [
            {
              "node_id": "tests/test_planning_studies.py::test_point_cloud_icp_recovers_rigid_transform_without_photo_inputs",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "maximum_correspondence_distance_m",
              "unit": "m",
              "minimum": 0.0001,
              "maximum": 1000000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "trim_fraction",
              "unit": "fraction",
              "minimum": 0.25,
              "maximum": 1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "maximum_iterations",
              "unit": "iterations",
              "minimum": 1,
              "maximum": 100,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "convergence_tolerance_m",
              "unit": "m",
              "minimum": 1e-7,
              "maximum": 0.1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [
            {
              "name": "design_points_xyz",
              "label": "Design points (XYZ m)",
              "default": {
                "type": "array",
                "items": 6,
                "itemTypes": [
                  "array"
                ],
                "sha256": "7aa7a0411b6b561b1526546a78cc46311da50c803b9d75840ccc3f193672c9ba"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            },
            {
              "name": "survey_points_xyz",
              "label": "Unordered surveyed points (XYZ m)",
              "default": {
                "type": "array",
                "items": 6,
                "itemTypes": [
                  "array"
                ],
                "sha256": "e463ddc6e523cc0e6475d39ab73dbb9f947c1ca8b4dab2eed062c84c014d539d"
              },
              "unit": null,
              "unitStatus": "U — JSON data-field units are not declared in the catalogue metadata."
            }
          ],
          "strictValidationReference": "apps/api/helioforge/engines/planning_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "367874488470d723caec10abe36fedca2e97198ff8fe0a753f2adb1d39fe018a",
          "evidenceSha256": "2781e5d644a0041bcfb805e79f0fd5f55a991a37ddbfd89f999ba4d7207837fd",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pilots-registration\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"pilots-registration\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "An independently rotated/translated 3D point set is recovered with RMSE below 1e-6 m, all inliers retained, and no photo claim.",
            "testIds": [
              {
                "node_id": "tests/test_planning_studies.py::test_point_cloud_icp_recovers_rigid_transform_without_photo_inputs",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "maximum_correspondence_distance_m below declared minimum 0.0001",
            "maximum_correspondence_distance_m above declared maximum 1000000",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_planning_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "maximum_correspondence_distance_m=0.0001 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Final trimmed registration RMSE",
                  "value": 7.798156063713201e-16,
                  "unit": "m"
                },
                {
                  "label": "Final inlier count",
                  "value": 5,
                  "unit": "points"
                },
                {
                  "label": "Discarded survey points",
                  "value": 1,
                  "unit": "points"
                },
                {
                  "label": "Rigid rotation",
                  "value": 0.0000012074182697257333,
                  "unit": "degrees"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {
            "converged": true
          },
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "PASS",
          "actual": "converged=True"
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "8e18ff40b3c8d51d27fcefb1a01771bb02964d6e7f4be92823502f234b72b2c3"
          }
        },
        "limitations": [
          "Requires supplied XYZ point clouds; no drone imagery ingestion or 3D reconstruction is performed.",
          "Unpaired ICP can converge to a local alignment; coordinate frames, units, point density, outliers, and registration quality require survey review.",
          "No scale, non-rigid deformation, georeferencing, or survey certification is modeled."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/planning_studies.py",
          "tests": [
            "apps/api/tests/test_planning_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "pilots-registration",
            "workspace": "pilots",
            "title": "Unpaired point-cloud registration",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_planning_studies.py::test_point_cloud_icp_recovers_rigid_transform_without_photo_inputs",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "An independently rotated/translated 3D point set is recovered with RMSE below 1e-6 m, all inliers retained, and no photo claim.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Final trimmed registration RMSE",
                        "value": 9.199304589560955e-16,
                        "unit": "m"
                      },
                      {
                        "label": "Final inlier count",
                        "value": 8,
                        "unit": "points"
                      },
                      {
                        "label": "Discarded survey points",
                        "value": 0,
                        "unit": "points"
                      },
                      {
                        "label": "Rigid rotation",
                        "value": 1.1459155902598013,
                        "unit": "degrees"
                      }
                    ],
                    "scalar_details": {
                      "registration_rmse_m": 9.199304589560955e-16,
                      "median_residual_m": 9.469156946781128e-16,
                      "p95_residual_m": 1.2397689367106114e-15,
                      "maximum_residual_m": 1.2994827337208943e-15,
                      "inlier_count": 8,
                      "survey_point_count": 8,
                      "design_point_count": 8,
                      "discarded_survey_point_count": 0,
                      "eligible_correspondence_count": 8,
                      "minimum_retained_count": 8,
                      "actual_retained_fraction": 1,
                      "iterations": 2,
                      "converged": true,
                      "maximum_correspondence_distance_m": 1,
                      "trim_fraction": 1,
                      "photos_used": false,
                      "photo_reconstruction": false,
                      "alignment_method": "identity-initialized trimmed nearest-neighbor ICP with rigid Kabsch updates; no scale"
                    },
                    "table_row_count": 8,
                    "result_sha256": "dfe8885682c38cb6ec6059fa23c993c0111dd7c8337f52aedcb973dad97f5641"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Final trimmed registration RMSE",
                    "value": 7.798156063713201e-16,
                    "unit": "m"
                  },
                  {
                    "label": "Final inlier count",
                    "value": 5,
                    "unit": "points"
                  },
                  {
                    "label": "Discarded survey points",
                    "value": 1,
                    "unit": "points"
                  },
                  {
                    "label": "Rigid rotation",
                    "value": 0.0000012074182697257333,
                    "unit": "degrees"
                  }
                ],
                "default_result_sha256": "8e18ff40b3c8d51d27fcefb1a01771bb02964d6e7f4be92823502f234b72b2c3"
              },
              "units": "m / points / radians",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 4
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "maximum_correspondence_distance_m=0.0001 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Final trimmed registration RMSE",
                    "value": 7.798156063713201e-16,
                    "unit": "m"
                  },
                  {
                    "label": "Final inlier count",
                    "value": 5,
                    "unit": "points"
                  },
                  {
                    "label": "Discarded survey points",
                    "value": 1,
                    "unit": "points"
                  },
                  {
                    "label": "Rigid rotation",
                    "value": 0.0000012074182697257333,
                    "unit": "degrees"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "8e18ff40b3c8d51d27fcefb1a01771bb02964d6e7f4be92823502f234b72b2c3"
              },
              "solver_feasibility": {
                "status": "PASS",
                "actual": "converged=True"
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/planning_studies.py",
              "engine_sha256": "5334bb9e0dec5acbc4b57b10ca675d2157bdc89f0fb0620e903b676184d8376c",
              "test_file": "apps/api/tests/test_planning_studies.py",
              "test_sha256": "3e5e2eccbc88a1d0eff85a8f1d40dbef5fd6bcb203645b5ab2b185be796ce4ce",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Requires supplied XYZ point clouds; no drone imagery ingestion or 3D reconstruction is performed.",
              "Unpaired ICP can converge to a local alignment; coordinate frames, units, point density, outliers, and registration quality require survey review.",
              "No scale, non-rigid deformation, georeferencing, or survey certification is modeled.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-pilots-registration"
    },
    {
      "id": "hybrid-controls",
      "title": "Averaged inverter control response",
      "summary": "Simulate a balanced dq LCL inverter with cascaded voltage/current PI control, vector limits, back-calculation anti-windup, and a refined RK4 trace.",
      "workspace": {
        "id": "hybrid",
        "name": "Hybrid systems lab",
        "appRoute": "#hybrid"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "electrical",
        "equations": [
          "L_f·d i_f/dt = u_inv − v_c − R_f i_f + ωL_f J i_f; C_f·d v_c/dt = i_f − i_g − i_load + ωC_f J v_c; L_g·d i_g/dt = v_c − v_grid − R_g i_g + ωL_g J i_g, with J=[[0,1],[-1,0]].",
          "i_ref = sat_I(i_g+i_load+K_pv(v_ref−v_c)+K_iv∫(v_ref−v_c)dt); u_inv = sat_V(v_c+K_pi(i_ref−i_f)+K_ii∫(i_ref−i_f)dt). Integrators use back-calculation from each vector-limit residual.",
          "The averaged two-level inverter voltage bound is Vdc·m_max/√3. State derivatives are integrated with fixed-step RK4 at dt and dt/2; aligned trajectories are compared as a numerical refinement check."
        ],
        "limitations": [
          "Balanced positive-sequence dq averaged plant; the grid angle is imposed at the nominal frequency. There is no PLL state or synchronization transient.",
          "No PWM switching ripple, dead time, sample/computation delay, sensor filters, DC-link dynamics, unbalance, harmonics, protection, or grid-code behavior is modeled.",
          "The fixed-step RK4 model and local linearization are software research screens, not a calibrated controller, hardware result, field validation, or service qualification."
        ],
        "sources": [
          {
            "title": "Imperix TN106: Vector current control (dq-frame plant equations, PI loop, and limits)",
            "url": "https://imperix.com/doc/implementation/vector-current-control"
          },
          {
            "title": "NREL: Small-Signal Stability Analysis of Low-Inertia Power Grids with Inverter-Based Resources",
            "url": "https://www.nrel.gov/docs/fy22osti/80613.pdf"
          },
          {
            "title": "Imperix TN135: Neutral Point Clamped Inverter control and modulation (dq PI saturation and anti-windup implementation)",
            "url": "https://imperix.com/doc/implementation/neutral-point-clamped-inverter"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "duration_s",
            "label": "Simulation duration",
            "default": 0.04,
            "min": 0.001,
            "max": 0.12,
            "step": 0.001,
            "unit": "s"
          },
          {
            "name": "time_step_s",
            "label": "Coarse time step",
            "default": 0.00005,
            "min": 0.00001,
            "max": 0.0005,
            "step": 0.00001,
            "unit": "s"
          },
          {
            "name": "load_step_time_s",
            "label": "Load step time",
            "default": 0.004,
            "min": 0,
            "max": 0.12,
            "step": 0.001,
            "unit": "s"
          },
          {
            "name": "load_step_a",
            "label": "d-axis load-current step",
            "default": 8,
            "min": 0,
            "max": 500,
            "step": 1,
            "unit": "A"
          },
          {
            "name": "filter_inductance_h",
            "label": "Converter-side inductance",
            "default": 0.0015,
            "min": 0.0001,
            "max": 0.02,
            "step": 0.0001,
            "unit": "H"
          },
          {
            "name": "filter_resistance_ohm",
            "label": "Converter-side resistance",
            "default": 0.08,
            "min": 0.001,
            "max": 10,
            "step": 0.01,
            "unit": "Ω"
          },
          {
            "name": "filter_capacitance_f",
            "label": "Filter capacitance",
            "default": 0.00002,
            "min": 0.000001,
            "max": 0.001,
            "step": 0.000001,
            "unit": "F"
          },
          {
            "name": "grid_inductance_h",
            "label": "Grid-side inductance",
            "default": 0.001,
            "min": 0.0001,
            "max": 0.1,
            "step": 0.0001,
            "unit": "H"
          },
          {
            "name": "grid_resistance_ohm",
            "label": "Grid-side resistance",
            "default": 0.05,
            "min": 0.001,
            "max": 10,
            "step": 0.01,
            "unit": "Ω"
          },
          {
            "name": "grid_angular_frequency_rad_s",
            "label": "Imposed grid angular frequency",
            "default": 314.1592653589793,
            "min": 250,
            "max": 400,
            "step": 1,
            "unit": "rad/s"
          },
          {
            "name": "grid_voltage_peak_v",
            "label": "Grid d-axis voltage",
            "default": 325.27,
            "min": 50,
            "max": 800,
            "step": 1,
            "unit": "V peak"
          },
          {
            "name": "dc_bus_v",
            "label": "DC bus voltage",
            "default": 800,
            "min": 100,
            "max": 1800,
            "step": 10,
            "unit": "V"
          },
          {
            "name": "modulation_index_max",
            "label": "Maximum linear modulation index",
            "default": 0.95,
            "min": 0.4,
            "max": 0.95,
            "step": 0.01,
            "unit": "ratio"
          },
          {
            "name": "current_limit_a",
            "label": "Vector current-reference limit",
            "default": 40,
            "min": 0.1,
            "max": 1000,
            "step": 1,
            "unit": "A"
          },
          {
            "name": "current_kp_v_per_a",
            "label": "Inner current PI proportional gain",
            "default": 1.5,
            "min": 0.001,
            "max": 100,
            "step": 0.1,
            "unit": "V/A"
          },
          {
            "name": "current_ki_v_per_as",
            "label": "Inner current PI integral gain",
            "default": 1000,
            "min": 0.01,
            "max": 100000,
            "step": 10,
            "unit": "V/(A·s)"
          },
          {
            "name": "voltage_kp_a_per_v",
            "label": "Outer voltage PI proportional gain",
            "default": 0.2,
            "min": 0.001,
            "max": 100,
            "step": 0.01,
            "unit": "A/V"
          },
          {
            "name": "voltage_ki_a_per_vs",
            "label": "Outer voltage PI integral gain",
            "default": 80,
            "min": 0.01,
            "max": 100000,
            "step": 10,
            "unit": "A/(V·s)"
          },
          {
            "name": "current_antiwindup_per_s",
            "label": "Current PI back-calculation gain",
            "default": 100,
            "min": 0,
            "max": 10000,
            "step": 10,
            "unit": "s⁻¹"
          },
          {
            "name": "voltage_antiwindup_per_s",
            "label": "Voltage PI back-calculation gain",
            "default": 50,
            "min": 0,
            "max": 10000,
            "step": 10,
            "unit": "s⁻¹"
          },
          {
            "name": "trace_points",
            "label": "Maximum trace points",
            "default": 240,
            "min": 50,
            "max": 500,
            "step": 10,
            "unit": "points"
          }
        ],
        "jsonDataFields": []
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "58d71dd8e911c20b1c3fc01dc2d8887a8917f80e75d256549465cb3af365d0e8",
        "evidenceSha256": "5c4ef5f7b976c6be3dd76495c7f8b6574ef8747bcd1a3b773b7faffb41c7a736",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-controls\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-controls\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "At zero load the balanced dq control holds grid d-voltage, zero d-axis current, and the analytic capacitor q-axis current.",
          "testIds": [
            {
              "node_id": "tests/test_control_studies.py::test_no_load_case_holds_grid_voltage_and_zero_current",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "duration_s",
              "unit": "s",
              "minimum": 0.001,
              "maximum": 0.12,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "time_step_s",
              "unit": "s",
              "minimum": 0.00001,
              "maximum": 0.0005,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "load_step_time_s",
              "unit": "s",
              "minimum": 0,
              "maximum": 0.12,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "load_step_a",
              "unit": "A",
              "minimum": 0,
              "maximum": 500,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "filter_inductance_h",
              "unit": "H",
              "minimum": 0.0001,
              "maximum": 0.02,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "filter_resistance_ohm",
              "unit": "Ω",
              "minimum": 0.001,
              "maximum": 10,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "filter_capacitance_f",
              "unit": "F",
              "minimum": 0.000001,
              "maximum": 0.001,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "grid_inductance_h",
              "unit": "H",
              "minimum": 0.0001,
              "maximum": 0.1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "grid_resistance_ohm",
              "unit": "Ω",
              "minimum": 0.001,
              "maximum": 10,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "grid_angular_frequency_rad_s",
              "unit": "rad/s",
              "minimum": 250,
              "maximum": 400,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "grid_voltage_peak_v",
              "unit": "V peak",
              "minimum": 50,
              "maximum": 800,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "dc_bus_v",
              "unit": "V",
              "minimum": 100,
              "maximum": 1800,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "modulation_index_max",
              "unit": "ratio",
              "minimum": 0.4,
              "maximum": 0.95,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "current_limit_a",
              "unit": "A",
              "minimum": 0.1,
              "maximum": 1000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "current_kp_v_per_a",
              "unit": "V/A",
              "minimum": 0.001,
              "maximum": 100,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "current_ki_v_per_as",
              "unit": "V/(A·s)",
              "minimum": 0.01,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "voltage_kp_a_per_v",
              "unit": "A/V",
              "minimum": 0.001,
              "maximum": 100,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "voltage_ki_a_per_vs",
              "unit": "A/(V·s)",
              "minimum": 0.01,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "current_antiwindup_per_s",
              "unit": "s⁻¹",
              "minimum": 0,
              "maximum": 10000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "voltage_antiwindup_per_s",
              "unit": "s⁻¹",
              "minimum": 0,
              "maximum": 10000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "trace_points",
              "unit": "points",
              "minimum": 50,
              "maximum": 500,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [],
          "strictValidationReference": "apps/api/helioforge/engines/control_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "58d71dd8e911c20b1c3fc01dc2d8887a8917f80e75d256549465cb3af365d0e8",
          "evidenceSha256": "5c4ef5f7b976c6be3dd76495c7f8b6574ef8747bcd1a3b773b7faffb41c7a736",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-controls\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"hybrid-controls\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "At zero load the balanced dq control holds grid d-voltage, zero d-axis current, and the analytic capacitor q-axis current.",
            "testIds": [
              {
                "node_id": "tests/test_control_studies.py::test_no_load_case_holds_grid_voltage_and_zero_current",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "duration_s below declared minimum 0.001",
            "duration_s above declared maximum 0.12",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_control_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "duration_s=0.12 (max bound)",
              "metric_snapshot": [
                {
                  "label": "Maximum capacitor voltage deviation",
                  "value": 0.0517967023073993,
                  "unit": "V (coarse vs refined)"
                },
                {
                  "label": "Maximum filter/grid current deviation",
                  "value": 0.006591749848826822,
                  "unit": "A (coarse vs refined)"
                },
                {
                  "label": "Final d-axis capacitor voltage",
                  "value": 325.2699783660054,
                  "unit": "V peak"
                },
                {
                  "label": "Final d-axis filter current",
                  "value": 8.000301172919423,
                  "unit": "A"
                },
                {
                  "label": "Current-reference limit-hit steps",
                  "value": 0,
                  "unit": "steps"
                },
                {
                  "label": "Inverter voltage limit-hit steps",
                  "value": 0,
                  "unit": "steps"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {
            "converged": true
          },
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "PASS",
          "actual": "converged=True"
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "e48e476c83a549690d2285fae6064fd21a6d6d0dcc38b4ab546ddd58fe6080b6"
          }
        },
        "limitations": [
          "Balanced positive-sequence dq averaged plant; the grid angle is imposed at the nominal frequency. There is no PLL state or synchronization transient.",
          "No PWM switching ripple, dead time, sample/computation delay, sensor filters, DC-link dynamics, unbalance, harmonics, protection, or grid-code behavior is modeled.",
          "The fixed-step RK4 model and local linearization are software research screens, not a calibrated controller, hardware result, field validation, or service qualification."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/control_studies.py",
          "tests": [
            "apps/api/tests/test_control_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "hybrid-controls",
            "workspace": "hybrid",
            "title": "Averaged inverter control response",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_control_studies.py::test_no_load_case_holds_grid_voltage_and_zero_current",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "At zero load the balanced dq control holds grid d-voltage, zero d-axis current, and the analytic capacitor q-axis current.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Maximum capacitor voltage deviation",
                        "value": 1.7053025658242404e-13,
                        "unit": "V (coarse vs refined)"
                      },
                      {
                        "label": "Maximum filter/grid current deviation",
                        "value": 3.4267342050340055e-14,
                        "unit": "A (coarse vs refined)"
                      },
                      {
                        "label": "Final d-axis capacitor voltage",
                        "value": 325.27,
                        "unit": "V peak"
                      },
                      {
                        "label": "Final d-axis filter current",
                        "value": -4.048613296466515e-15,
                        "unit": "A"
                      },
                      {
                        "label": "Current-reference limit-hit steps",
                        "value": 0,
                        "unit": "steps"
                      },
                      {
                        "label": "Inverter voltage limit-hit steps",
                        "value": 0,
                        "unit": "steps"
                      }
                    ],
                    "scalar_details": {
                      "converged": true,
                      "integrator": "fixed-step classical RK4",
                      "time_step_s": 0.00005,
                      "refined_time_step_s": 0.000025,
                      "coarse_step_count": 800,
                      "refined_step_count": 1600,
                      "output_point_count": 240,
                      "load_step_a": 0,
                      "load_step_time_s": 0.004,
                      "grid_voltage_peak_v": 325.27,
                      "dc_bus_v": 800,
                      "inverter_voltage_limit_v": 438.7862045841156,
                      "current_limit_a": 40,
                      "current_antiwindup_per_s": 100,
                      "voltage_antiwindup_per_s": 50,
                      "current_reference_limit_hit_count": 0,
                      "inverter_voltage_limit_hit_count": 0,
                      "maximum_abs_voltage_refinement_error_v": 1.7053025658242404e-13,
                      "maximum_abs_current_refinement_error_a": 3.4267342050340055e-14,
                      "filter_inductance_h": 0.0015,
                      "filter_resistance_ohm": 0.08,
                      "filter_capacitance_f": 0.00002,
                      "grid_inductance_h": 0.001,
                      "grid_resistance_ohm": 0.05,
                      "frame": "balanced positive-sequence dq with ideal imposed grid angle",
                      "control_mode": "grid-following cascaded capacitor-voltage/current PI; no PLL state"
                    },
                    "table_row_count": 0,
                    "result_sha256": "b626ebf75e803670d197ce66ea1dca0d9de76f91c1b9a47efcb077a3b36bbcac"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Maximum capacitor voltage deviation",
                    "value": 0.0517967023073993,
                    "unit": "V (coarse vs refined)"
                  },
                  {
                    "label": "Maximum filter/grid current deviation",
                    "value": 0.006591749848826822,
                    "unit": "A (coarse vs refined)"
                  },
                  {
                    "label": "Final d-axis capacitor voltage",
                    "value": 325.32257111595044,
                    "unit": "V peak"
                  },
                  {
                    "label": "Final d-axis filter current",
                    "value": 7.854616223454734,
                    "unit": "A"
                  },
                  {
                    "label": "Current-reference limit-hit steps",
                    "value": 0,
                    "unit": "steps"
                  },
                  {
                    "label": "Inverter voltage limit-hit steps",
                    "value": 0,
                    "unit": "steps"
                  }
                ],
                "default_result_sha256": "e48e476c83a549690d2285fae6064fd21a6d6d0dcc38b4ab546ddd58fe6080b6"
              },
              "units": "V / A",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 6
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "duration_s=0.12 (max bound)",
                "metric_snapshot": [
                  {
                    "label": "Maximum capacitor voltage deviation",
                    "value": 0.0517967023073993,
                    "unit": "V (coarse vs refined)"
                  },
                  {
                    "label": "Maximum filter/grid current deviation",
                    "value": 0.006591749848826822,
                    "unit": "A (coarse vs refined)"
                  },
                  {
                    "label": "Final d-axis capacitor voltage",
                    "value": 325.2699783660054,
                    "unit": "V peak"
                  },
                  {
                    "label": "Final d-axis filter current",
                    "value": 8.000301172919423,
                    "unit": "A"
                  },
                  {
                    "label": "Current-reference limit-hit steps",
                    "value": 0,
                    "unit": "steps"
                  },
                  {
                    "label": "Inverter voltage limit-hit steps",
                    "value": 0,
                    "unit": "steps"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "e48e476c83a549690d2285fae6064fd21a6d6d0dcc38b4ab546ddd58fe6080b6"
              },
              "solver_feasibility": {
                "status": "PASS",
                "actual": "converged=True"
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/control_studies.py",
              "engine_sha256": "0e5e6fbbee0fb171c2703eff122cf6bcede34869b7cfe521040dfac565680f48",
              "test_file": "apps/api/tests/test_control_studies.py",
              "test_sha256": "d4c7620200be16e5fe8c4d12a60fdd4221b1387d3121d978258537050953dd53",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Balanced positive-sequence dq averaged plant; the grid angle is imposed at the nominal frequency. There is no PLL state or synchronization transient.",
              "No PWM switching ripple, dead time, sample/computation delay, sensor filters, DC-link dynamics, unbalance, harmonics, protection, or grid-code behavior is modeled.",
              "The fixed-step RK4 model and local linearization are software research screens, not a calibrated controller, hardware result, field validation, or service qualification.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-hybrid-controls"
    },
    {
      "id": "research-eigenmodes",
      "title": "Closed-loop inverter eigenmodes",
      "summary": "Linearize the unsaturated averaged LCL and cascaded PI model at its grid-voltage equilibrium, then report actual state-matrix eigenvalues and modal damping ratios.",
      "workspace": {
        "id": "research",
        "name": "Research pillars",
        "appRoute": "#research"
      },
      "delivery": {
        "publicBrowser": "bundled synthetic example only",
        "newCalculations": "separate local Python application",
        "boundedTerminal": false
      },
      "model": {
        "version": "roadmap-screen-v1",
        "kind": "stability",
        "equations": [
          "Compute the local matrix A by central-difference linearization of the same ten-state averaged plant/controller used by hybrid-controls at its unsaturated zero-load equilibrium; i_f,q=ωC_fV_grid,d supplies synchronous capacitor current, and PI integrators supply the balancing equilibrium outputs.",
          "λ_k are computed from A; local asymptotic stability requires max Re(λ_k)<0. The reported damping ratio is −Re(λ_k)/|λ_k|, and is a local linear modal quantity only."
        ],
        "limitations": [
          "Balanced positive-sequence dq averaged plant; the grid angle is imposed at the nominal frequency. There is no PLL state or synchronization transient.",
          "No PWM switching ripple, dead time, sample/computation delay, sensor filters, DC-link dynamics, unbalance, harmonics, protection, or grid-code behavior is modeled.",
          "The fixed-step RK4 model and local linearization are software research screens, not a calibrated controller, hardware result, field validation, or service qualification.",
          "This is the unsaturated local model at an equilibrium; current and modulation limit nonlinearities are inactive. It is not a large-disturbance stability guarantee.",
          "A negative outer voltage integral gain is accepted only to expose an intentionally wrong-sign unstable thought experiment; it is not a candidate tuning."
        ],
        "sources": [
          {
            "title": "Imperix TN106: Vector current control (dq-frame plant equations, PI loop, and limits)",
            "url": "https://imperix.com/doc/implementation/vector-current-control"
          },
          {
            "title": "NREL: Small-Signal Stability Analysis of Low-Inertia Power Grids with Inverter-Based Resources",
            "url": "https://www.nrel.gov/docs/fy22osti/80613.pdf"
          },
          {
            "title": "Imperix TN135: Neutral Point Clamped Inverter control and modulation (dq PI saturation and anti-windup implementation)",
            "url": "https://imperix.com/doc/implementation/neutral-point-clamped-inverter"
          }
        ]
      },
      "inputs": {
        "numericFields": [
          {
            "name": "filter_inductance_h",
            "label": "Converter-side inductance",
            "default": 0.0015,
            "min": 0.0001,
            "max": 0.02,
            "step": 0.0001,
            "unit": "H"
          },
          {
            "name": "filter_resistance_ohm",
            "label": "Converter-side resistance",
            "default": 0.08,
            "min": 0.001,
            "max": 10,
            "step": 0.01,
            "unit": "Ω"
          },
          {
            "name": "filter_capacitance_f",
            "label": "Filter capacitance",
            "default": 0.00002,
            "min": 0.000001,
            "max": 0.001,
            "step": 0.000001,
            "unit": "F"
          },
          {
            "name": "grid_inductance_h",
            "label": "Grid-side inductance",
            "default": 0.001,
            "min": 0.0001,
            "max": 0.1,
            "step": 0.0001,
            "unit": "H"
          },
          {
            "name": "grid_resistance_ohm",
            "label": "Grid-side resistance",
            "default": 0.05,
            "min": 0.001,
            "max": 10,
            "step": 0.01,
            "unit": "Ω"
          },
          {
            "name": "grid_angular_frequency_rad_s",
            "label": "Imposed grid angular frequency",
            "default": 314.1592653589793,
            "min": 250,
            "max": 400,
            "step": 1,
            "unit": "rad/s"
          },
          {
            "name": "grid_voltage_peak_v",
            "label": "Grid d-axis voltage",
            "default": 325.27,
            "min": 50,
            "max": 800,
            "step": 1,
            "unit": "V peak"
          },
          {
            "name": "dc_bus_v",
            "label": "DC bus voltage",
            "default": 800,
            "min": 100,
            "max": 1800,
            "step": 10,
            "unit": "V"
          },
          {
            "name": "modulation_index_max",
            "label": "Maximum linear modulation index",
            "default": 0.95,
            "min": 0.4,
            "max": 0.95,
            "step": 0.01,
            "unit": "ratio"
          },
          {
            "name": "current_limit_a",
            "label": "Vector current-reference limit",
            "default": 40,
            "min": 0.1,
            "max": 1000,
            "step": 1,
            "unit": "A"
          },
          {
            "name": "current_kp_v_per_a",
            "label": "Inner current PI proportional gain",
            "default": 1.5,
            "min": 0.001,
            "max": 100,
            "step": 0.1,
            "unit": "V/A"
          },
          {
            "name": "current_ki_v_per_as",
            "label": "Inner current PI integral gain",
            "default": 1000,
            "min": 0.01,
            "max": 100000,
            "step": 10,
            "unit": "V/(A·s)"
          },
          {
            "name": "voltage_kp_a_per_v",
            "label": "Outer voltage PI proportional gain",
            "default": 0.2,
            "min": 0.001,
            "max": 100,
            "step": 0.01,
            "unit": "A/V"
          },
          {
            "name": "voltage_ki_a_per_vs",
            "label": "Outer voltage PI integral gain",
            "default": 80,
            "min": -100000,
            "max": 100000,
            "step": 10,
            "unit": "A/(V·s)"
          }
        ],
        "jsonDataFields": []
      },
      "fixture": {
        "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
        "dataKind": "synthetic_or_user_supplied_unverified",
        "inputSha256": "c90990ac9fbccada9027b5165ae90ecb5c7cf0ce16c81225fd85d22475083464",
        "evidenceSha256": "194c18e7acd8061111a4d872cd6c8d7525f1ec4b9aca2c1fd859c80652d2e2d0",
        "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"research-eigenmodes\"].inputs",
        "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"research-eigenmodes\"]",
        "handCheck": {
          "status": "SOFTWARE_ORACLE_PASS",
          "note": "Reported eigenvalues match independent NumPy eigvals of the reconstructed 10x10 state matrix; the hand matrix coefficient and stable classification agree.",
          "testIds": [
            {
              "node_id": "tests/test_control_studies.py::test_eigenmodes_report_actual_matrix_eigenvalues_and_stable_default",
              "status": "PASS"
            }
          ],
          "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
        }
      },
      "acceptance": {
        "status": "SOFTWARE_CHECKS_PASS",
        "schema": {
          "declaredVersion": null,
          "versionStatus": "U — study result envelope has no separate schema_version in the current shared result contract.",
          "numericFields": [
            {
              "field": "filter_inductance_h",
              "unit": "H",
              "minimum": 0.0001,
              "maximum": 0.02,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "filter_resistance_ohm",
              "unit": "Ω",
              "minimum": 0.001,
              "maximum": 10,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "filter_capacitance_f",
              "unit": "F",
              "minimum": 0.000001,
              "maximum": 0.001,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "grid_inductance_h",
              "unit": "H",
              "minimum": 0.0001,
              "maximum": 0.1,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "grid_resistance_ohm",
              "unit": "Ω",
              "minimum": 0.001,
              "maximum": 10,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "grid_angular_frequency_rad_s",
              "unit": "rad/s",
              "minimum": 250,
              "maximum": 400,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "grid_voltage_peak_v",
              "unit": "V peak",
              "minimum": 50,
              "maximum": 800,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "dc_bus_v",
              "unit": "V",
              "minimum": 100,
              "maximum": 1800,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "modulation_index_max",
              "unit": "ratio",
              "minimum": 0.4,
              "maximum": 0.95,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "current_limit_a",
              "unit": "A",
              "minimum": 0.1,
              "maximum": 1000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "current_kp_v_per_a",
              "unit": "V/A",
              "minimum": 0.001,
              "maximum": 100,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "current_ki_v_per_as",
              "unit": "V/(A·s)",
              "minimum": 0.01,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "voltage_kp_a_per_v",
              "unit": "A/V",
              "minimum": 0.001,
              "maximum": 100,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            },
            {
              "field": "voltage_ki_a_per_vs",
              "unit": "A/(V·s)",
              "minimum": -100000,
              "maximum": 100000,
              "boundSemantics": "U — catalogue bounds are recorded; consult the Pydantic field for inclusive/exclusive semantics."
            }
          ],
          "jsonDataFields": [],
          "strictValidationReference": "apps/api/helioforge/engines/control_studies.py"
        },
        "fixture": {
          "type": "bundled default-input example; generated by scripts/generate-studies.py from the Python study implementation",
          "dataKind": "synthetic_or_user_supplied_unverified",
          "inputSha256": "c90990ac9fbccada9027b5165ae90ecb5c7cf0ce16c81225fd85d22475083464",
          "evidenceSha256": "194c18e7acd8061111a4d872cd6c8d7525f1ec4b9aca2c1fd859c80652d2e2d0",
          "inputReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"research-eigenmodes\"].inputs",
          "resultReference": "apps/web/src/roadmap-data.ts — STUDY_BUNDLE.results[\"research-eigenmodes\"]",
          "handCheck": {
            "status": "SOFTWARE_ORACLE_PASS",
            "note": "Reported eigenvalues match independent NumPy eigvals of the reconstructed 10x10 state matrix; the hand matrix coefficient and stable classification agree.",
            "testIds": [
              {
                "node_id": "tests/test_control_studies.py::test_eigenmodes_report_actual_matrix_eigenvalues_and_stable_default",
                "status": "PASS"
              }
            ],
            "scope": "Independent arithmetic/invariant/software checks; scientific and field validation remain unestablished."
          }
        },
        "invalidBoundary": {
          "suggestedCases": [
            "filter_inductance_h below declared minimum 0.0001",
            "filter_inductance_h above declared maximum 0.02",
            "unknown input field and non-finite numeric input"
          ],
          "sourceAndTests": [
            "apps/api/tests/test_control_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "status": "PASS",
          "crossFieldConstraints": "Consult the linked Pydantic model and study-specific tests; not inferred from the catalogue bounds alone.",
          "executed": [
            {
              "status": "PASS",
              "case": "filter_inductance_h=0.0001 (min bound)",
              "metric_snapshot": [
                {
                  "label": "Largest eigenvalue real part",
                  "value": -16.122876157881542,
                  "unit": "s⁻¹"
                },
                {
                  "label": "Eigenmodes",
                  "value": 10,
                  "unit": "modes"
                },
                {
                  "label": "Local classification",
                  "value": "locally_asymptotically_stable",
                  "unit": "state"
                }
              ]
            },
            {
              "status": "PASS",
              "case": "unknown input field",
              "actual": "ValidationError"
            }
          ]
        },
        "deterministicOrSeeded": {
          "contract": "docs/roadmap/CONTRACT.md states study runs must not use global random state; stochastic studies use a per-run seeded generator.",
          "seedFields": [],
          "status": "PASS",
          "comparison": "exact after excluding reported wall-clock solver fields"
        },
        "feasibilityOrConvergence": {
          "defaultExampleSignals": {},
          "applicability": "Model-specific; the common result envelope does not require a solver-status field.",
          "status": "NOT_APPLICABLE",
          "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
        },
        "export": {
          "expected": [
            "JSON inputs and full result",
            "full result table CSV",
            "selected series CSV",
            "study memorandum"
          ],
          "implementation": "apps/web/src/roadmap.ts",
          "status": "JSON_PASS; BROWSER_EXPORTS_SEPARATE",
          "json": {
            "status": "PASS",
            "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
            "sha256": "2ae93f590d62dd614bf01a57ee438896556607d680f7903d511fd383c4278431"
          }
        },
        "limitations": [
          "Balanced positive-sequence dq averaged plant; the grid angle is imposed at the nominal frequency. There is no PLL state or synchronization transient.",
          "No PWM switching ripple, dead time, sample/computation delay, sensor filters, DC-link dynamics, unbalance, harmonics, protection, or grid-code behavior is modeled.",
          "The fixed-step RK4 model and local linearization are software research screens, not a calibrated controller, hardware result, field validation, or service qualification.",
          "This is the unsaturated local model at an equilibrium; current and modulation limit nonlinearities are inactive. It is not a large-disturbance stability guarantee.",
          "A negative outer voltage integral gain is accepted only to expose an intentionally wrong-sign unstable thought experiment; it is not a candidate tuning."
        ],
        "modelReferences": {
          "code": "apps/api/helioforge/engines/control_studies.py",
          "tests": [
            "apps/api/tests/test_control_studies.py",
            "apps/api/tests/test_studies_api.py"
          ],
          "sharedContract": "docs/roadmap/CONTRACT.md"
        },
        "executionEvidence": {
          "reference": "docs/remediation-20261029/study-contracts.json",
          "sourceMatches": true,
          "record": {
            "study_id": "research-eigenmodes",
            "workspace": "research",
            "title": "Closed-loop inverter eigenmodes",
            "status": "PASS",
            "test_ids": [
              {
                "node_id": "tests/test_control_studies.py::test_eigenmodes_report_actual_matrix_eigenvalues_and_stable_default",
                "status": "PASS"
              }
            ],
            "oracle": {
              "method": "Executed existing targeted independent software oracle; expected relation is documented below and in the referenced test.",
              "expected": "Reported eigenvalues match independent NumPy eigvals of the reconstructed 10x10 state matrix; the hand matrix coefficient and stable classification agree.",
              "actual": {
                "independent_test_outcome": "PASS",
                "observed_cases": [
                  {
                    "outcome": "returned",
                    "metric_snapshot": [
                      {
                        "label": "Largest eigenvalue real part",
                        "value": -53.37867888082236,
                        "unit": "s⁻¹"
                      },
                      {
                        "label": "Eigenmodes",
                        "value": 10,
                        "unit": "modes"
                      },
                      {
                        "label": "Local classification",
                        "value": "locally_asymptotically_stable",
                        "unit": "state"
                      }
                    ],
                    "scalar_details": {
                      "stability": "locally_asymptotically_stable",
                      "maximum_real_part_per_s": -53.37867888082236,
                      "linearization": "central finite difference of the averaged closed-loop RHS at the computed unsaturated zero-load equilibrium, with current and modulation limits inactive",
                      "filter_inductance_h": 0.0015,
                      "filter_resistance_ohm": 0.08,
                      "filter_capacitance_f": 0.00002,
                      "grid_inductance_h": 0.001,
                      "grid_resistance_ohm": 0.05,
                      "current_kp_v_per_a": 1.5,
                      "current_ki_v_per_as": 1000,
                      "voltage_kp_a_per_v": 0.2,
                      "voltage_ki_a_per_vs": 80,
                      "current_limit_a": 40,
                      "control_mode": "grid-following cascaded capacitor-voltage/current PI; no PLL state",
                      "pll_included": false
                    },
                    "table_row_count": 10,
                    "result_sha256": "a37d9335c21569be830c986576eec38e0c986feaf15b2c910dcbb3597ed9ddf7"
                  }
                ],
                "default_metric_snapshot": [
                  {
                    "label": "Largest eigenvalue real part",
                    "value": -53.37867888082236,
                    "unit": "s⁻¹"
                  },
                  {
                    "label": "Eigenmodes",
                    "value": 10,
                    "unit": "modes"
                  },
                  {
                    "label": "Local classification",
                    "value": "locally_asymptotically_stable",
                    "unit": "state"
                  }
                ],
                "default_result_sha256": "2ae93f590d62dd614bf01a57ee438896556607d680f7903d511fd383c4278431"
              },
              "units": "s⁻¹ / modes",
              "evidence_class": "independent_software_oracle"
            },
            "checks": {
              "default_contract": {
                "status": "PASS",
                "actual": {
                  "finite_json": true,
                  "null_metric_count": 0,
                  "metric_count": 3
                }
              },
              "boundary_case": {
                "status": "PASS",
                "case": "filter_inductance_h=0.0001 (min bound)",
                "metric_snapshot": [
                  {
                    "label": "Largest eigenvalue real part",
                    "value": -16.122876157881542,
                    "unit": "s⁻¹"
                  },
                  {
                    "label": "Eigenmodes",
                    "value": 10,
                    "unit": "modes"
                  },
                  {
                    "label": "Local classification",
                    "value": "locally_asymptotically_stable",
                    "unit": "state"
                  }
                ]
              },
              "invalid_input": {
                "status": "PASS",
                "case": "unknown input field",
                "actual": "ValidationError"
              },
              "repeatability": {
                "status": "PASS",
                "comparison": "exact after excluding reported wall-clock solver fields"
              },
              "json_export": {
                "status": "PASS",
                "actual": "strict JSON round-trip preserved result and recomputed evidence digest",
                "sha256": "2ae93f590d62dd614bf01a57ee438896556607d680f7903d511fd383c4278431"
              },
              "solver_feasibility": {
                "status": "NOT_APPLICABLE",
                "reason": "This study result has no solver_status field; solver feasibility is not part of its declared computation."
              }
            },
            "source_hashes": {
              "provenance_file": "apps/api/helioforge/provenance.py",
              "provenance_sha256": "45a8fdc5b19da4327f73492616ddfb87ca8fe9daf7fb0abbbb946106f3c2c70e",
              "schemas_file": "apps/api/helioforge/schemas.py",
              "schemas_sha256": "4c03d0ba4e4b20a7a8e6a869954bd9d0bb2a6fad7fdf9a8ffa5f4ba801152d22",
              "shared_finance_file": "apps/api/helioforge/engines/finance.py",
              "shared_finance_sha256": "393528feb42fbec36fa51e642d16e63ba9e9b968c91d9466214ceb01f6c4f136",
              "engine_file": "apps/api/helioforge/engines/control_studies.py",
              "engine_sha256": "0e5e6fbbee0fb171c2703eff122cf6bcede34869b7cfe521040dfac565680f48",
              "test_file": "apps/api/tests/test_control_studies.py",
              "test_sha256": "d4c7620200be16e5fe8c4d12a60fdd4221b1387d3121d978258537050953dd53",
              "catalogue_file": "apps/api/helioforge/studies.py",
              "catalogue_sha256": "2e16e60dc96cb430f74605105484e168435ce1dc3054874937943c6ba3989cd2"
            },
            "uncovered_conditions": [
              "Balanced positive-sequence dq averaged plant; the grid angle is imposed at the nominal frequency. There is no PLL state or synchronization transient.",
              "No PWM switching ripple, dead time, sample/computation delay, sensor filters, DC-link dynamics, unbalance, harmonics, protection, or grid-code behavior is modeled.",
              "The fixed-step RK4 model and local linearization are software research screens, not a calibrated controller, hardware result, field validation, or service qualification.",
              "This is the unsaturated local model at an equilibrium; current and modulation limit nonlinearities are inactive. It is not a large-disturbance stability guarantee.",
              "A negative outer voltage integral gain is accepted only to expose an intentionally wrong-sign unstable thought experiment; it is not a candidate tuning.",
              "No independent scientific or field validation is established by this software test."
            ]
          }
        }
      },
      "documentationUrl": "/models/#study-research-eigenmodes"
    }
  ]
}
