← Manual home

V14 · Scenario Lab

The practice kitchen: every seeded scenario, every panel, every action — how to rehearse a market month without confusing it for the live one.

1. What the Scenario Lab is — a practice kitchen, not the live market

Imagine you want to ask: what if the sun fails for three days? what if the grid tells a plant to cut back at noon? what if the market’s invoice is wrong by five rand? You cannot wait a real month, and you must not practise on the live books.

The Scenario Lab is the practice kitchen. You write a recipe (a scenario), press Run, and the platform cooks a whole market month in seconds — bids, prices, grid orders, meter readings, imbalance, wheeling settlement, and the month-end check against the market invoice. Then you compare two cooks, and you wash the dishes (Teardown) so the practice food does not sit on the live plates.

The screen lives at /org/sim. It is not on the organisation landing page. It exists only in development and demo builds. Production builds do not compile the route at all.

Follow along. Seat: any organisation user in a development or demo build (for example trader_1@webill.net, password supersecret). Path: after sign-in, open /org/sim in the address bar. You should see the beaker heading Scenario Lab and a Scenarios strip of named chips.

Where this is taught. Strategy reading of a labelled simulated screen is V3 §17. How the harness itself clears prices and writes one set of meter truth is V3 §16. Demo setup, live meter ticks, and the Eskom / Munix apps stay in V21.

1.1 The reader’s problem

You need a safe place to:

  1. Rehearse a month of market life without waiting thirty calendar days.
  2. Change one thing (cloud, a grid order, a bad invoice line) and see what moved.
  3. Prove that two runs of the same recipe produce the same numbers — or, if they do not, that the platform changed, not the weather.
  4. Clean up every row the rehearsal created, while keeping a paper trail of what you did.

The Lab is that place. It is not a second market. It is not a forecast workbench. It is not a place to publish a price to the country.

1.2 What it is for — and what it is not for

Use the Lab to Do not use the Lab to
Ask a what-if against a written recipe Claim a production System Marginal Price
Drill reconciliation on a declared bad invoice Treat a ribbon-marked screen as live evidence
Watch the same clocks the live platform uses, sped up Replace overnight jobs or the live meter writer
Compare two completed cooks of a recipe Edit a recipe while it is still cooking

Two other tools share the word simulation. Keep them apart:

Tool Question it answers Where
Scenario Lab (/org/sim) What if the market month ran under this weather, these grid orders, these invoice faults? This volume
Strategy Simulations What if I signed this contract into a book? V3 §8
Live meter ticks (S15) Keep writing 20-minute readings on the wall clock after setup V21 §6

1.3 How you should use it

You use the Lab as a controlled experiment.

  1. Start from a seeded recipe, or Clone one so the original stays untouched.
  2. Change one family of inputs — weather or grid orders or invoice faults or how jumpy residual prices are — not all four at once.
  3. Press Run scenario. Wait until every delivery day appears in the Live day log and the run badge reads COMPLETE (or FAILED).
  4. Read the day log and the Compare strip. Follow any amber SIMULATED ribbon on other screens back here.
  5. Press Teardown when you are done looking. The practice rows go away. The run stays as a tombstone (the log and the headline numbers remain; the created data is gone).
flowchart LR
  A[Pick or clone a scenario] --> B[Change one thing]
  B --> C[Run scenario]
  C --> D[Watch the day log]
  D --> E{Complete?}
  E -->|yes| F[Compare KPIs]
  E -->|failed| G[Read the last day]
  F --> H[Teardown]
  G --> H

1.4 Who this volume is for

The primary seat is the platform operator in a demo world — the person who builds the dataset, presses Run, and must not confuse the result with production. Traders, IPP operators, and off-takers meet the Lab only when an amber SIMULATED ribbon appears on a Strategy, Bid Desk, Market Operator, or System Operator screen. That ribbon is a warning label, not a second product.

Data. Follow-along numbers in this volume come from mix wb.setup --profile lab --reset --seed 42 or a completed full_demo that sealed stage S12 (Scenario Lab scripts). Typical sealed clock: as_of 2026-07-18, so the Lab’s own month is August 2026. If your demo was sealed on an older date and the Lab feels stale, roll it forward first (mix wb.setup.roll_forward — V21 §4).

2. Words you need — every idea before the buttons

This chapter names every idea the Lab uses. Later chapters assume these words. First use in this volume links to the glossary.

2.1 The kitchen metaphor, then the platform words

Plain English Platform word One-sentence meaning
The recipe Scenario The written script of a month: dates, seed, weather, grid orders, invoice faults, who takes part
One cook of the recipe SimRun One execution: status, day-by-day diary, list of rows it created, headline numbers
The cook Driver The sped-up clock that walks each delivery day through the real daily jobs
The random-looking spice that is not random Seed An integer (demo uses 42) that makes “noise” repeatable
Scripted weather Day factor A multiplier on solar, wind, or load for one calendar day (default 1.00)
A grid order you wrote in advance SO event A System Operator instruction the driver issues on a scripted day
A deliberate invoice fault MO injection A declared error applied only to the Market Operator’s published file
The rest of the country Residual market Everyone not on this platform, modelled as a price-elastic pool
How jumpy that pool is Elasticity k Megawatt-hours the residual absorbs per 100% price move
The starting price shape p_base The hour-by-hour base price the residual bends around
The warning label SIMULATED ribbon Amber bar on any screen showing a live run’s data
The created rows Artifact registry The run’s list of every bid, meter row, settlement line it wrote
Washing the dishes Teardown Delete exactly those artifacts; keep the diary and headlines as a tombstone
“This cook used an older recipe” Scenario drifted The script was edited after this run was stored

2.2 Abbreviations the Lab shows you

Abbreviation Stands for In the Lab it means
SO System Operator The grid controller. Lab events pretend to be its orders.
MO Market Operator The market clerk. Lab injections pretend its invoice is wrong.
BRP Balance Responsible Party The organisation that must match what it scheduled to what the meters did.
DAM Day-Ahead Market Yesterday’s auction for today’s hours. The driver builds those bids at a fake 03:00 the day before.
IDM Intra-Day Market Today’s later auction, so you can correct the morning plan. The driver runs it at a fake 16:00.
SMP System Marginal Price The one price every cleared megawatt-hour settles at that hour.
TOU Time of Use Peak / standard / off-peak price bands. The mock p_base is a TOU curve.
KPI Key Performance Indicator The small headline bundle on each run (days, bids, margin, coverage, …).
S12 Setup stage 12 The setup step that plants the three seeded scenarios and their sample runs.
SAST South Africa Standard Time UTC+2. Gates and the fake clocks are SAST.
MW / MWh / kWh Megawatt / megawatt-hour / kilowatt-hour Power, then energy. 1 MWh = 1,000 kWh.
ISO date / ISO time International date format 2026-08-13 or 2026-08-13T10:00:00Z (the Z means UTC).

2.3 The two clocks

The Lab does not wait for real nights. It hands the same daily jobs a sim-clock: “pretend it is 03:00 on 12 August.” Recorded timestamps on the rows still say when the computer actually wrote them. Decision logic (gates, “is it too late to bid?”) reads the pretend clock.

Clock What it drives Example
Sim-clock Bid gates, which delivery day is “today”, when IDM runs Driver says 03:00 SAST on D−1
Wall clock When the row was really inserted Your laptop at 17:22
Production Oban cron Live overnight jobs — not the Lab 01:00 SAST settlement runner
S15 live ticks Continuous 20-minute meter writer V21 §6

2.4 Why a seed makes “random” repeatable

Meter “forecast error” looks random. It is not. The Lab hashes (seed, site, hour) into a number between −1 and 1, then scales it by a small σ (sigma). Same seed, same site, same hour → same wiggle. Change the seed and the wiggle pattern changes. That is why the chip under each scenario name shows seed 42: the seed is part of the experiment, not decoration.

2.5 Who “participants” are

A scenario does not invent companies. It points at organisations already planted by setup:

If those lists are empty, the driver falls back to active pool memberships of the named books. A brand-new scenario from New starts with an empty participants map unless you cloned, or created it while another scenario was selected (then it inherits that scenario’s participants).

3. Opening the Lab — how you get there and what you see first

3.1 The two gates

Two locks sit in front of /org/sim. Both must be open.

  1. Compile-time. The Elixir app is built with Application.compile_env(:wb_wheeling, :sim_enabled) true. That is on in dev and test, off in production. When it is off, the route is not even in the router — there is nothing to bookmark.
  2. Runtime. WB.Sim.sim_enabled?/0 must also pass. If you somehow reach the LiveView with the flag off, you are sent home to /.

There is no landing-page card. That is deliberate: a practice kitchen that is one mis-click from a live trader’s home page would be too easy to confuse with production.

Follow along. Development server (mix phx.server) → sign in as trader_1@webill.net / supersecret → type /org/sim after the host. You should see Scenario Lab 🧪 in the shell brand and WB.Sim in the page header.

If the page is missing, you are on a production build, the runtime flag is off, or you are not signed in as an organisation user. See §14.

3.2 The page chrome

The page is a single LiveView. It does not have tabs. Everything you need is stacked on one scroll.

The Scenario Lab page, top to bottom — what each band is for (lab · seed 42 · S12)
HeaderWB.Sim · Scenario Lab · beaker
↓always visible
Scenarios stripchips · New · Clone · Delete
↓after you select a chip
Editorfactors · market · events · injections
↓same scenario
RunsRun · statuses · Compare · day log

The header title is Scenario Lab. The description reads: Drive a scripted market month deterministically; compare and tear down runs. The icon is the Heroicon beaker (hero-beaker). The left shell has one item, also Scenario Lab, pointing at /org/sim.

Other screens can send you here with a run already open:

/org/sim?run_id=<the-run-uuid>

The Lab loads that run’s scenario, selects it, and fills the Live day log from that run’s diary. If the id is unknown, the page opens as a plain gallery — it does not error. The amber SIMULATED ribbon on Strategy, Bid Desk, Market Operator, and System Operator screens uses this link. Clicking the ribbon is how you “inspect in Lab.”

3.4 What you see before you pick a scenario

Until a chip is selected, only the Scenarios strip is interactive. The editor, the Runs list, Compare, and the day log stay hidden. That is why the first action is always: click a named chip.

After S12 you should see at least three chips:

Chip text (name + seed) What it is
AUG-BASELINE seed 42 The full August reference month
CODEX-S12-WARM seed 42 (or your setup seed) The three-day warm window used to prove the Lab is alive
CODEX-S12-ISOLATION seed 42 The one-day script used to prove teardown does not touch other desks

Each chip shows the scenario name and, in smaller type, seed {n}. The selected chip gets a thicker ink border and a light grey fill.

You cannot change chips while a run task is in flight — the buttons disable. Finish or fail first.

4. The scenario list — chips and the three list actions

The Scenarios card is the gallery. Its heading is Scenarios. On the right sit three buttons. Under them sit one chip per saved recipe.

Scenarios strip lab · seed 42 · S12

Chip 1 Chip Chip Actions 2
AUG-BASELINE seed 42 CODEX-S12-WARM seed 42 CODEX-S12-ISOLATION seed 42 New · Clone · Delete
  1. 1Chip — click to select. The selected chip is the recipe the editor and Runs panel talk about.
  2. 2Actions — New always; Clone and Delete only after a chip is selected.

4.1 Select

Click a chip. The Lab:

  1. Loads that scenario’s script into the editor.
  2. Subscribes to live messages for that scenario (so a running cook streams days as they finish).
  3. Lists every run that belongs to it, newest first.
  4. Clears the day log until you run again or open a deep link.

Nothing is saved by selecting. Select is look-up, not edit.

4.2 New

New creates a blank-ish recipe and selects it. The flash says New scenario created.

The new name is Scenario {unique-integer} — a throwaway label so two people cannot collide. Change the interesting fields in the editor afterwards; the name itself is not an on-screen rename field (names are unique in the database).

What it copies depends on whether a chip was already selected:

Field No chip selected A chip was selected
Name Scenario {n} Scenario {n}
Seed 42 that scenario’s seed
Period 1–1 Aug 2026 that scenario’s period
Day factors empty (every day is 1.00) empty — weather is not copied
SO events none none
MO injections none none
p_base source mock that scenario’s source
Elasticity k blank (∞) that scenario’s k
Participants empty that scenario’s participants

So New while AUG-BASELINE is selected gives you the same books and the same market knobs, but a clean weather and event slate. That is the safe way to start a fresh experiment that still talks to the demo portfolio.

New is disabled when the selected scenario is not mutable — a run is PENDING or RUNNING, or a task is in flight.

4.3 Clone

Clone needs a selected chip. It copies everything, including weather, events, and injections, and names the copy {old name} copy {unique-integer}. The flash says Scenario cloned with a new identity.

Clone is how you keep AUG-BASELINE as the reference. Edit the clone. Leave the original alone so later comparisons still have a known starting point.

Clone is disabled while the selected scenario is not mutable.

4.4 Delete

Delete removes the recipe, not a run. It is rose-coloured on purpose.

It stays disabled when:

If you try to delete a recipe that still has history, the editor error reads: A scenario with historical run tombstones cannot be deleted. Tear those runs down if you like — teardown does not remove the tombstone row — so a scenario that has ever been run cannot be deleted from the screen. That is the audit rule: the recipe that produced a tombstone must remain nameable.

4.5 When the strip freezes

You see Why What to do
Chips disabled A run task is in flight Wait for COMPLETE or FAILED
New / Clone / Delete faded Scenario not mutable Same — wait, or teardown is not the issue here
Delete faded, runs exist History exists Keep the recipe; start a clone if you need a disposable copy

5. What a scenario contains — the script behind every chip

A scenario is pure data. It writes nothing by itself. The driver reads it and walks the month. Think of a sheet of paper titled “August, seed 42, cloudy mid-month, one noon curtailment, two invoice faults.”

5.1 The fields, in the order the editor shows them

Field What you are telling the cook Where you edit it
Name The chip label. Unique. Examples: AUG-BASELINE, CODEX-S12-WARM Set at create / clone; not a form field
Seed The repeatability key Shown in the chip and the editor subtitle
Period start / end Inclusive calendar window the driver walks, one delivery day at a time Shown in the editor subtitle (2026-08-01 → 2026-08-31)
Day factors Sparse weather / load multipliers §9 Day factors table
SO events Scripted grid orders §9 SO events list
MO injections Scripted invoice faults §9 MO error injections
p_base source Where the starting price shape comes from: mock or scenario Residual market panel
Elasticity k How jumpy the residual market is. Blank = ∞ (flat mock) Residual market panel
Participants Which already-seeded registrations, books, and sites take part Not a form — inherited from setup / clone / New-from-selected

The editor subtitle always repeats the period, the seed, and k (or ∞).

5.2 Sparse means “only the exceptions”

Day factors are a map of exceptions. A day you do not list is 1.00 for solar, wind, and load — a normal day. You do not fill thirty-one rows by hand unless you want thirty-one unusual days.

day_factors = {
  2026-08-12: { solar: 0.35 },
  2026-08-13: { solar: 0.35 },
  2026-08-14: { solar: 0.35 }
}

Those three cloudy days are the original AUG-BASELINE weather story. S12 then writes a richer set on top — §6 shows what you actually see after setup.

5.3 The three seeded recipes, at a glance

After stage S12 the gallery is not empty. Setup plants three recipes and several sample runs so the Runs panel is already a teaching aid.

Name Period (typical sealed as_of 2026-07-18) Why it exists
AUG-BASELINE 1–31 Aug 2026 The documentation month: weather, every event kind, every injection kind, comparison runs, a failed run, a torn-down run, then a deliberate edit so one run shows Scenario drifted
CODEX-S12-WARM 10–12 Aug 2026 A short window the verifier actually drives, so you can open Lab on a completed cook without waiting for thirty days
CODEX-S12-ISOLATION 25 Aug 2026 (one day) A throwaway script whose only job is to prove teardown deletes its rows and leaves every other desk unchanged

Note. If you sealed on a different as_of, the warm window is always the 10th–12th of the month after as_of, and isolation is thirteen days after the 12th. August dates below assume the usual July seal.

5.4 Sample runs that are not extra scenarios

S12 also stores runs under AUG-BASELINE. They are cooks, not recipes.

Sample run Status you will see What it is teaching
Comparison A COMPLETE + Scenario drifted Two identical KPI bundles, then the script was edited — A no longer matches today’s paper
Comparison B COMPLETE The twin of A, stored before the edit
Failed partial FAILED A cook that wrote one day and then stopped on purpose
Tombstone TORN_DOWN A cook whose artifacts are already gone

CODEX-S12-WARM has a warm run the verifier leaves COMPLETE with artifacts still attached — that is the run a deep link such as /org/sim?run_id=… usually opens.

CODEX-S12-ISOLATION may show torn-down isolation cooks; their point is the empty desks around them, not a month of KPIs.

6. AUG-BASELINE — the full August month

AUG-BASELINE is the Lab’s reference recipe. The original builder writes a simple August story (seed 42, cloudy 12–14, one curtailment, two injections). Stage S12 then enriches that same named row so the editor exercises every control: more weather, every System Operator event kind, every Market Operator injection kind, several books, and a handful of sample runs.

What you see after a sealed lab or full_demo is the enriched recipe. This chapter describes that screen.

Follow along. Seat: organisation user. Path: /org/sim → chip AUG-BASELINE. You should see the subtitle 2026-08-01 → 2026-08-31 · seed 42 · k 500 and a day-factor grid with thirty-one date rows.

Data. lab / full_demo · seed 42 · stage S12 · as_of 2026-07-18 · last walked 2026-08-18.

6.1 Purpose

Use AUG-BASELINE when you want the whole month in one script:

Do not edit this chip for a new experiment. Clone it first. S12 already edits it once after storing comparison A, which is why A’s row wears Scenario drifted. A second casual edit makes every historical cook harder to read.

6.2 Identity panel (the editor subtitle)

Fact Value after S12
Name AUG-BASELINE
Seed 42
Period 2026-08-01 → 2026-08-31 (31 delivery days)
p_base source Scenario curve (not the flat mock)
Elasticity k 500 MWh per 100% price move
Participants The S08 active BRP registration; the S07 representative trader, off-taker, and IPP books; every generator and off-taker site those books touch
Noise σ 0.02 (two percent seeded wiggle on written actuals)

6.3 Day factors table — what each unusual day is saying

Unlisted days are 1.00 / 1.00 / 1.00. After S12 the exceptions are the warm window plus two extra cloudy days, and — after the drift edit — 20 August.

Date Solar Wind Load Extra Plain English
2026-08-10 0.95 1.12 1.04 σ 0.01 Slightly dull sun, breezy, a little extra demand
2026-08-11 0.48 0.87 1.10 σ 0.02 A dim solar day, softer wind, busier load
2026-08-12 0.35 1.25 0.96 σ 0.03 Heavy cloud, strong wind, slightly quieter load
2026-08-13 0.35 1.18 1.02 — Cloud continues; wind still up
2026-08-14 0.35 0.91 1.07 — Cloud continues; wind eases
2026-08-20 0.72 1.08 1.03 — Added after comparison A was stored — this is the drift

Worked numbers for one hour of a 10 MWh solar baseline on 12 August:

Cells below 0.60 tint sky-blue; cells above 1.40 tint amber; other non-1.00 cells tint pale indigo. That is how the grid helps your eye find the story without reading every number.

6.4 SO events list — the six scripted grid stories

S12 writes one event of each kind the Lab (and the setup pack) know about. Site names are the real demo site names, not the old IPP-A label.

Typical window against as_of 2026-07-18 (generator = first participating IPP site; off-taker = first participating buyer site):

Date Kind Who Hours (SAST) Cap What it does
2026-08-10 CURTAILMENT generator 10:00–13:00 8.0 MW The plant may not put out more than 8 MW for those three hours
2026-08-10 OUTAGE_PLANNED generator 14:00–16:00 — A booked outage — the plant is off the circuit on purpose
2026-08-11 OUTAGE_UNPLANNED off-taker 08:00–10:00 — A surprise outage on the buyer side
2026-08-11 DISPATCH_CONSTRAINT generator 16:00–18:00 4.5 MW A tighter operating cap for the late afternoon
2026-08-12 RESERVE_ACTIVATION generator 17:00–19:00 3.0 MW Reserve is called — the plant is held for the system
2026-08-12 SYSTEM_CONDITION registration whole day — A high-stress banner (stress: HIGH) on the BRP, not a site cap

Each list row reads: {date} · {site} · {kind} · {start}:00–{end}:00. Edit and Remove sit on the right. Severity on these seeded rows is WARNING (the isolation failed run uses CRITICAL — that is a different chip).

The add form under the list is how you would append a seventh event. Leave it alone on this chip; clone first.

6.5 MO error injections list — four declared invoice faults

Each injection names the reconciliation outcome it must produce. The list shows {type} · {when} → {outcome} in bold.

Type When Value Charge Reconciliation must say
republish_price 2026-08-10T10:00:00Z +R 5 — VARIANCE / PRICE — the invoice priced the hour R 5 above the schedule you already stored
meter_bias 2026-08-11 +3.3% — VARIANCE / METER — the MO’s metered quantity is 3.3% away from yours
drop_line 2026-08-12T10:00:00Z — ENERGY MISSING_MO — you have an energy line; the MO file does not
phantom_line 2026-08-12T15:30:00Z R 275.00 ENERGY MISSING_INTERNAL — the MO bills R 275 you have no record of

No injections would mean a 100% matched month, because the simulated MO otherwise echoes your own truth. These four exist so the reconciler’s whole vocabulary is exercisable on one recipe.

6.6 Residual market panel on this chip

The sparkline is the deterministic TOU preview: R 700 / R 980 / R 1,400 per MWh (night / day / evening peak 17:00–20:00). AUG-BASELINE uses Scenario curve plus k = 500.

Worked price impact (the same arithmetic V3 §16 teaches):

If the hour’s p_base is R 980 and you sell 4.71 MWh into a residual with k = 500:

That R 43 is the lesson: your own volume moves the price. k = 500 is deliberately “thin enough to feel.”

6.7 Runs list you should already see

Do not be surprised that Run scenario is not the first cook. S12 pre-seeded several.

What the row looks like Meaning
Green COMPLETE · 31 days or 1 days · a KPI map · maybe Scenario drifted A finished cook. Comparison A/B store margin_r: 12138.50 and coverage_pct: 100.0
Red FAILED · a short day count · error in the KPI map The injected failure after the first durable day
Grey TORN_DOWN · Teardown hidden Artifacts already removed; diary kept

Compare (latest vs previous) appears as soon as two runs exist. On this chip it is already showing two KPI blobs side by side.

The Live day log stays on “No completed days.” until you start a new cook or open a ?run_id= link. Pre-seeded runs have diaries; a deep link is how you reopen one.

6.8 Actions that are safe on this chip

Action Safe? Why
Select / read every panel Yes Looking does not mutate
Run scenario Yes, if no cook is in flight Starts a new cook; does not overwrite old tombstones
Teardown on a COMPLETE / FAILED row Yes Removes that cook’s artifacts only
Clone Yes — preferred Your experiment gets a new identity
Edit factors / events / injections / market Possible, but do not on the reference You will drift every stored cook
Delete No History exists; the button stays disabled

6.9 How this scenario should be used

  1. Read it as the picture of a full month.
  2. Clone it to AUG-BASELINE copy … before changing weather.
  3. Run the clone, then Compare against a completed cook of the original (or against the clone’s own previous cook).
  4. Teardown the clone’s cooks when you are finished looking.
  5. Leave the original chip’s sample runs as the gallery lesson unless you are deliberately cleaning a private database.

Claim scripts

Scenario curtailment events can carry a claim script with a lodgement delay, provisional percentage and final energy factor. The simulation clock runs the production claim lifecycle through the Mock channel and keeps it scoped to that run’s instructions. An explicitly supplied operator quantity is a simulated input; absent weather or meters are not manufactured. Uncompensated claims close without a payment path. Teardown removes the run’s claim records while retaining immutable billing artefacts. The S08/S11 gallery separately demonstrates draft, lodged, provisionally paid, reconciled, disputed and closed states, plus an escalated draft.

Plant feedback during a curtailed afternoon

The Driver uses the same System Operator instruction windows for device setpoints and meter actuals. Existing mock feeds emit capped power, setpoint and curtailment mode before, during and after the instruction; a site without a feed remains forecast-only. The day log records feed IDs and trace hashes under plant_telemetry.

After running a curtailed day, open the participating plant’s Your plant live page and choose its trading date. Compare the amber curtailment band, setpoint changes, meter-backed trace status and the claim’s evidence reference. The two-hour arithmetic example is 16,350 + 16,690 kWh potential less 8,010 + 7,990 kWh metered = 17,040 kWh shortfall; at R0.65/kWh that is R11,076. Actual scenario traces depend on the PPA method and available curves and meters, so an incomplete trace stays PARTIAL.

Teardown tombstones the Lab run and removes its mutable financial rows. Immutable plant traces, device batches and the instructions needed by those traces remain as provenance; a teardown never rewrites downloaded evidence.

7. CODEX-S12-WARM — the three-day warm run

CODEX-S12-WARM is the short, living proof that the Lab works. Setup does not wait for you to press Run on a thirty-one-day script. It plants a three-day window, drives it, and leaves a COMPLETE cook with artifacts still attached so a deep link can open a finished diary.

Follow along. Path: /org/sim → CODEX-S12-WARM. You should see a three-day period in the subtitle (typically 2026-08-10 → 2026-08-12 · seed 42 · k 500) and a much shorter day-factor grid than AUG-BASELINE.

Data. lab / full_demo · seed 42 · stage S12 · warm window = 10th–12th of the month after as_of.

7.1 Purpose

Use the warm chip when you want to:

It is not a second reference month. It is the tour-size version of the same story.

7.2 Identity panel

Fact Typical sealed value
Name CODEX-S12-WARM
Seed The setup seed (42)
Period 2026-08-10 → 2026-08-12
p_base source Scenario curve
Elasticity k 500.000
Participants The same registration / books / sites as AUG-BASELINE

The window is computed, not hardcoded: start = first day of (as_of’s next month) plus nine days. A July seal → August 10–12. A June seal → July 10–12. That is why the mix task wb.setup.roll_forward exists — so Lab scripts stay on “this month” instead of a fossil August after you have lived past it.

7.3 Day factors table

Only the three warm dates are stored (S12 takes those keys from the richer map).

Date Solar Wind Load σ Plain English
10th 0.95 1.12 1.04 0.01 Almost-normal sun, extra wind, slightly busy
11th 0.48 0.87 1.10 0.02 Dim sun, softer wind, busier load
12th 0.35 1.25 0.96 0.03 Heavy cloud, strong wind, quieter load

The grid therefore has three date rows, not thirty-one. Double-click a cell to edit (range 0.00–2.00) only if you have cloned, or if you accept that you will drift the warm run.

Bulk fill on this chip can paint all three days at once — From / To must stay inside 10–12 August.

7.4 SO events list

S12 keeps only events whose date is one of the three warm dates:

Date Kind Hours What you are seeing
10th CURTAILMENT on the generator 10:00–13:00 at 8.0 MW A noon cap
10th OUTAGE_PLANNED on the generator 14:00–16:00 A booked afternoon outage
11th OUTAGE_UNPLANNED on the off-taker 08:00–10:00 A morning surprise on the buyer
11th DISPATCH_CONSTRAINT on the generator 16:00–18:00 at 4.5 MW A late-afternoon tighter cap
12th RESERVE_ACTIVATION on the generator 17:00–19:00 at 3.0 MW Reserve called at peak
12th SYSTEM_CONDITION on the registration (no hour window) High stress banner

So the warm chip’s event list is the same six rows as AUG-BASELINE’s first three days — not a different story, a cropped one.

7.5 MO error injections list

Injections are copied in full from the canonical set — they are not cropped by date. You will still see all four:

That is intentional. Month-end reconciliation still needs a file that can be wrong in every classified way, even when the driven window is three days.

7.6 Residual market panel

Same as the big month: Scenario curve, k = 500, the 700 / 980 / 1400 sparkline. Saving a new k here does not change AUG-BASELINE; they are separate rows.

7.7 Runs, Compare, and the day log

The warm run is the one setup leaves COMPLETE with artifacts still owned. Opening the chip lists that cook:

If you tear it down, the SIMULATED ribbon on other screens that pointed at this run will clear, and the setup pack’s printed /org/sim?run_id=… link will open a tombstone diary instead of live artifacts. Prefer cloning and running the clone if you want a private cook.

Compare appears once a second cook exists (for example after you press Run scenario yourself).

Live day log fills from a deep link or from a cook you start while watching. Each streamed row is one ISO date plus the inspect of that day’s entry (dam, cleared, so_events, idm, imbalance, actuals, wheeling_settlement).

7.8 Actions

Action On this chip
Read panels The intended first use
Run scenario Starts another three-day cook; the editor locks while it runs
Teardown the warm run Allowed — you will lose the “already complete” gallery
Clone Best way to experiment with the three-day weather
Delete Disabled while any run (including a tombstone) exists

7.9 How this scenario should be used

Treat it as the five-minute tour: select it, read the three-day grid, read the six events, read the four injections, open the warm run’s diary from the ribbon or ?run_id=, then go back to AUG-BASELINE or a clone for a real experiment.

8. CODEX-S12-ISOLATION — proving teardown is safe

CODEX-S12-ISOLATION is not a weather story. It is a safety drill. Setup creates a one-day recipe, writes a couple of dummy cooks, tears them down, and checks that every other desk — Strategy allocations, Bid Desk rows, statements, ordinary meter history — still checksums the same.

You open this chip to understand Teardown, not to study cloudy days.

Follow along. Path: /org/sim → CODEX-S12-ISOLATION. You should see a one-day period (typically 2026-08-25 → 2026-08-25 · seed 42) and empty day-factor / event / injection panels.

Data. lab / full_demo · seed 42 · stage S12 · isolation day = warm-window end plus 13 days (12 Aug + 13 = 25 Aug).

8.1 Purpose

The fear teardown must kill: “if I wash the practice dishes, do I also throw away last month’s real statement?”

Isolation answers no, by construction:

  1. Create a tiny recipe that owns a few rows (a System Operator instruction tagged CODEX-S12-ISOLATION-COMPLETE or …-FAILED).
  2. Checksum every “desk” table excluding rows this Lab run registered.
  3. Teardown the isolation cooks.
  4. Checksum again. The numbers must match.
  5. Confirm the warm run and the failed AUG-BASELINE run still exist with their artifacts.
  6. Confirm you cannot hang another run’s artifact id on a neighbour run (cross-claim is blocked).

If any of that failed, S12 would not have sealed.

8.2 Identity panel

Fact Typical sealed value
Name CODEX-S12-ISOLATION
Seed 42
Period 2026-08-25 → 2026-08-25 (one delivery day)
Day factors empty — every factor is 1.00
SO events empty in the script (the isolation runs ingest a SYSTEM_CONDITION as an artifact, they do not store it on the recipe)
MO injections empty
p_base source mock (the default; this chip is not a price experiment)
Elasticity k blank / ∞
Participants The same registration and books; site lists may be omitted

The editor subtitle therefore looks almost empty: one date, seed 42, k ∞.

8.3 Day factors table

One date row. All three cells read 1.00 and stay white (the “normal” tint). There is nothing to bulk-fill unless you are inventing a new drill.

8.4 SO events list

The list shows No scripted events. The add form is there, unused.

The isolation runs still created a SYSTEM_CONDITION instruction each (external refs CODEX-S12-ISOLATION-COMPLETE and CODEX-S12-ISOLATION-FAILED) and registered those ids on the cook. After a successful S12 those instructions have already been torn down. You will not find them on the live SO desk. That absence is the point.

8.5 MO error injections list

No deliberate reconciliation differences. Isolation is not a reconciliation drill. AUG-BASELINE and the warm chip own that lesson.

8.6 Residual market panel

Default mock curve, blank k. Changing k here does not help you understand teardown. Leave it.

8.7 Runs list — tombstones on purpose

After S12 the isolation cooks have been torn down (and teardown was pressed a second time to prove it is repeat-safe). You should see grey TORN_DOWN rows:

The warm run on the other chip must still be COMPLETE. If tearing down isolation had eaten the warm artifacts, S12 would have failed warm_run_preserved?.

8.8 Actions

Action On this chip
Read the empty panels Yes — they teach “this recipe is a harness, not a month”
Run scenario Possible, but you are no longer in the sealed drill
Teardown Only if you started a new cook
Clone / Delete Clone is fine; Delete stays disabled while tombstones exist

8.9 How this scenario should be used

Read it once so Teardown is not a superstition. Then leave it alone. Everyday what-ifs belong on a clone of AUG-BASELINE or on CODEX-S12-WARM, not on the isolation chip.

9. The editor — four panels, every field and action

Once a chip is selected, the scenario editor (#scenario-editor) opens. The heading is the scenario name. The subtitle is {start} → {end} · seed {n} · k {value-or-∞}.

A rose error banner (#scenario-editor-error) appears only when a save fails — for example a factor outside 0.0–2.0, or an edit while a cook is running: Scenario edits are disabled while its run is active.

The four panels sit in a two-by-two grid on a wide screen, stacked on a narrow one:

Panel Asks Must not be used to claim
Day factors What if the sun / wind / load is different on these days? that real weather changed
Residual market How jumpy is the rest of the country? an approved SMP
SO events What if the grid issues this order? that the System Operator really issued it
MO error injections Can reconciliation name this fault? that a live MO file arrived wrong

Careful. While a run is PENDING or RUNNING, every editor control disables. That stops the recipe changing under the cook’s hands.

9.1 Day factors — the table

Heading: Day factors. Helper text: Double-click a cell to edit. Range 0.0–2.0; unlisted values are 1.0.

The grid (#day-factor-grid) has four columns: Day, SOLAR, WIND, LOAD. One row per calendar date in the period.

Column Meaning
Day ISO date (2026-08-12)
SOLAR Multiplier on solar-shaped generation that day
WIND Multiplier on wind-shaped generation
LOAD Multiplier on consumption

A cell at 1.00 is a normal day. 0.35 means “about a third of the usual profile.” 2.00 is the allowed ceiling — twice the usual. 0.00 is allowed and means “none of that resource that day.”

Tints:

Tint When
White exactly 1.00
Pale indigo not 1.00, but between 0.60 and 1.40
Sky blue below 0.60 (a real shortage)
Amber above 1.40 (a real surplus)

To edit one cell: double-click it. A small number field and Save appear. Enter a value, press Save. Flash: Factor saved. The table does not show a separate Cancel on the cell; click another control or press the page’s cancel-factor path by starting a different edit.

Bulk fill (#bulk-factor-form) paints a date range in one go:

Field What to enter
From First ISO date in range (must sit inside the period)
To Last ISO date (not before From)
Factor solar, wind, or load
Value 0.00–2.00
Bulk fill Writes that value onto every day in the range

Flash: Factors filled across N day(s). Use this for “make the second week cloudy” instead of eighteen double-clicks.

Worked fill: From 2026-08-12, To 2026-08-14, Factor solar, Value 0.35 writes the original three-day cloud. Conservation of meaning: wind and load on those days stay whatever they were; only solar changes.

9.2 Residual market — the price knobs

Heading: Residual market.

The p_base preview is a 24-bar sparkline, one bar per hour. Hover a bar to read hour {h}: R {price}/MWh. Heights:

Hours (SAST) Preview price Bar
00:00–06:59 and 21:00–23:59 R 700 / MWh short
07:00–16:59 R 980 / MWh medium
17:00–20:59 R 1,400 / MWh tall

That is the deterministic TOU curve, not a live stack. Caption under it: p_base preview: deterministic TOU curve (700 / 980 / 1400 R/MWh).

The form (#market-form):

Field Options / range Meaning
p_base source Mock curve / Scenario curve Mock = the TOU picture. Scenario = the scenario is allowed to supply the shape (S12 sets this).
Elasticity k (MWh) blank, or a non-negative decimal with up to 3 places Blank persists as ∞ — the flat mock, no price impact. 500 is the demo’s thin market.
k slider 0–2000 Convenience; it writes the same field. Moving the slider live-syncs the number box.
Save market — Persists source + k. Flash: Market parameters saved.

k is “how many megawatt-hours the rest of the country will absorb (or release) when the price moves 100%.” Small k = a thin market = your volume moves the price a lot. Huge k = an ocean = your volume barely shows. ∞ = the old mock: the price never moves.

You cannot type a fourth decimal place. The Lab stores the decimal exactly as entered.

9.3 SO events — the list and the add form

Heading: SO events. Helper: date · site · kind · affected hours.

The list (#scenario-events) is one row per scripted order. Empty state: No scripted events.

Each row shows {date} · {site_ref} · {kind} · {start}:00–{end}:00 and two actions:

Action What it does
Edit Loads that row into the form. The submit button flips from Add event to Save event.
Remove Drops that row. Flash: Event removed. (via the editor save path)

The form (#event-form) fields:

Field Rule
Date Must fall inside the scenario period
Site A non-empty site label (demo names, or the old IPP-A style tag)
Kind One of CURTAILMENT, OUTAGE_PLANNED, OUTAGE_UNPLANNED
Start hour Integer 0–23
End hour Integer 1–24, and strictly after start
MW cap Required and ≥ 0 when Kind is CURTAILMENT; ignored for the outage kinds in the Lab form

The Lab form’s kind menu is those three. S12’s setup pack can also store DISPATCH_CONSTRAINT, RESERVE_ACTIVATION, and SYSTEM_CONDITION on the recipe — you will see those rows on AUG-BASELINE and the warm chip, and you can Remove them, but the on-screen add menu does not offer them. That is honest: the gallery shows every kind the verifier planted; the live form authors the three kinds the editor validates.

As you type hours, event preview updates:

Example: start 11, end 13, kind CURTAILMENT, cap 8 → Curtailment preview: 2 affected hour(s) at a 8 MW cap. Those two hours are 11:00 and 12:00 — end is exclusive in ordinary speech (the label prints 11:00–13:00).

9.4 MO error injections — the list and the add form

Heading: MO error injections.

The list (#scenario-injections) shows {type} · {period_start} → **{recon outcome}**. Empty state: No deliberate reconciliation differences.

Remove drops a row. There is no Edit — change means remove and add again.

The form (#injection-form):

Field Used by Meaning
Injection all republish_price, meter_bias, drop_line, phantom_line
Date / ISO time all A date (2026-08-14) or a UTC timestamp (2026-08-13T10:00:00Z) inside the period
Value (+R / % / amount) price, bias, phantom delta_r for a republish; pct for bias (0.033 = 3.3%); amount_r for a phantom line
Charge type drop and phantom ENERGY or IMBALANCE
Add injection — Appends the row. Flash: Injection saved.
Type What the simulated MO does wrong Reconciliation label you must see
republish_price Prices an hour at SMP + Δ VARIANCE / PRICE
meter_bias Shifts the metered quantity by a percent VARIANCE / METER
drop_line Omits a line you have internally MISSING_MO
phantom_line Adds a line you do not have MISSING_INTERNAL

Injections touch only the MO’s published file. Internal truth — your bids, your meters, your ledger — stays clean. That is how you can drill “the invoice is wrong” without lying to yourself about what happened.

9.5 Your own scenarios share this editor

A New or Clone recipe uses the same four panels. There is no second screen. What changes is the content: a new recipe starts with empty weather and events; a clone starts as a carbon copy. Walk the four panels on the clone exactly as you walked them on AUG-BASELINE.

10. Runs, Compare, the day log, and Teardown

Under the editor sits the Runs card. This is the report side of the Lab. There is no PDF download and no separate “Reports” menu. The reports are on this card: the run list, the KPI blobs, the Compare strip, and the Live day log.

10.1 The Run scenario button

Top right: Run scenario (play icon). It is disabled while the scenario is not mutable.

Pressing it starts a supervised task that calls WB.Sim.Driver.run/2. Flash: Simulation started. If nothing is selected: Select a scenario first. If a task is already in flight: A simulation is already running. The driver itself also refuses a second cook while any run is active anywhere (:simulation_already_running).

The editor locks. Chips lock. You watch the day log instead.

A SimRun’s life — statuses the Runs list paints as pills (lab · seed 42)
PENDING
→driver starts
RUNNING
→last day + month-end
COMPLETE
Or from RUNNING: FAILED After COMPLETE or FAILED, Teardown: TORN_DOWN

PENDING and RUNNING pills are amber. COMPLETE is green. FAILED is rose. TORN_DOWN is grey. Teardown is hidden on TORN_DOWN and disabled on PENDING / RUNNING.

10.2 The run list

Empty state: No runs yet — press Run scenario.

Each row, newest first, shows:

Piece What it is
Status pill PENDING / RUNNING / COMPLETE / FAILED / TORN_DOWN
{n} days How many dates are already in the day log (map_size)
Scenario drifted (amber) The recipe’s updated_at is after this run was created. Today’s paper is not the paper this cook used.
KPI blob A monospaced inspect of the kpis map
Teardown Shown unless status is already TORN_DOWN

Above the list, if any run exists, the SIMULATED ribbon for the newest run may appear — the same amber warning other screens use.

10.3 What the KPI blob is saying

A driver-finished cook typically stores:

Key Meaning
days How many delivery days were logged
bids How many bid rows the participating registrations own
imbalance_positions How many imbalance position rows those registrations own
scenario_snapshot A frozen copy of the recipe at finish time (seed, period, factors, events, injections, market, participants)
scenario_hash SHA-256 of that snapshot — a short fingerprint for “did the paper change?”

S12’s comparison A/B additionally store a teaching bundle:

Key Seeded value Meaning on that gallery row
days 31 “Treat this as a full month” (the stored cook itself logged one comparison day)
margin_r 12138.50 Headline margin in rand, identical on A and B
coverage_pct 100.0 Full coverage in the teaching bundle
scenario_snapshot A’s snapshot from before the 20 Aug edit Why A can show Scenario drifted after S12 edits 20 August

A failed cook stores error (the reason string) plus whatever snapshot it had. An isolation cook stores isolation: true and terminal: COMPLETE or FAILED.

These blobs are the on-screen report. Read them as headlines, then open the day log for the diary.

10.4 Compare (latest vs previous)

When at least two runs exist, a strip appears: Compare (latest vs previous). Two monospaced KPI blobs sit side by side — newest cook on the left, the one before it on the right.

There is no chart and no colouring of diffs. You read the two maps with your eyes. Because a scenario is deterministic, the same paper + the same seed must produce the same headlines. A difference means:

  1. you changed the paper (factors, events, injections, k, period, seed, participants), or
  2. the platform changed, or
  3. one side is a teaching fixture (A/B) and the other is a real driver cook — do not treat those as a regression.

If a row wears Scenario drifted, stop calling it a current baseline. Clone, run two fresh cooks, and Compare those.

10.5 Live day log — the diary

Heading: Live day log. Right side: {n} completed.

The stream (#sim-days) appends one card per finished delivery day:

2026-08-12    %{ "dam" => …, "cleared" => …, "so_events" => 2, "idm" => …,
                 "imbalance" => …, "actuals" => …, "wheeling_settlement" => … }
Key in the entry What that day did
dam Day-ahead build + submit summary
cleared What the elastic engine accepted
so_events How many scripted grid orders were issued that day
idm Intraday cycle summary
imbalance Yesterday-shaped imbalance summary for the day
actuals The one-truth meter write (baseline × factors × noise − curtailment)
wheeling_settlement How many settlement periods closed, with energy and artifact ids

Empty state: No completed days. A deep link fills this from the opened run’s stored diary. A live cook streams days as the driver finishes them.

S12’s comparison cooks store a shorter teaching entry (phase: comparison, margin_r: 12138.50). Isolation stores phase: isolation. A failed partial stores an actuals / written note and then stops.

This diary survives teardown. That is why a grey TORN_DOWN row is still worth opening from ?run_id=: you can read what happened after the created rows are gone.

10.6 Teardown

Teardown deletes exactly the rows in that run’s artifact registry — bids, bid lines, imbalance positions, SO instructions, settlement periods, line items, allocation runs, and so on — and reports the count.

Flash: Torn down — N row(s) deleted.

Rules:

After teardown, any screen that was showing those rows drops the SIMULATED ribbon (nothing live to label). Treat remaining ribbon-marked screens as still owned by some other cook.

11. What happens when you press Run — one day, then the next

This is the workflow the Driver walks. You do not press these steps yourself. You press Run scenario once. The day log is how you watch.

Where this is taught in depth. Elastic clearing, the actuals formula, and injection arithmetic live in V3 §16. This chapter is the Lab-operator walk: what order, what you see, what “done” means.

11.1 Before the first delivery day

  1. The Lab starts a SimRun (PENDING). The runtime guard must still be on — a run cannot begin in production.
  2. The driver marks it RUNNING.
  3. Unless told to skip, it runs the nightly strategy for each participating book over a one-month horizon (allocation runs, BESS intervals, risk snapshots). Those rows go into the artifact registry.
  4. Then it walks period_start … period_end, one calendar date at a time.

Only one driver may mutate the simulated Market Operator / System Operator gateways at a time. A second Run is refused until the first finishes or fails.

11.2 One delivery day

For date D the driver pretends two clocks:

Pretend clock When What job
03:00 SAST on D−1 The day before Build and submit DAM bids. If that pretend clock is after the gate (often 10:00), the bid expires with a gate-miss — the same rule as the live desk.
16:00 SAST on D The afternoon Run the IDM cycle so the morning plan can be corrected

Between those, in order:

flowchart TB
  A[DAM build + submit at fake 03:00 D-1] --> B[Elastic clear + write accepted volumes]
  B --> C[Issue today's scripted SO events]
  C --> D[IDM cycle at fake 16:00]
  D --> E[Write one-truth meter actuals]
  E --> F[Imbalance for the day]
  F --> G[Wheeling settlement for participating PPAs]
  G --> H[Append the day-log card and stream it]

Actuals are the one physical truth both legs read:

actual(site, hour) = baseline × day_factor × (1 + σ × noise(seed, site, hour))
                     − curtailed(site, hour)

The generator writes three 20-minute meter rows per hour. The last row eats the rounding remainder so the three always sum to the hour. Wheeling settlement and market imbalance both read those rows. They cannot disagree about what physically happened.

If any leg raises, the driver stops. Remaining days are not walked. The run finishes FAILED with the error in its KPI map. Earlier days stay in the log. Artifacts already registered stay until you Teardown.

11.3 After the last day

  1. The simulated MO publishes the month’s settlement file, with injections applied.
  2. Reconciliation runs against that file.
  3. Those rows join the artifact registry.
  4. The run finishes COMPLETE with the KPI bundle in §10.3.

A 31-day AUG-BASELINE cook therefore does thirty-one day-log cards, then one month-end file. A 3-day warm cook does three cards, then the same kind of month-end (injections on the 10th–12th still sit on that file).

11.4 What you should do while it runs

Do Do not
Watch #sim-days grow Edit factors “just for the next day” — the form is locked, and even if it were not, you would be changing the paper mid-cook
Wait for COMPLETE or FAILED Start a second Run
On FAILED, read the last card and the error KPI Assume later dates happened
On COMPLETE, Compare, then follow ribbons Read a ribbon-marked Bid Desk as a live position

11.5 After it finishes

  1. Read Compare if you have a previous cook of the same paper.
  2. Open Strategy / Bid Desk / MO / SO only to see labelled artifacts, then come back.
  3. Teardown when the lesson is over so the next person does not inherit your practice rows.

Organization controls drill

From an organization seat, choose Controls drill to add the controls_drill script to the picker. S12 also seeds this script for its active participant. It needs two distinct active organization members. Choose Run controls drill to arm the organization switch at 02:55 SAST on the day before delivery and run the 03:00 DAM cycle for every active registration. The run checks that submissions do not increase, then uses the second member’s approval to disarm into MANUAL within the exercise.

The exercise rolls back its temporary policy, stop and approval changes. It keeps a dated DRILL event with the registrations and independent checker, and the run displays zero submissions and each blocked outcome. The current data model permits one registration per organization; the script selects the whole organization scope. A failed drill is shown as a failed run. S17’s separate month fixture records a corrupt draft refusal on day 2 and a gate miss on day 4; the annual controls export includes those incidents and the drill.

12. Worked example — one cloud, one cap, one bad price

This walk clones AUG-BASELINE, changes only 12 August’s solar factor, runs the clone, and reads the reports. The arithmetic is small enough to redo on paper.

Data. lab · seed 42 · stage S12 · as_of 2026-07-18 · clone of AUG-BASELINE · last walked 2026-08-18.

Follow along. Seat: trader_1@webill.net / supersecret. Path: /org/sim → AUG-BASELINE → Clone → Day factors → 2026-08-12 SOLAR. You should see a new chip named AUG-BASELINE copy {n}.

12.1 Inputs

Input Baseline chip Clone (the experiment)
Name AUG-BASELINE AUG-BASELINE copy {n}
Seed 42 42 (inherited)
Period 1–31 Aug 2026 1–31 Aug 2026
Solar on 2026-08-12 0.35 (already cloudy) 0.70
Wind / load that day 1.25 / 0.96 unchanged
k 500 500
SO events the six S12 events unchanged
MO injections the four S12 injections unchanged

We change one cell. Everything else is the conservation control: if Compare later moves on 18 August’s curtailment story, something else is wrong — we did not touch that day.

12.2 The solar arithmetic on 12 August

Take a solar site whose forecast baseline at 12:00 is 10 MWh.

Step Formula Baseline (0.35) Clone (0.70)
R1 listed factor cell 0.35 0.70
R2 scripted availability 10 × factor 3.50 MWh 7.00 MWh
R3 ignore noise for the lesson σ is 0.03 on this date; we treat the centre 3.50 7.00
R4 noon curtailment at 8 MW cap is 8 MWh/h; both 3.50 and 7.00 sit under 8 3.50 7.00

Conservation check on the uncapped story: the clone should show twice the solar of the baseline on that hour (0.70 ÷ 0.35 = 2). The 8 MW cap does not bind either number, so the ratio survives into actuals.

If the baseline had been 16.4 MWh (the V3 §16 curtailment example) the cap would bind:

Unconstrained After 8 MW cap
factor 0.35 16.4 × 0.35 = 5.74 5.74 (under the cap)
factor 1.00 16.4 8.00
factor 0.70 16.4 × 0.70 = 11.48 8.00 (cap binds)

So: a solar-factor experiment only shows through when the plant is not already pinned by a cap. On 12 August AUG-BASELINE already has a 10:00–13:00 8 MW curtailment. Pick an hour outside 10:00–13:00 (for example 08:00) if you want the 0.35 → 0.70 doubling to be visible in the day-log actuals summary.

12.3 Steps on the screen

  1. Select AUG-BASELINE. Confirm the subtitle k 500.
  2. Press Clone. Select the new chip.
  3. In Day factors, find 2026-08-12 / SOLAR. Double-click. Change 0.35 to 0.70. Save. Flash: Factor saved. The cell tints pale indigo (0.70 is between 0.60 and 1.40).
  4. Leave Residual market, SO events, and MO injections untouched.
  5. Press Run scenario. Wait until Live day log shows 31 cards and the newest run pill is green COMPLETE.
  6. Read Compare (latest vs previous) — if this is the clone’s first cook there is no previous clone cook yet. Run a second time without further edits. Compare must now show the same scenario_hash and the same days / bid counts. That is the determinism check.
  7. To see weather impact, Compare a completed cook of the unedited clone (or a fresh clone left at 0.35) against the 0.70 cook. The hashes must differ. The day-log card for 2026-08-12 is where actuals should move; 2026-08-18 should not.

12.4 What the reports should look like

Report What you expect
Run list Two COMPLETE rows on the clone if you ran twice; neither should say Scenario drifted if you did not edit after the first cook
KPI days 31
KPI scenario_hash Identical across the two 0.70 cooks; different from a 0.35 cook
Compare Two maps; your eye checks days, bids, hash
Day log 2026-08-12 so_events still 2 (curtailment + planned outage). actuals energy higher than the 0.35 cook on solar-shaped hours outside the cap window
Day log 2026-08-18 Unchanged story versus a 0.35 cook — you did not touch that date
Ribbon on Bid Desk / Strategy Amber, naming this run, until Teardown

12.5 Teardown

Press Teardown on the clone’s cooks when you have the numbers. Flash names the deleted row count. The clone chip remains. AUG-BASELINE and CODEX-S12-WARM remain. Isolation’s checksum lesson still holds: you washed this cook’s dishes.

This is the pattern to expect on your own page: one change, two cooks, one compare, one teardown.

13. How the Lab sits beside the rest of the platform

The Lab is one room. It borrows every other room’s jobs. This chapter says who owns what, so you do not re-learn settlement here or treat a ribbon as a live book.

13.1 The SIMULATED ribbon on other screens

While a cook that is not TORN_DOWN owns artifacts, any screen showing those rows wears a slim amber bar:

SIMULATED — run #… (name, seed) · inspect in Lab

The bar is a deep link back to /org/sim?run_id=…. It can appear on Strategy, Bid Desk, Market Operator, and System Operator screens. Treat every number on a ribbon-marked page as demo-contaminated until you teardown that run.

Where this is taught. The ribbon as a Strategy reading skill is V3 §17.

13.2 Not the Strategy Simulations workbench

Strategy → Simulations answers “what if I signed this contract?” One hypothetical member, one engine run, on a book. It does not walk DAM, IDM, SO, imbalance, or month-end reconciliation.

The Lab answers “what if the market month ran under this paper?” Same English word, opposite scale. See V3 §8.

13.3 Not the overnight jobs

Mode Clock Owner
Scenario Lab Driver Accelerated sim-clock This volume
Production Oban cron Real SAST V21 §3 · V1
S15 live meters Wall clock, +20 min virtual ticks V21 §6

The Lab calls the same cycle functions the cron would call. It does not replace cron. Do not stop Oban “because the Lab is running” unless you are doing a destructive setup reset — and then stop Lab drivers as well (V21 §4).

13.4 Settlement, market, and SO meaning do not change

A cook writes real-shaped rows: settlement periods, bid lines, SO instructions. Their meaning is still owned elsewhere:

If you want to understand… Read
DRAFT / PRELIMINARY / FINAL, statements, the ledger V8
Bid Desk, gates, imbalance, reconciliation classes V9
How the Market Operator console shows a day V11
How a System Operator instruction is issued and acked V12
Demo rebuilds, roll-forward, Eskom / Munix V21

The Lab only answers: under this paper, those rooms were exercised, and here is the diary.

13.5 Roll-forward and a stale August

Manual examples often cite a sealed clock such as full_demo · seed 42 · as_of 2026-07-18. That date is the manifest clock, not “today.” If your local demo is weeks old, Lab scripts and Strategy snapshots sit on last month’s calendar.

# Stop Phoenix, Oban peers, Lab drivers, and live ticks first.
mix wb.setup.roll_forward

That rebuilds the prior sealed profile onto today’s UTC date (or --as-of). It is destructive. After it, S12’s warm window and isolation day move with the new month. Details: V21 §4.

14. Common questions

  1. Why is there no Scenario Lab card on the home page?
    On purpose. The route is /org/sim, development and demo only. A landing card would invite a live seat to author a recipe. Type the path, or follow a SIMULATED ribbon.

  2. Why does /org/sim send me home?
    The runtime sim_enabled flag is off, or you are on a build that never compiled the route. Use a development or demo server, not production.

  3. Why are New / Clone / the editor faded?
    A cook is PENDING or RUNNING, or a task is still in flight. Wait for COMPLETE or FAILED. You cannot edit the paper mid-cook.

  4. Why can’t I Delete AUG-BASELINE?
    It has runs, including tombstones. The recipe that produced history must stay nameable. Clone instead.

  5. What does Scenario drifted mean?
    Someone saved the recipe after that cook was stored. The diary is still true of the old paper. Do not use that row as a current baseline. S12 does this on purpose to comparison A.

  6. Why does Compare show two identical maps?
    Either you ran the same paper twice (determinism — good) or you are looking at S12’s teaching pair A/B (margin_r 12,138.50 both sides). A real weather change must change the scenario_hash.

  7. Why is the day log empty when the list already has COMPLETE rows?
    The live stream starts empty until you Run again or open ?run_id=. Pre-seeded cooks have diaries; deep-link to see them.

  8. Why did my run fail after a few days?
    A leg raised and the driver stopped. Read the last day-log card and the error KPI. Remaining dates did not run. Teardown when you have finished reading. S12’s failed gallery row is an injected example of this shape.

  9. Can I teardown a running cook?
    No. Flash: A running simulation cannot be torn down. Wait for a terminal status.

  10. Does Teardown delete the scenario?
    No. It deletes that cook’s registered rows and greys the run. The chip stays.

  11. Will Teardown delete last month’s live statement?
    Not if isolation still holds. Teardown deletes registered artifacts of that run only. Ordinary history before the Lab’s writer window is excluded from the drill. See §8.

  12. Why is a Bid Desk / MO / SO screen wearing an amber bar?
    A Lab cook still owns those rows. Inspect in Lab, then Teardown when the exercise is over. Do not quote that screen as production.

  13. Is this the same as Strategy → Simulations?
    No. That workbench is “what if I signed this contract?” The Lab is “what if the market month ran under this paper?” §1.2.

  14. Why are some event kinds on the list but not in the add menu?
    S12 plants DISPATCH_CONSTRAINT, RESERVE_ACTIVATION, and SYSTEM_CONDITION for the verifier. The live form authors CURTAILMENT and the two outage kinds. You can Remove a planted row; you add the three form kinds.

  15. I changed solar on the 12th and the noon hour barely moved.
    Check the 10:00–13:00 8 MW curtailment. A cap that is already binding hides a factor change. Compare an hour outside that window (§12.2).

  16. The dates are last month.
    Your seal is old. Roll forward before authoring (§13.5).

  17. Password?
    Demo seats use supersecret. See V21 §4.

15. Terms used in this volume

First-use definitions live in the hub glossary. This list is the volume’s own index — every Lab-specific word, with the chapter that taught it.

Term Taught in
Scenario Lab §1
Scenario (sim) §2 · §5
SimRun §2 · §10
Driver §11
Seed / seeded noise §2.4
Day factor §5.2 · §9.1
Elasticity k §6.6 · §9.2
p_base §9.2
Residual market §2.1
SO instruction / event §9.3
MO injection §9.4
AUG-BASELINE §6
CODEX-S12-WARM §7
CODEX-S12-ISOLATION §8
Artifact registry §10.6
Teardown / tombstone §8 · §10.6
Scenario drifted §10.2
SIMULATED ribbon §13.1
Simulation harness (WB.Sim) V3 §16
DAM / IDM / SMP / BRP / MO / SO §2.2

16. Reference card — the Lab on one page

Screen: Scenario Lab (/org/sim) — development and demo builds only. Header: WB.Sim · beaker · Drive a scripted market month deterministically; compare and tear down runs.

16.1 The three seeded chips (typical as_of 2026-07-18)

Chip Period Use it for
AUG-BASELINE seed 42 1–31 Aug The full month. Clone before editing.
CODEX-S12-WARM seed 42 10–12 Aug The three-day tour and the living COMPLETE cook.
CODEX-S12-ISOLATION seed 42 25 Aug Understanding Teardown. Do not experiment here.

16.2 List actions

Button Needs Does
New Mutable Creates Scenario {n}. Inherits market + participants from the selected chip; does not copy weather or events.
Clone A selected, mutable chip Carbon copy named {name} copy {n}.
Delete Selected, mutable, zero runs Removes the recipe. History blocks it.
Chip click No task in flight Selects the recipe; loads editor + runs.

16.3 Editor panels

Panel Actions Empty state
Day factors Double-click cell · Save · Bulk fill Every cell 1.00
Residual market p_base source · k box · k slider · Save market Mock + k ∞
SO events Edit · Remove · Add event No scripted events.
MO injections Remove · Add injection No deliberate reconciliation differences.

Factor range 0.00–2.00. Event hours start 0–23, end 1–24, end > start. Injection types: republish_price → VARIANCE/PRICE; meter_bias → VARIANCE/METER; drop_line → MISSING_MO; phantom_line → MISSING_INTERNAL.

16.4 Runs and reports

Control Does
Run scenario Starts the driver. Locks the editor. Streams the day log.
Status pills PENDING → RUNNING → COMPLETE or FAILED → TORN_DOWN
Scenario drifted Paper changed after this cook.
KPI blob Headlines (days, bids, hash, …). This is the report.
Compare Latest vs previous KPI maps.
Live day log One card per finished delivery day. Survives teardown.
Teardown Deletes registered artifacts; keeps diary + KPIs. Not on a running cook.

16.5 Safe daily path

  1. Open /org/sim in a demo build.
  2. Clone AUG-BASELINE (or tour CODEX-S12-WARM first).
  3. Change one family of inputs.
  4. Run. Wait for COMPLETE.
  5. Compare. Follow ribbons only as labelled artefacts.
  6. Teardown.

16.6 Demo walkthrough

Follow along. mix phx.server · org seat · password supersecret.

  1. /org/sim — three chips.
  2. CODEX-S12-WARM — three-day grid, six events, four injections.
  3. AUG-BASELINE — thirty-one rows, comparison cooks, one Scenario drifted.
  4. CODEX-S12-ISOLATION — empty panels, TORN_DOWN rows.
  5. Clone baseline → change one solar cell → Run → Compare → Teardown.

Where next. Ribbon reading: V3 §17. Harness arithmetic: V3 §16. Setup and live ticks: V21.

17. Stochastic mode

Route: /org/sim · Panel: #stochastic-mode · development and demo builds only.

The Lab already replays a scripted month once. Stochastic mode asks the same scenario N times against a chosen book, with the three named shocks (tariff hike, municipal surcharge, curtailment-day count) exposed as inputs, and shows the spread of margin instead of a single replay.

No new route. The Lab is one page and this is a mode within it. The existing guards hold: the route is compiled behind :sim_enabled, the LiveView runtime-guards on WB.Sim.sim_enabled?/0, and a stochastic run is unreachable when the sim is disabled.

17.1 A rehearsal, not the book’s risk position

A stochastic run evaluates and displays. It does not persist a RiskDistributionSnapshot against the book. That row is the book’s real risk position, written by the nightly worker at 02:45. A practice kitchen must not overwrite the live number.

Keep the result on the page (#stochastic-spread). If a run is to be kept, it belongs in the Lab’s own artifact registry so WB.Sim.teardown/1 can delete it exactly — never in the strategy risk tables.

The same scenario, seed and N render the same spread twice. Sampling is a pure hash of (seed, path_index, dimension, coordinate), the same discipline as a Lab month replay.

17.2 The form

#stochastic-form after you select a scenario:

Field Meaning
Book One of the scenario’s participant books
Paths (N) How many tomorrows. Default on the form is 50; the nightly worker uses 500
Seed Determinism seed. Defaults to the scenario’s seed
Tariff hike % Fixed shock, or leave blank for none
Municipal surcharge % Fixed shock, or leave blank for none
Curtailment days Fixed count of curtailed delivery days, or blank

[Run N times] samples, evaluates, and fills #stochastic-spread with VaR-95, VaR-99, expected shortfall-95 and mean margin. Nothing is written to the book.

17.3 How this sits beside a live month

Use Run scenario when you want the scripted month, the day log, and teardown. Use Run N times when you want the distribution of the book’s economics under that script’s horizon, with optional shocks. They do not substitute for each other. The nightly VaR on the Dashboard is the estate’s real number; this panel is the rehearsal.

Follow along. Seat: any seeded user (password supersecret). Open /org/sim, select AUG-BASELINE, run N = 8 with seed 42 twice, and confirm the two spreads match. Confirm the book’s RiskDistributionSnapshot row (if any) is unchanged.